Actor Logging with Akka .NET

In this article, we’re going to see how we can work with logging in Akka .NET. There is a logging adapter which allows one to work with various different logging providers (e.g. NLog), and it is very easy to use from within actors.

The source code for this article is available at the Gigi Labs BitBucket repository.

Example Application

Before we get to the details of how to use logging in Akka .NET, let’s create a very simple application. The first thing we need to do is install the Akka package:

Install-Package Akka

To work with basic Akka components, we need the following using:

using Akka.Actor;

We can now create a simple actor. This actor will just output whatever message (string) it receives:

    public class LoggingActor : ReceiveActor
    {
        public LoggingActor()
        {
            this.Receive<string>(s => Console.WriteLine(s));
        }
    }

Finally, we create our ActorSystem in Main() and send a message to the actor:

        static void Main(string[] args)
        {
            Console.Title = "Akka .NET Logging";

            using (var actorSystem = ActorSystem.Create("MyActorSystem"))
            {
                Console.WriteLine("ActorSystem created!");

                var actor = actorSystem.ActorOf(Props.Create<LoggingActor>(), "loggingActor");

                actor.Tell("Hello!");

                Console.WriteLine("Press ENTER to exit...");
                Console.ReadLine();
            }
        }

If we run this, we get the following output:

akkanetlogging-initialoutput

You can see that some of the output came from the Console.WriteLine()s we have in Main(); while the “Hello!” message came from the actor. But there is also a warning that is not coming from anywhere within our code.

StandardOutLogger

We can adjust our actor’s code to use a logger instead of writing directly to the console. The actor doesn’t care what kind of logger it is; this detail is entirely configurable. By default, it will use the StandardOutLogger which writes to the console. But we can use something else (e.g. NLog or Serilog) and the code would be completely unchanged.

    public class LoggingActor : ReceiveActor
    {
        private readonly ILoggingAdapter logger = Logging.GetLogger(Context);

        public LoggingActor()
        {
            this.Receive<string>(s => logger.Info(s));
        }
    }

If we run this, the “Hello!” message still gets written to the console, but it is now part of a longer formatted log message:

akkanetlogging-standardoutlogger

LogLevel

Just about all logging frameworks have a concept of log level. Typically these consist of at least Debug, Info, Warning and Error (some even have a Fatal level). You tell the logging framework what is the minimum level you’re interested in. So if your minimum level is Warning, the logging framework omits Debug and Info messages.

In Akka .NET, you can configure the minimum log level you’re interested in as part of the HOCON configuration within your App.config as follows:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <configSections>
    <section name="akka" type="Akka.Configuration.Hocon.AkkaConfigurationSection, Akka" />
  </configSections>

  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
  </startup>

  <akka>
    <hocon>
      <![CDATA[ akka { loglevel = DEBUG } ]]>
    </hocon>
  </akka>

</configuration>

If we set loglevel to INFO, we get exactly the same as before. But now that we’ve set it to DEBUG, we actually get some additional logging from within Akka .NET:

akkanetlogging-debugoutput

Note: the screenshot actually contradicts what I was saying earlier about the StandardOutLogger. It actually seems to be replacing StandardOutLogger with this DefaultLogger. Presumably DefaultLogger uses StandardOutLogger underneath.

Automatic Logging

Akka .NET provides certain logging mechanisms out of the box. One of these is logging received messages (so we don’t even need to do that in code). You can turn on these mechanisms in the HOCON configuration as follows:

  <akka>
    <hocon>
      <![CDATA[
        akka
        {
          loglevel = DEBUG
          
          actor
          {
            debug
            {
              receive = on      # log any received message
              autoreceive = on  # log automatically received messages, e.g. PoisonPill
              lifecycle = on    # log actor lifecycle changes
              event-stream = on # log subscription changes for Akka.NET event stream
              unhandled = on    # log unhandled messages sent to actors
            }
          }
        }
      ]]>
    </hocon>
  </akka>

Now, to actually log received messages, you also need to make your actor implement the empty ILogReceive interface as per this StackOverflow question:

    public class LoggingActor : ReceiveActor, ILogReceive

If you run the application now, you’ll see that a lot more messages are being logged, including another entry for the “Hello!” message:

akkanetlogging-automaticlogging

Tip: ideally make sure the loglevel HOCON setting and the node under actor in HOCON (in this case debug) are in sync. If you set loglevel to INFO now, all the automatic logging will stop writing.

Integrating NLog

The logging adapter is particularly useful because you can swap out logging frameworks without having to touch the code. We are currently using the StandardOutLogger (or DefaultLogger?), but with some simple configuration, we can use NLog or some other provider instead.

The Akka .NET Logging documentation page shows the currently supported logging frameworks. Typically, you install a NuGet package for the framework you want, and set up its own configuration. I’m going to show you how to do this with NLog, but it’s an arbitrary choice, and the steps are similar for other logging frameworks.

1. Install the relevant NuGet package:

Install-Package Akka.Logger.NLog

2. Add an NLog.config to the project and configure it as you like:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <targets>
    <target name="file" xsi:type="File" fileName="test.log" />
  </targets>
  
  <rules>
    <logger name="*" minlevel="Info" writeTo="file" />
  </rules>
  
</nlog>

3. Right click NLog.config, Properties, then set to Copy always or Copy if newer:

akkanetlogging-copyalways

4. Update your HOCON to use the relevant logger:

  <akka>
    <hocon>
      <![CDATA[
        akka
        {
          loglevel = DEBUG
          loggers = ["Akka.Logger.NLog.NLogLogger, Akka.Logger.NLog"]
          
          actor
          {
            debug
            {
              receive = on      # log any received message
              autoreceive = on  # log automatically received messages, e.g. PoisonPill
              lifecycle = on    # log actor lifecycle changes
              event-stream = on # log subscription changes for Akka.NET event stream
              unhandled = on    # log unhandled messages sent to actors
            }
          }
        }
      ]]>
    </hocon>
  </akka>

Let’s run it now:

akkanetlogging-nlogoutput

You can see that the console output is now limited to what we’re writing in Main(), while the rest has gone into a file called test.log. Since our minlevel is Info, we’re not getting the automatic logging which is configured for the debug log level.

6 thoughts on “Actor Logging with Akka .NET”

    1. See the loggers array in the HOCON? Just add additional entries there. I don’t remember the exact setting for Console, but you should be able to find it in the docs (assuming they are better now than they were back when I wrote this).

Leave a Reply

Your email address will not be published. Required fields are marked *