Custom events

The custom events extension provides an API whereby a developer can create events for capturing arbitrary event data. The API also provides the ability to create the custom events at the application level, system level, or actor level. Please note that Lightbend Telemetry events use structured data, i.e. a map of key-values pairs, rather than message-based.

Why would one use custom events instead of a logging approach? One of the benefits of using the custom event approach is that it enables flexible rate limiting.

Custom event log levels

Events can be created at three different log levels:

  • Error
  • Warning
  • Info

The selected log level is used whenever the event is used and is selected via one of the methods:

  • createInfoEvent
  • createWarningEvent
  • createErrorEvent

Developer API

Accessing CinnamonEvents

To start using the Cinnamon events API you will first want to import the CinnamonEvents and CinnamonEvent classes:

Scala
import com.lightbend.cinnamon.akka.{ CinnamonEvent, CinnamonEvents }
Java
import com.lightbend.cinnamon.akka.CinnamonEvents;
import com.lightbend.cinnamon.akka.CinnamonEvent;

Accessing CinnamonEvents from Akka Typed

For Akka Typed, there is a separate extension that can be used with typed actor systems or actor contexts.

First add the Cinnamon Akka Typed module dependency to your build file:

sbt
libraryDependencies += Cinnamon.library.cinnamonAkkaTyped
Maven
<dependency>
  <groupId>com.lightbend.cinnamon</groupId>
  <artifactId>cinnamon-akka-typed_2.13</artifactId>
  <version>2.20.4</version>
</dependency>
Gradle
dependencies {
  implementation group: 'com.lightbend.cinnamon', name: 'cinnamon-akka-typed_2.13', version: '2.20.4'
}

Then import the typed extension for CinnamonEvents:

Scala
import com.lightbend.cinnamon.akka.CinnamonEvent
import com.lightbend.cinnamon.akka.typed.CinnamonEvents
Java
import com.lightbend.cinnamon.akka.typed.CinnamonEvents;
import com.lightbend.cinnamon.akka.CinnamonEvent;

Creating system level custom events

System level custom events provide an excellent way to capture events at the ActorSystem level. To create custom events at this level, you use CinnamonEvents with the ActorSystem and call the createEvent follows:

Scala
val systemLevelEvent: CinnamonEvent = CinnamonEvents(system).createWarningEvent("systemWarningLevelEvent")
Java
CinnamonEvent systemWarningLevelEvent =
    CinnamonEvents.get(getSystem()).createWarningEvent("systemWarningLevelEvent");

Creating actor level custom events

Actor level custom events provide an excellent way to capture actor-specific behavior as events. To create custom events at this level, you use CinnamonEvents with the ActorContext and call the createEvent follows:

Scala
val actorLevelEvent: CinnamonEvent = CinnamonEvents(context).createInfoEvent("actorInfoLevelEvent")
Java
CinnamonEvent actorInfoLevelEvent =
    CinnamonEvents.get(getContext()).createInfoEvent("actorInfoLevelEvent");

Creating application level custom events

Application level custom events are associated with the application as a whole, rather than with the actor system. See Cinnamon metadata for setting the application name. To create custom events at the application level, you first need to use the CinnamonEvents extension with the ActorSystem or with the ActorContext, and then access the application-level metrics using the eventsForApplication method. You can then call the to create createEvent to custom application level events:

Scala
val appEvents = CinnamonEvents(system).eventsForApplication()
val applicationLevelEvent: CinnamonEvent = appEvents.createErrorEvent("applicationErrorLevelEvent")
Java
CinnamonEvents appEvents = CinnamonEvents.get(getSystem()).eventsForApplication();
CinnamonEvent applicationErrorLevelEvent =
    appEvents.createErrorEvent("applicationErrorLevelEvent");

Using custom events

Using the custom events involves calling one of the methods supplied by the event class. The actorLevelEvent created above is used in this example to create events on the actor level:

Scala
actorLevelEvent.fire("key1" -> "value1", "key2" -> "value2")
Java
actorInfoLevelEvent.fireWithData("key1", "value1", "key2", "value2");

One can also use respective Maps for Java or Scala when invoking the fire method on an event. More information about the available methods can be found in the API documentation.

Destroying custom events

When finished with the event(s), you should call destroy. Calling destroy will, in turn, invoke the underlying event backend to run any cleanup tasks if required.

Scala
actorLevelEvent.destroy()
Java
actorInfoLevelEvent.destroy();

Custom event example

Here is an example of an actor-level custom event to log particular messages that an actor might receive.

import com.lightbend.cinnamon.akka.{ CinnamonEvent, CinnamonEvents }

case class EventIn(id: Long)
case class EventOut(id: Long)
case object EventDone

class CustomerEventActor extends Actor {
  val event: CinnamonEvent = CinnamonEvents(context).createInfoEvent("customerEvent")

  def receive: PartialFunction[Any, Unit] = {
    case EventIn(id) =>
      event.fire("InEventId" -> s"ID-$id")
      sender() ! EventIn(id)
    case EventOut(id) =>
      event.fire("OutEventId" -> s"ID-$id")
      sender() ! EventOut(id)
    case EventDone =>
      event.destroy()
      sender() ! EventDone
      context.stop(self)
  }
}