Akka

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 adaptor 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 lets first see what steps you need to setup basic logging in Akka

Step1 : Grab the JARs

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

See Built.sbt

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")

Step2 : application.conf

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

See resources/application.conf

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

Step3 : 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 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>

Step4 : 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

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

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 experiences 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

akka {
  # general logging level
  loglevel = DEBUG
}

Log Akka Config At Start

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

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

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

kka {
  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

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:

https://github.com/sachabarber/SachaBarber.AkkaExamples

5 thoughts on “akka logging

  1. Hi,
    Can you please give code examples using java instead of scala.
    -vijayakumar

    1. For java I’ll assume you’re using gradle instead of sbt

      compile (“com.typesafe.akka:akka-actor_$scalaVersion:$akkaVersion”) {}
      runtime(“ch.qos.logback:logback-classic:1.2.3”) {}
      runtime(‘com.typesafe.akka:akka-slf4j_2.11:2.5.3’) {}

      The only real difference between the scala and java code is that the scala for
      val log = Logging(system, classOf[Runner])
      becomes
      LoggingAdapter log = Logging.getLogger(system, this);

      This java code is assuming you’re trying to access the logger inside of an Actor object and is actually the default implementation of log() within AbstractLoggingActor

Leave a comment