Logging
![NOTE] For information on how to use Serilog with Akka.NET, we have a dedicated page for that: "Using Serilog for Akka.NET Logging."
How to Log
To log in an actor, create a logger and assign it to a private field:
private readonly ILoggingAdapter _log = Logging.GetLogger(Context);
Use the Debug
, Info
, Warning
and Error
methods to log.
_log.Debug("Some message");
Standard Loggers
Akka.NET comes with two built in loggers.
- StandardOutLogger
- BusLogging
StandardOutLogger
StandardOutLogger
is considered as a minimal logger and implements the MinimalLogger
abstract
class. Its job is simply to output all LogEvent
s emitted by the EventBus
onto the console.
Since it is not an actual actor, ie. it doesn't need the ActorSystem
to operate, it is also
used to log other loggers activity at the very start and very end of the ActorSystem
life cycle.
You can change the minimal logger start and end life cycle behavior by changing the
akka.stdout-loglevel
HOCON settings to OFF
if you do not need these feature in your application.
Advanced MinimalLogger Setup
You can also replace StandardOutLogger
by making your own logger class with an empty constructor
that inherits/implements the MinimalLogger
abstract class and passing the fully qualified class
name into the akka.stdout-logger-class
HOCON settings.
Warning
Be aware that MinimalLogger
implementations are NOT real actors and will NOT have any
access to the ActorSystem
and all of its extensions. All logging done inside a MinimalLogger
have to be done in as simple as possible manner since it is used to log how other loggers are
behaving at the very start and very end of the ActorSystem
life cycle.
Note that MinimalLogger
are NOT interchangeable with other Akka.NET loggers and there can
only be one MinimalLogger
registered with the ActorSystem
in the HOCON settings.
Third Party Loggers
These loggers are also available as separate nuget packages
- Akka.Logger.Serilog which logs using serilog. See Detailed instructions on using Serilog.
- Akka.Logger.NLog which logs using NLog
- Microsoft.Extensions.Logging - which is built into Akka.Hosting.
Note that you need to modify the config as explained below.
NLog Configuration
Example NLog configuration inside your app.config or web.config:
akka {
loggers = ["Akka.Logger.NLog.NLogLogger, Akka.Logger.NLog"]
}
The above NLog components can be found on Nuget (https://www.nuget.org/packages/Akka.Logger.NLog/)
Configuring Custom Loggers
To configure a custom logger inside your Akka.Config, you need to use a fully qualified .NET class name like this:
akka {
loggers = ["NameSpace.ClassName, AssemblyName"]
}
Or using Akka.Hosting, you can configure loggers programmatically using strongly typed references to the underlying logging classes:
builder.Services.AddAkka("MyActorSystem", configurationBuilder =>
{
configurationBuilder
.ConfigureLoggers(setup =>
{
// Example: This sets the minimum log level
setup.LogLevel = LogLevel.DebugLevel;
// Example: Clear all loggers
setup.ClearLoggers();
// Example: Add the default logger
// NOTE: You can also use setup.AddLogger<DefaultLogger>();
setup.AddDefaultLogger();
// Example: Add the ILoggerFactory logger
// NOTE:
// - You can also use setup.AddLogger<LoggerFactoryLogger>();
// - To use a specific ILoggerFactory instance, you can use setup.AddLoggerFactory(myILoggerFactory);
setup.AddLoggerFactory();
// Example: Adding a serilog logger
setup.AddLogger<SerilogLogger>();
})
.WithActors((system, registry) =>
{
var echo = system.ActorOf(act =>
{
act.ReceiveAny((o, context) =>
{
Logging.GetLogger(context.System, "echo").Info($"Actor received {o}");
context.Sender.Tell($"{context.Self} rcv {o}");
});
}, "echo");
registry.TryRegister<Echo>(echo); // register for DI
});
});
Customizing the ILogMessageFormatter
A new feature introduced in Akka.NET v1.5, you now have the ability to customize the ILogMessageFormatter
- the component responsible for formatting output written to all Logger
implementations in Akka.NET.
The primary use case for this is supporting semantic logging across the board in your user-defined actors, which is something that Akka.Logger.Serilog supports quite well.
However, maybe there are certain pieces of data you want to have injected into all of the log messages produced by Akka.NET internally - that's the sort of thing you can accomplish by customizing the ILogMessageFormatter
:
private class CustomLogFormatter : ILogMessageFormatter
{
public string Format(string format, params object[] args)
{
return string.Format("Custom: " + format, args);
}
public string Format(string format, IEnumerable<object> args)
{
return string.Format("Custom: " + format, args.ToArray());
}
}
This class will be responsible for formatting all log messages when they're written out to your configured sinks - once we configure it in HOCON using the akka.logger-formatter
setting:
public static readonly Config Configuration = "akka.logger-formatter = \"Akka.Tests.Loggers.CustomLogFormatterSpec+CustomLogFormatter, Akka.Tests\"";
Logging Unhandled Messages
It is possible to configure akka so that Unhandled messages are logged as Debug log events for debug purposes. This can be achieved using the following configuration setting:
akka {
actor.debug.unhandled = on
}
Example Configuration
akka {
stdout-loglevel = DEBUG
loglevel = DEBUG
log-config-on-start = on
actor {
debug {
receive = on
autoreceive = on
lifecycle = on
event-stream = on
unhandled = on
}
}
}
Logging All Received Messages
It is possible to log all Receive'd messages, usually for debug purposes. This can be achieved by implementing the ILogReceive interface:
public class MyActor : ReceiveActor, ILogReceive
{
public MyActor()
{
Receive<string>(s => Sender.Tell("ok"));
}
}
...
// send a MyActor instance a string message
myActor.Tell("hello");
In your log, expect to see a line such as:
[DEBUG]... received handled message hello from akka://test/deadLetters
This logging can be toggled by configuring akka.actor.debug.receive
.
Filtering Log Messages
Since v1.5.21, Akka.NET supports for filtering log messages based on the LogSource
or the content of a log message.
The goal of this feature is to allow users to run Akka.NET at more verbose logging settings (i.e. LogLevel.Debug
) while not getting completely flooded with unhelpful noise from the Akka.NET logging system. You can use the LogFilterBuilder
to exclude messages don't need while still keeping ones that you do.
Configuring Log Filtering
public static Setup LoggerSetup()
{
var builder = new LogFilterBuilder();
builder.ExcludeSourceContaining("Akka.Tests")
.ExcludeMessageContaining("foo-bar");
return builder.Build();
}
We create a LogFilterBuilder
prior to starting the ActorSystem
and provide it with rules for which logs should be excluded from any of Akka.NET's logged output - this uses the ActorSystemSetup
class functionality that Akka.NET supports for programmatic ActorSystem
configuration:
public static ActorSystemSetup CustomLoggerSetup()
{
var hocon = @$"akka.stdout-logger-class = ""{typeof(CustomLogger).AssemblyQualifiedName}""";
var bootstrapSetup = BootstrapSetup.Create().WithConfig(ConfigurationFactory.ParseString(hocon));
return ActorSystemSetup.Create(bootstrapSetup, LoggerSetup());
}
From there, we can create our ActorSystem
with these rules enabled:
ActorSystemSetup completeSetup = CustomLoggerSetup();
// start the ActorSystem with the LogFilterBuilder rules enabled
ActorSystem mySystem = ActorSystem.Create("MySys", completeSetup);
Log Filtering Rules
There are two built-in types of log filtering rules:
ExcludeSource___
- filters logs based on theLogSource
; this type of filtering is very resource efficient because it doesn't require the log message to be expanded in order for filtering to work.ExcludeMessage___
- filters logs based on the content of the message. More resource-intensive as it does require log messages to be fully expanded prior to filtering.
Note
For an Akka.NET log to be excluded from the output logs, only one filter rule has to return a LogFilterDecision.Drop
.
However, if that's not sufficient for your purposes we also support defining custom rules via the LogFilterBase
class:
/// <summary>
/// Base class for all log filters
/// </summary>
/// <remarks>
/// Worth noting: these run inside the Logging actors, so they're out of band
/// from any high performance workloads already.
///
/// In addition to this - all log filters will only run if the log level is enabled.
///
/// i.e. if we're at INFO level and the filter is set to a lower level, i.e. filtering DEBUG
/// logs, the filter won't even run.
/// </remarks>
public abstract class LogFilterBase : INoSerializationVerificationNeeded, IDeadLetterSuppression
{
/// <summary>
/// Which part of the log message this filter is evaluating?
/// </summary>
/// <remarks>
/// This actually has a performance implication - if we're filtering on the source, which
/// is already fully "expanded" into its final string representation, we can try to fail fast
/// on that without any additional allocations.
///
/// If we're filtering on the message, we have to fully expand the log message first which
/// involves allocations. Users on really tight performance budgets should be aware of this.
/// </remarks>
public abstract LogFilterType FilterType { get; }
/// <summary>
/// Fast path designed to avoid allocating strings if we're filtering on the message content.
/// </summary>
/// <param name="content">Usually the fully expanded message content.</param>
/// <param name="expandedMessage">The fully expanded message, optional.</param>
public abstract LogFilterDecision ShouldKeepMessage(LogEvent content, string? expandedMessage = null);
}
You can filter log messages based on any of the accessibly properties, and for performance reasons any LogFilterBase
that looks at LogFilterType.Content
will be passed in the fully expanded log message as a string?
via the optional expandedMessage
property. This is done in order to avoid allocating the log message every time for each possible rule that might be evaluated.