summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLi Haoyi <haoyi.sg@gmail.com>2019-11-03 23:51:10 +0800
committerLi Haoyi <haoyi.sg@gmail.com>2019-11-03 23:51:10 +0800
commita1a5484405867069427ed1f9090c847ac7ddd4c6 (patch)
tree1b90c4993d23d7d0ca91cf45b6e9ccd24db921c2
parent4da9f351409e2aeeb34cf31c576a3eb08f26e795 (diff)
downloadcask-a1a5484405867069427ed1f9090c847ac7ddd4c6.tar.gz
cask-a1a5484405867069427ed1f9090c847ac7ddd4c6.tar.bz2
cask-a1a5484405867069427ed1f9090c847ac7ddd4c6.zip
actor log readme
-rw-r--r--cask/actor/test/src-jvm/JvmActorsTest.scala74
-rw-r--r--docs/pages/4 - Cask Actors.md72
2 files changed, 145 insertions, 1 deletions
diff --git a/cask/actor/test/src-jvm/JvmActorsTest.scala b/cask/actor/test/src-jvm/JvmActorsTest.scala
index dc92555..066fd3e 100644
--- a/cask/actor/test/src-jvm/JvmActorsTest.scala
+++ b/cask/actor/test/src-jvm/JvmActorsTest.scala
@@ -169,6 +169,80 @@ object JvmActorsTest extends TestSuite{
"Yoghurt curds cream cheese and butter Comes from liquids from my udder I am cow, I am cow Hear me moo, moooo",
)
}
+ test("log"){
+ sealed trait Msg
+ case class Debounced() extends Msg
+ case class Text(value: String) extends Msg
+
+ class Logger(log: os.Path, debounceTime: java.time.Duration)
+ (implicit ac: Context) extends StateMachineActor[Msg]{
+ def initialState = Idle()
+ case class Idle() extends State({
+ case Text(value) =>
+ ac.scheduleMsg(this, Debounced(), debounceTime)
+ Buffering(Vector(value))
+ })
+ case class Buffering(buffer: Vector[String]) extends State({
+ case Text(value) => Buffering(buffer :+ value)
+ case Debounced() =>
+ os.write.append(log, buffer.mkString(" ") + "\n", createFolders = true)
+ Idle()
+ })
+
+ override def run(msg: Msg): Unit = {
+ println(s"$state + $msg -> ")
+ super.run(msg)
+ println(state)
+ }
+ }
+
+ implicit val ac = new Context.Test()
+
+ val logPath = os.pwd / "out" / "scratch" / "log.txt"
+
+ val logger = new Logger(logPath, java.time.Duration.ofMillis(50))
+
+ logger.send(Text("I am cow"))
+ // Idle() + Text(I am cow) ->
+ // Buffering(Vector(I am cow))
+ logger.send(Text("hear me moo"))
+ // Buffering(Vector(I am cow)) + Text(hear me moo) ->
+ // Buffering(Vector(I am cow, hear me moo))
+ Thread.sleep(100)
+ // Buffering(Vector(I am cow, hear me moo)) + Debounced() ->
+ // Idle()
+ logger.send(Text("I weight twice as much as you"))
+ // Idle() + Text(I weight twice as much as you) ->
+ // Buffering(Vector(I weight twice as much as you))
+ logger.send(Text("And I look good on the barbecue"))
+ // Buffering(Vector(I weight twice as much as you)) + Text(And I look good on the barbecue) ->
+ // Buffering(Vector(I weight twice as much as you, And I look good on the barbecue))
+ Thread.sleep(100)
+ // Buffering(Vector(I weight twice as much as you, And I look good on the barbecue)) + Debounced() ->
+ // Idle()
+ logger.send(Text("Yoghurt curds cream cheese and butter"))
+ // Idle() + Text(Yoghurt curds cream cheese and butter) ->
+ // Buffering(Vector(Yoghurt curds cream cheese and butter))
+ logger.send(Text("Comes from liquids from my udder"))
+ // Buffering(Vector(Yoghurt curds cream cheese and butter)) +
+ // Text(Comes from liquids from my udder) -> Buffering(Vector(Yoghurt curds cream cheese and butter, Comes from liquids from my udder))
+ logger.send(Text("I am cow, I am cow"))
+ // Buffering(Vector(Yoghurt curds cream cheese and butter, Comes from liquids from my udder)) + Text(I am cow, I am cow) ->
+ // Buffering(Vector(Yoghurt curds cream cheese and butter, Comes from liquids from my udder, I am cow, I am cow))
+ logger.send(Text("Hear me moo, moooo"))
+ // Buffering(Vector(Yoghurt curds cream cheese and butter, Comes from liquids from my udder, I am cow, I am cow)) + Text(Hear me moo, moooo) ->
+ // Buffering(Vector(Yoghurt curds cream cheese and butter, Comes from liquids from my udder, I am cow, I am cow, Hear me moo, moooo))
+
+ ac.waitForInactivity()
+ // Buffering(Vector(Yoghurt curds cream cheese and butter, Comes from liquids from my udder, I am cow, I am cow, Hear me moo, moooo)) + Debounced() ->
+ // Idle()
+
+ os.read.lines(logPath) ==> Seq(
+ "I am cow hear me moo",
+ "I weight twice as much as you And I look good on the barbecue",
+ "Yoghurt curds cream cheese and butter Comes from liquids from my udder I am cow, I am cow Hear me moo, moooo",
+ )
+ }
}
} \ No newline at end of file
diff --git a/docs/pages/4 - Cask Actors.md b/docs/pages/4 - Cask Actors.md
index 5d8d3c7..6845d08 100644
--- a/docs/pages/4 - Cask Actors.md
+++ b/docs/pages/4 - Cask Actors.md
@@ -347,4 +347,74 @@ does not enforce it. Similarly, it is generally good practice to avoid defining
"auxiliary" mutable state `var`s in the body of a `StateMachineActor`. The
library does not enforce that either, but doing so somewhat defeats the purpose
of using a `StateMachineActor` to model your actor state in the first place, in
-which case you might as well use `SimpleActor`. \ No newline at end of file
+which case you might as well use `SimpleActor`.
+
+### Debug Logging State Machines
+
+When using `StateMachineActor`, all your actor's internal state should be in the
+single `state` variable. You can thus easily override `def run` to print the
+state before and after each message is received:
+
+```scala
+override def run(msg: Msg): Unit = {
+ println(s"$state + $msg -> ")
+ super.run(msg)
+ println(state)
+}
+```
+
+If your `StateMachineActor` is misbehaving, this should hopefully make it easier
+to trace what it is doing in response to each message, so you can figure out
+exactly why it is misbehaving:
+
+```scala
+logger.send(Text("I am cow"))
+// Idle() + Text(I am cow) ->
+// Buffering(Vector(I am cow))
+logger.send(Text("hear me moo"))
+// Buffering(Vector(I am cow)) + Text(hear me moo) ->
+// Buffering(Vector(I am cow, hear me moo))
+Thread.sleep(100)
+// Buffering(Vector(I am cow, hear me moo)) + Debounced() ->
+// Idle()
+logger.send(Text("I weight twice as much as you"))
+// Idle() + Text(I weight twice as much as you) ->
+// Buffering(Vector(I weight twice as much as you))
+logger.send(Text("And I look good on the barbecue"))
+// Buffering(Vector(I weight twice as much as you)) + Text(And I look good on the barbecue) ->
+// Buffering(Vector(I weight twice as much as you, And I look good on the barbecue))
+Thread.sleep(100)
+// Buffering(Vector(I weight twice as much as you, And I look good on the barbecue)) + Debounced() ->
+// Idle()
+logger.send(Text("Yoghurt curds cream cheese and butter"))
+// Idle() + Text(Yoghurt curds cream cheese and butter) ->
+// Buffering(Vector(Yoghurt curds cream cheese and butter))
+logger.send(Text("Comes from liquids from my udder"))
+// Buffering(Vector(Yoghurt curds cream cheese and butter)) +
+// Text(Comes from liquids from my udder) -> Buffering(Vector(Yoghurt curds cream cheese and butter, Comes from liquids from my udder))
+logger.send(Text("I am cow, I am cow"))
+// Buffering(Vector(Yoghurt curds cream cheese and butter, Comes from liquids from my udder)) + Text(I am cow, I am cow) ->
+// Buffering(Vector(Yoghurt curds cream cheese and butter, Comes from liquids from my udder, I am cow, I am cow))
+logger.send(Text("Hear me moo, moooo"))
+// Buffering(Vector(Yoghurt curds cream cheese and butter, Comes from liquids from my udder, I am cow, I am cow)) + Text(Hear me moo, moooo) ->
+// Buffering(Vector(Yoghurt curds cream cheese and butter, Comes from liquids from my udder, I am cow, I am cow, Hear me moo, moooo))
+
+ac.waitForInactivity()
+// Buffering(Vector(Yoghurt curds cream cheese and butter, Comes from liquids from my udder, I am cow, I am cow, Hear me moo, moooo)) + Debounced() ->
+// Idle()
+```
+
+Logging every message received and processed by one or more Actors may get very
+verbose in a large system with lots going on; you can use a conditional
+`if(...)` in your `override def run` to specify exactly which state transitions
+on which actors you care about (e.g. only actors handling a certain user ID) to
+cut down on the noise:
+
+
+```scala
+override def run(msg: Msg): Unit = {
+ if (???) println(s"$state + $msg -> ")
+ super.run(msg)
+ if (???) println(state)
+}
+``` \ No newline at end of file