Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / All-Topics

Akka Logging

5.00/5 (1 vote)
3 Aug 2016CPOL2 min read 9.1K  
Akka logging

This is likely to be a smaller one of the series, but just because it is small in size doesn’t mean that it is not mighty.

Every app needs logging, and when working with a distributed set of actors, this is crucial.

Akka provides 2 types of logging adaptors out of the box:

  • Console
  • SLF4J (where you need the appropriate back end for this which is usually Logback)

It also has various configuration sections that allow you to adjust the following elements of Akka.

  • Akka setup messages
  • Receive of messages
  • All actor lifecycle messages
  • Finite state machine messages
  • EventStream subscription messages
  • Remoting messages

Before we look at how you can customize the logging to capture all this good stuff, let's first see what steps you need to setup basic logging in Akka.

Step 1: Grab the JARs

There are a couple of JARs you will need to perform logging in Akka. These are shown below.

See Built.sbt:

Scala
name := "HelloWorld"
 
version := "1.0"
 
scalaVersion := "2.11.8"
 
libraryDependencies ++= Seq(
  "com.typesafe.akka" % "akka-actor_2.11" % "2.4.8",
  "ch.qos.logback" % "logback-classic" % "1.1.7",
  "com.typesafe.akka" % "akka-slf4j_2.11" % "2.4.8")    

Step 2: application.conf

You must then configure how Akka will log its entries. This is done in a configuration file, I have decided to call mine “application.conf”.

See resources/application.conf.

Scala
akka {
  # event-handlers = ["akka.event.slf4j.Slf4jEventHandler"]
  loggers = ["akka.event.slf4j.Slf4jLogger"]
  loglevel = "DEBUG"
} 

Step 3: logback.xml

For the SL4J logging to work, we need to configure logback. This is typically done with a configuration file called “logback.xml”.

An example of this may look like this:

See “resources/logback.xml”:

XML
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
 
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <target>System.out</target>
        <encoder>
            <pattern>%X{akkaTimestamp} %-5level[%thread] %logger{0} - %msg%n</pattern>
        </encoder>
    </appender>
 
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>c:/logs/akka.log</file>
        <append>true</append>
        <encoder>
            <pattern>%date{yyyy-MM-dd} %X{akkaTimestamp} %-5level[%thread] %logger{1} - %msg%n</pattern>
        </encoder>
    </appender>
 
    <logger name="akka" level="DEBUG" />
 
    <root level="DEBUG">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE"/>
    </root>
 
</configuration> 

Step 4: Log Some Stuff

Once you have the above steps completed, you have 2 choices about how to consume the logging.

Using the LoggingAdaptor.Apply

You are able to use the LoggingAdaptor.Apply to create a new log that you may use. Here is an example:

Scala
import akka.actor._
import scala.language.postfixOps
import scala.io.StdIn
import akka.event.Logging
 
class Runner {
 
  def Run(): Unit = {
 
    //create the actor system
    val system = ActorSystem("LifeCycleSystem")
 
    val log = Logging(system, classOf[Runner])
    log.debug("Runner IS UP BABY")
    ...
 
    StdIn.readLine()
  }
}

Using Logging Trait

To make this process easier, Akka also provides a trait that can be mixed in called “akka.actor.ActorLogging”. This can be mixed in wherever you require logging.

Scala
import akka.actor.Actor
import akka.event.Logging
 
class LifeCycleActorWithLoggingTrait extends Actor with akka.actor.ActorLogging {
 
  log.info("LifeCycleActor: constructor")
 
  .....
}   

Async Or NOT

Some of the more experienced JVM/Scala devs amongst you may think heck I can just use my own logging, I don’t need the Akka trait or LoggingAdaptor.

The thing is if you use the Akka trait or LogginAdaptor, they are setup to log asynchronously and not introduce any time delays into the messaging pipeline when logging.

So just be warned that you should probably use the inbuilt Akka stuff rather than roll your own. Logging to things like an ELK stack may be the exception.

Common Akka Configuration Around Logging

These configuration sections are useful for controlling what is logged.

General Log Level

Scala
akka {
  # general logging level
  loglevel = DEBUG
}    

Log Akka Config At Start

Scala
akka {
  # Log the complete configuration at INFO level when the actor system is started.
  # This is useful when you are uncertain of what configuration is used.
  log-config-on-start = on
}  

Actor Receive Messages

Scala
akka {
  debug {
    # enable function of LoggingReceive, which is to log any received message at
    # DEBUG level
    receive = on
  }
}   

You can also monitor lifecycle events like this:

Scala
akka {
  debug {
    # enable DEBUG logging of actor lifecycle changes
    lifecycle = on
  }
}   

Remoting Logging

Firstly, you can log the message the remote actor is sent by the transport layer.

Scala
akka {
  remote {
    # If this is "on", Akka will log all outbound messages at DEBUG level, 
      if off then they are not logged
    log-sent-messages = on
  }
}  

You may also see what messages are received by the transport layer like this:

Scala
akka {
  remote {
    # If this is "on", 
      Akka will log all inbound messages at DEBUG level, if off then they are not logged
    log-received-messages = on
  }
}

Where is the Code?

As previously stated, all the code for this series will end up in this GitHub repo:

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)