diff --git a/docs/articles/utilities/logging.md b/docs/articles/utilities/logging.md
index bbe33664d2e..097212a5499 100644
--- a/docs/articles/utilities/logging.md
+++ b/docs/articles/utilities/logging.md
@@ -25,6 +25,28 @@ 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 behaviour 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.
+
## Contrib Loggers
These loggers are also available as separate nuget packages
diff --git a/src/contrib/testkits/Akka.TestKit.Xunit/Internals/Loggers.cs b/src/contrib/testkits/Akka.TestKit.Xunit/Internals/Loggers.cs
index 66244c36e28..6b64b006915 100644
--- a/src/contrib/testkits/Akka.TestKit.Xunit/Internals/Loggers.cs
+++ b/src/contrib/testkits/Akka.TestKit.Xunit/Internals/Loggers.cs
@@ -5,6 +5,7 @@
//
//-----------------------------------------------------------------------
+using System;
using Akka.Actor;
using Akka.Event;
using Xunit.Abstractions;
@@ -16,20 +17,44 @@ namespace Akka.TestKit.Xunit.Internals
///
public class TestOutputLogger : ReceiveActor
{
+ private readonly ITestOutputHelper _output;
+
///
/// Initializes a new instance of the class.
///
/// The provider used to write test output.
public TestOutputLogger(ITestOutputHelper output)
{
- Receive(e => output.WriteLine(e.ToString()));
- Receive(e => output.WriteLine(e.ToString()));
- Receive(e => output.WriteLine(e.ToString()));
- Receive(e => output.WriteLine(e.ToString()));
+ _output = output;
+
+ Receive(Write);
+ Receive(Write);
+ Receive(Write);
+ Receive(Write);
Receive(e =>
{
e.LoggingBus.Subscribe(Self, typeof (LogEvent));
});
}
+
+ private void Write(LogEvent e)
+ {
+ try
+ {
+ _output.WriteLine(e.ToString());
+ }
+ catch (FormatException ex)
+ {
+ if (e.Message is LogMessage msg)
+ {
+ var message =
+ $"Received a malformed formatted message. Log level: [{e.LogLevel()}], Template: [{msg.Format}], args: [{string.Join(",", msg.Args)}]";
+ if(e.Cause != null)
+ throw new AggregateException(message, ex, e.Cause);
+ throw new FormatException(message, ex);
+ }
+ throw;
+ }
+ }
}
}
diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt
index 58979a4589c..1f45083d22c 100644
--- a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt
+++ b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt
@@ -1696,6 +1696,7 @@ namespace Akka.Actor
public bool SerializeAllMessages { get; }
public Akka.Actor.Setup.ActorSystemSetup Setup { get; }
public string StdoutLogLevel { get; }
+ public Akka.Event.MinimalLogger StdoutLogger { get; }
public string SupervisorStrategyClass { get; }
public Akka.Actor.ActorSystem System { get; }
public System.TimeSpan UnstartedPushTimeout { get; }
@@ -3089,7 +3090,7 @@ namespace Akka.Event
public static string FromType(System.Type t, Akka.Actor.ActorSystem system) { }
public static System.Type SourceType(object o) { }
}
- public class LoggerInitialized : Akka.Actor.INoSerializationVerificationNeeded
+ public class LoggerInitialized : Akka.Actor.INoSerializationVerificationNeeded, Akka.Event.IDeadLetterSuppression
{
public LoggerInitialized() { }
}
@@ -3099,7 +3100,6 @@ namespace Akka.Event
}
public class static Logging
{
- public static readonly Akka.Event.StandardOutLogger StandardOutLogger;
public static System.Type ClassFor(this Akka.Event.LogLevel logLevel) { }
public static Akka.Event.ILoggingAdapter GetLogger(this Akka.Actor.IActorContext context, Akka.Event.ILogMessageFormatter logMessageFormatter = null) { }
public static Akka.Event.ILoggingAdapter GetLogger(Akka.Actor.ActorSystem system, object logSourceObj, Akka.Event.ILogMessageFormatter logMessageFormatter = null) { }
@@ -3152,6 +3152,14 @@ namespace Akka.Event
public void SetLogLevel(Akka.Event.LogLevel logLevel) { }
public void StartStdoutLogger(Akka.Actor.Settings config) { }
}
+ public abstract class MinimalLogger : Akka.Actor.MinimalActorRef
+ {
+ protected MinimalLogger() { }
+ public virtual Akka.Actor.ActorPath Path { get; }
+ public virtual Akka.Actor.IActorRefProvider Provider { get; }
+ protected abstract void Log(object message);
+ protected virtual void TellInternal(object message, Akka.Actor.IActorRef sender) { }
+ }
public sealed class NoLogger : Akka.Event.ILoggingAdapter
{
public static readonly Akka.Event.ILoggingAdapter Instance;
@@ -3172,18 +3180,15 @@ namespace Akka.Event
public void Warning(string format, params object[] args) { }
public void Warning(System.Exception cause, string format, params object[] args) { }
}
- public class StandardOutLogger : Akka.Actor.MinimalActorRef
+ public class StandardOutLogger : Akka.Event.MinimalLogger
{
public StandardOutLogger() { }
public static System.ConsoleColor DebugColor { get; set; }
public static System.ConsoleColor ErrorColor { get; set; }
public static System.ConsoleColor InfoColor { get; set; }
- public override Akka.Actor.ActorPath Path { get; }
- public override Akka.Actor.IActorRefProvider Provider { get; }
public static bool UseColors { get; set; }
public static System.ConsoleColor WarningColor { get; set; }
- public static void PrintLogEvent(Akka.Event.LogEvent logEvent) { }
- protected override void TellInternal(object message, Akka.Actor.IActorRef sender) { }
+ protected override void Log(object message) { }
}
public class Subscription
{
diff --git a/src/core/Akka.TestKit/EventFilter/TestEventListener.cs b/src/core/Akka.TestKit/EventFilter/TestEventListener.cs
index a959d8c9da3..a42ca7b2fd5 100644
--- a/src/core/Akka.TestKit/EventFilter/TestEventListener.cs
+++ b/src/core/Akka.TestKit/EventFilter/TestEventListener.cs
@@ -30,57 +30,65 @@ public class TestEventListener : DefaultLogger
/// TBD
protected override bool Receive(object message)
{
- if(message is InitializeLogger)
+ switch (message)
{
- var initLogger = (InitializeLogger)message;
- var bus = initLogger.LoggingBus;
- var self = Context.Self;
- bus.Subscribe(self, typeof(Mute));
- bus.Subscribe(self, typeof(Unmute));
- bus.Subscribe(self, typeof(DeadLetter));
- bus.Subscribe(self, typeof(UnhandledMessage));
- Sender.Tell(new LoggerInitialized());
- }
- else if(message is Mute)
- {
- var mute = (Mute)message;
- foreach(var filter in mute.Filters)
+ case InitializeLogger initLogger:
{
- AddFilter(filter);
+ base.Receive(message);
+ var bus = initLogger.LoggingBus;
+ var self = Context.Self;
+ bus.Subscribe(self, typeof(Mute));
+ bus.Subscribe(self, typeof(Unmute));
+ bus.Subscribe(self, typeof(DeadLetter));
+ bus.Subscribe(self, typeof(UnhandledMessage));
+ Sender.Tell(new LoggerInitialized());
+ break;
}
- }
- else if(message is Unmute)
- {
- var unmute = (Unmute)message;
- foreach(var filter in unmute.Filters)
+ case Mute mute:
{
- RemoveFilter(filter);
+ foreach(var filter in mute.Filters)
+ {
+ AddFilter(filter);
+ }
+
+ break;
}
- }
- else if(message is LogEvent)
- {
- var logEvent = (LogEvent)message;
- if(!ShouldFilter(logEvent))
+ case Unmute unmute:
{
- Print(logEvent);
+ foreach(var filter in unmute.Filters)
+ {
+ RemoveFilter(filter);
+ }
+
+ break;
}
+ case LogEvent logEvent:
+ {
+ if(!ShouldFilter(logEvent))
+ {
+ Print(logEvent);
+ }
+
+ break;
+ }
+ case DeadLetter letter:
+ HandleDeadLetter(letter);
+ break;
+
+ case UnhandledMessage un:
+ {
+ var rcp = un.Recipient;
+ var warning = new Warning(rcp.Path.ToString(), rcp.GetType(), "Unhandled message from " + un.Sender + ": " + un.Message);
+ if(!ShouldFilter(warning))
+ Print(warning);
+ break;
+ }
+
+ default:
+ Print(new Debug(Context.System.Name,GetType(),message));
+ break;
}
- else if(message is DeadLetter)
- {
- HandleDeadLetter((DeadLetter)message);
- }
- else if(message is UnhandledMessage)
- {
- var un = (UnhandledMessage) message;
- var rcp = un.Recipient;
- var warning = new Warning(rcp.Path.ToString(), rcp.GetType(), "Unhandled message from " + un.Sender + ": " + un.Message);
- if(!ShouldFilter(warning))
- Print(warning);
- }
- else
- {
- Print(new Debug(Context.System.Name,GetType(),message));
- }
+
return true;
}
diff --git a/src/core/Akka.Tests/Configuration/ConfigurationSpec.cs b/src/core/Akka.Tests/Configuration/ConfigurationSpec.cs
index 7074746fa98..18654b6f9f9 100644
--- a/src/core/Akka.Tests/Configuration/ConfigurationSpec.cs
+++ b/src/core/Akka.Tests/Configuration/ConfigurationSpec.cs
@@ -42,6 +42,8 @@ public void The_default_configuration_file_contain_all_configuration_properties(
settings.LoggerStartTimeout.Seconds.ShouldBe(5);
settings.LogLevel.ShouldBe("INFO");
settings.StdoutLogLevel.ShouldBe("WARNING");
+ settings.StdoutLogger.Should().NotBeNull();
+ settings.StdoutLogger.Should().BeOfType();
settings.LogConfigOnStart.ShouldBeFalse();
settings.LogDeadLetters.ShouldBe(10);
settings.LogDeadLettersDuringShutdown.ShouldBeFalse();
diff --git a/src/core/Akka.Tests/Loggers/LoggerSpec.cs b/src/core/Akka.Tests/Loggers/LoggerSpec.cs
index 72681e225a6..d7079a1de5a 100644
--- a/src/core/Akka.Tests/Loggers/LoggerSpec.cs
+++ b/src/core/Akka.Tests/Loggers/LoggerSpec.cs
@@ -2,7 +2,10 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
+using System.Threading.Tasks;
using Akka.Actor;
+using Akka.Actor.Internal;
+using Akka.Actor.Setup;
using Akka.Configuration;
using Akka.Event;
using Akka.TestKit;
@@ -142,5 +145,26 @@ private class FakeException : Exception
public FakeException(string message) : base(message)
{ }
}
+
+ [Fact]
+ public async Task ShouldBeAbleToReplaceStandardOutLoggerWithCustomMinimalLogger()
+ {
+ var config = ConfigurationFactory
+ .ParseString("akka.stdout-logger-class = \"Akka.Tests.Loggers.LoggerSpec+CustomLogger, Akka.Tests\"")
+ .WithFallback(ConfigurationFactory.Default());
+
+ var system = ActorSystem.Create("MinimalLoggerTest", config);
+ system.Settings.StdoutLogger.Should().BeOfType();
+ await system.Terminate();
+ }
+
+ public class CustomLogger : MinimalLogger
+ {
+ protected override void Log(object message)
+ {
+ Console.WriteLine(message);
+ }
+ }
+
}
}
diff --git a/src/core/Akka.Tests/Loggers/ShutdownLoggerSpec.cs b/src/core/Akka.Tests/Loggers/ShutdownLoggerSpec.cs
new file mode 100644
index 00000000000..796a4404d26
--- /dev/null
+++ b/src/core/Akka.Tests/Loggers/ShutdownLoggerSpec.cs
@@ -0,0 +1,84 @@
+// //-----------------------------------------------------------------------
+// //
+// // Copyright (C) 2009-2021 Lightbend Inc.
+// // Copyright (C) 2013-2021 .NET Foundation
+// //
+// //-----------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+using Akka.Actor;
+using Akka.Configuration;
+using Akka.Event;
+using Akka.TestKit;
+using FluentAssertions;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Akka.Tests.Loggers
+{
+ public class ShutdownLoggerSpec: AkkaSpec
+ {
+ private static readonly Config Config = ConfigurationFactory.ParseString(@"
+akka.loglevel = OFF
+akka.stdout-loglevel = OFF
+akka.stdout-logger-class = ""Akka.Tests.Loggers.ThrowingLogger, Akka.Tests""");
+
+ public ShutdownLoggerSpec(ITestOutputHelper output) : base(Config, output)
+ {
+ }
+
+ [Fact(DisplayName = "StandardOutLogger should not be called on shutdown when stdout-loglevel is set to OFF")]
+ public async Task StandardOutLoggerShouldNotBeCalled()
+ {
+ Sys.Settings.StdoutLogger.Should().BeOfType();
+
+ var probeRef = Sys.ActorOf(Props.Create(() => new LogProbe()));
+ probeRef.Tell(new InitializeLogger(Sys.EventStream));
+ var probe = await probeRef.Ask("hey");
+
+ await Sys.Terminate();
+
+ await Task.Delay(RemainingOrDefault);
+ if (probe.Events.Any(o => o is Error err && err.Cause is ShutdownLogException))
+ throw new Exception("Test failed, log should not be called after shutdown.");
+ }
+ }
+
+ internal class LogProbe : ReceiveActor
+ {
+ public List Events { get; } = new List();
+
+ public LogProbe()
+ {
+ Receive(msg => Sender.Tell(this));
+ Receive(Events.Add);
+ Receive(e =>
+ {
+ e.LoggingBus.Subscribe(Self, typeof (LogEvent));
+ Sender.Tell(new LoggerInitialized());
+ });
+
+ }
+ }
+
+ internal class ShutdownLogException : Exception
+ {
+ public ShutdownLogException()
+ { }
+
+ public ShutdownLogException(string msg) : base(msg)
+ { }
+ }
+
+ internal class ThrowingLogger : MinimalLogger
+ {
+ protected override void Log(object message)
+ {
+ throw new ShutdownLogException("This logger should never be called.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/core/Akka/Actor/Settings.cs b/src/core/Akka/Actor/Settings.cs
index 906c016e3a5..1ecf387cbdc 100644
--- a/src/core/Akka/Actor/Settings.cs
+++ b/src/core/Akka/Actor/Settings.cs
@@ -11,6 +11,7 @@
using Akka.Actor.Setup;
using Akka.Configuration;
using Akka.Dispatch;
+using Akka.Event;
using Akka.Routing;
using ConfigurationFactory = Akka.Configuration.ConfigurationFactory;
@@ -109,6 +110,31 @@ public Settings(ActorSystem system, Config config, ActorSystemSetup setup)
LogLevel = Config.GetString("akka.loglevel", null);
StdoutLogLevel = Config.GetString("akka.stdout-loglevel", null);
+
+ var stdoutClassName = Config.GetString("akka.stdout-logger-class", null);
+ if (string.IsNullOrWhiteSpace(stdoutClassName))
+ {
+ StdoutLogger = new StandardOutLogger();
+ }
+ else
+ {
+ var stdoutLoggerType = Type.GetType(stdoutClassName);
+ if (stdoutLoggerType == null)
+ throw new ArgumentException($"Could not load type of {stdoutClassName} for standard out logger.");
+ if(!typeof(MinimalLogger).IsAssignableFrom(stdoutLoggerType))
+ throw new ArgumentException("Standard out logger type must inherit from the MinimalLogger abstract class.");
+
+ try
+ {
+ StdoutLogger = (MinimalLogger)Activator.CreateInstance(stdoutLoggerType);
+ }
+ catch (MissingMethodException)
+ {
+ throw new MissingMethodException(
+ "Standard out logger type must inherit from the MinimalLogger abstract class and have an empty constructor.");
+ }
+ }
+
Loggers = Config.GetStringList("akka.loggers", new string[] { });
LoggersDispatcher = Config.GetString("akka.loggers-dispatcher", null);
LoggerStartTimeout = Config.GetTimeSpan("akka.logger-startup-timeout", null);
@@ -247,6 +273,11 @@ public Settings(ActorSystem system, Config config, ActorSystemSetup setup)
/// The stdout log level.
public string StdoutLogLevel { get; private set; }
+ ///
+ /// Returns a singleton instance of the standard out logger.
+ ///
+ public MinimalLogger StdoutLogger { get; }
+
///
/// Gets the loggers.
///
diff --git a/src/core/Akka/Configuration/Pigeon.conf b/src/core/Akka/Configuration/Pigeon.conf
index a2d17b96a50..49a1fce3478 100644
--- a/src/core/Akka/Configuration/Pigeon.conf
+++ b/src/core/Akka/Configuration/Pigeon.conf
@@ -42,6 +42,10 @@ akka {
# Log level for the very basic logger activated during AkkaApplication startup
# Options: OFF, ERROR, WARNING, INFO, DEBUG
stdout-loglevel = "WARNING"
+
+ # Fully qualified class name (FQCN) of the very basic logger used on startup and shutdown.
+ # You can substitute the logger by supplying an FQCN to a custom class that implements Akka.Event.MinimumLogger
+ stdout-logger-class = "Akka.Event.StandardOutLogger"
# 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.
diff --git a/src/core/Akka/Event/DefaultLogger.cs b/src/core/Akka/Event/DefaultLogger.cs
index 7d03e27dfef..730b238138b 100644
--- a/src/core/Akka/Event/DefaultLogger.cs
+++ b/src/core/Akka/Event/DefaultLogger.cs
@@ -16,6 +16,8 @@ namespace Akka.Event
///
public class DefaultLogger : ActorBase, IRequiresMessageQueue
{
+ private MinimalLogger _stdoutLogger;
+
///
/// TBD
///
@@ -23,17 +25,18 @@ public class DefaultLogger : ActorBase, IRequiresMessageQueueTBD
protected override bool Receive(object message)
{
- if (message is InitializeLogger)
+ switch (message)
{
- Sender.Tell(new LoggerInitialized());
- return true;
+ case InitializeLogger _:
+ _stdoutLogger = Context.System.Settings.StdoutLogger;
+ Sender.Tell(new LoggerInitialized());
+ return true;
+ case LogEvent logEvent:
+ Print(logEvent);
+ return true;
+ default:
+ return false;
}
- var logEvent = message as LogEvent;
- if (logEvent == null)
- return false;
-
- Print(logEvent);
- return true;
}
///
@@ -42,7 +45,10 @@ protected override bool Receive(object message)
/// The log event that is to be output.
protected virtual void Print(LogEvent logEvent)
{
- StandardOutLogger.PrintLogEvent(logEvent);
+ if (_stdoutLogger == null)
+ throw new Exception("Logger has not been initialized yet.");
+
+ _stdoutLogger.Tell(logEvent);
}
}
}
diff --git a/src/core/Akka/Event/LoggerInitialized.cs b/src/core/Akka/Event/LoggerInitialized.cs
index d7aa17a92ec..2fe9b3e0222 100644
--- a/src/core/Akka/Event/LoggerInitialized.cs
+++ b/src/core/Akka/Event/LoggerInitialized.cs
@@ -12,7 +12,7 @@ namespace Akka.Event
///
/// This class represents a message used to notify subscribers that a logger has been initialized.
///
- public class LoggerInitialized : INoSerializationVerificationNeeded
+ public class LoggerInitialized : INoSerializationVerificationNeeded, IDeadLetterSuppression
{
}
}
diff --git a/src/core/Akka/Event/LoggerMailbox.cs b/src/core/Akka/Event/LoggerMailbox.cs
index fb8e5d7b518..51e32e25e87 100644
--- a/src/core/Akka/Event/LoggerMailbox.cs
+++ b/src/core/Akka/Event/LoggerMailbox.cs
@@ -82,15 +82,15 @@ void IMessageQueue.CleanUp(IActorRef owner, IMessageQueue deadletters)
{
if (HasMessages)
{
- Envelope envelope;
-
+ var logger = _system.Settings.StdoutLogger;
+
// Drain all remaining messages to the StandardOutLogger.
// CleanUp is called after switching out the mailbox, which is why
// this kind of look works without a limit.
- while (TryDequeue(out envelope))
+ while (TryDequeue(out var envelope))
{
// Logging.StandardOutLogger is a MinimalActorRef, i.e. not a "real" actor
- Logging.StandardOutLogger.Tell(envelope.Message, envelope.Sender);
+ logger.Tell(envelope.Message, envelope.Sender);
}
}
MessageQueue.CleanUp(owner, deadletters);
diff --git a/src/core/Akka/Event/Logging.cs b/src/core/Akka/Event/Logging.cs
index 05d334b8eac..a1104ed3dd5 100644
--- a/src/core/Akka/Event/Logging.cs
+++ b/src/core/Akka/Event/Logging.cs
@@ -147,11 +147,6 @@ public static string SimpleName(Type t)
private const string Off = "OFF";
private const LogLevel OffLogLevel = (LogLevel) int.MaxValue;
- ///
- /// Returns a singleton instance of the standard out logger.
- ///
- public static readonly StandardOutLogger StandardOutLogger = new StandardOutLogger();
-
///
/// Retrieves the log event class associated with the specified log level.
///
diff --git a/src/core/Akka/Event/LoggingBus.cs b/src/core/Akka/Event/LoggingBus.cs
index 2282eb6b3f8..fbe8e70149b 100644
--- a/src/core/Akka/Event/LoggingBus.cs
+++ b/src/core/Akka/Event/LoggingBus.cs
@@ -102,7 +102,7 @@ internal void StartDefaultLoggers(ActorSystemImpl system)
throw new ConfigurationException($@"Logger specified in config cannot be found: ""{strLoggerType}""");
}
- if (loggerType == typeof(StandardOutLogger))
+ if (typeof(MinimalLogger).IsAssignableFrom(loggerType))
{
shouldRemoveStandardOutLogger = false;
continue;
@@ -143,8 +143,9 @@ internal void StartDefaultLoggers(ActorSystemImpl system)
if (shouldRemoveStandardOutLogger)
{
- Publish(new Debug(logName, GetType(), "StandardOutLogger being removed"));
- Unsubscribe(Logging.StandardOutLogger);
+ var stdOutLogger = system.Settings.StdoutLogger;
+ Publish(new Debug(logName, GetType(), $"{Logging.SimpleName(stdOutLogger)} being removed"));
+ Unsubscribe(stdOutLogger);
}
Publish(new Debug(logName, GetType(), "Default Loggers started"));
@@ -157,19 +158,18 @@ internal void StartDefaultLoggers(ActorSystemImpl system)
internal void StopDefaultLoggers(ActorSystem system)
{
var level = LogLevel; // volatile access before reading loggers
- if (!_loggers.Any(c => c is StandardOutLogger))
+ if (!_loggers.Any(c => c is MinimalLogger))
{
SetUpStdoutLogger(system.Settings);
- Publish(new Debug(SimpleName(this), GetType(), "Shutting down: StandardOutLogger started"));
+ Publish(new Debug(SimpleName(this), GetType(), $"Shutting down: {Logging.SimpleName(system.Settings.StdoutLogger)} started"));
}
foreach (var logger in _loggers)
{
- if (!(logger is StandardOutLogger))
+ if (!(logger is MinimalLogger))
{
Unsubscribe(logger);
- var internalActorRef = logger as IInternalActorRef;
- if (internalActorRef != null)
+ if (logger is IInternalActorRef internalActorRef)
{
internalActorRef.Stop();
}
@@ -214,9 +214,9 @@ private string CreateLoggerName(Type actorClass)
}
///
- /// Starts the logger.
+ /// Starts the logger.
///
- /// The configuration used to configure the .
+ /// The configuration used to configure the .
public void StartStdoutLogger(Settings config)
{
SetUpStdoutLogger(config);
@@ -226,7 +226,7 @@ public void StartStdoutLogger(Settings config)
private void SetUpStdoutLogger(Settings config)
{
var logLevel = Logging.LogLevelFor(config.StdoutLogLevel);
- SubscribeLogLevelAndAbove(logLevel, Logging.StandardOutLogger);
+ SubscribeLogLevelAndAbove(logLevel, config.StdoutLogger);
}
///
diff --git a/src/core/Akka/Event/StandardOutLogger.cs b/src/core/Akka/Event/StandardOutLogger.cs
index 6493c136fc4..b213d0e4bac 100644
--- a/src/core/Akka/Event/StandardOutLogger.cs
+++ b/src/core/Akka/Event/StandardOutLogger.cs
@@ -13,6 +13,38 @@
namespace Akka.Event
{
+ public abstract class MinimalLogger : MinimalActorRef
+ {
+ ///
+ /// N/A
+ ///
+ /// This exception is automatically thrown since does not support this property.
+ public sealed override IActorRefProvider Provider
+ {
+ get { throw new NotSupportedException("This logger does not provide."); }
+ }
+
+ ///
+ /// The path where this logger currently resides.
+ ///
+ public sealed override ActorPath Path { get; } = new RootActorPath(Address.AllSystems, "/StandardOutLogger");
+
+ protected sealed override void TellInternal(object message, IActorRef sender)
+ {
+ switch (message)
+ {
+ case null:
+ throw new ArgumentNullException(nameof(message), "The message to log must not be null.");
+
+ default:
+ Log(message);
+ break;
+ }
+ }
+
+ protected abstract void Log(object message);
+ }
+
///
/// This class represents an event logger that logs its messages to standard output (e.g. the console).
///
@@ -21,9 +53,8 @@ namespace Akka.Event
/// even before normal logging is started.
///
///
- public class StandardOutLogger : MinimalActorRef
+ public class StandardOutLogger : MinimalLogger
{
- private readonly ActorPath _path = new RootActorPath(Address.AllSystems, "/StandardOutLogger");
///
/// Initializes the class.
@@ -37,23 +68,6 @@ static StandardOutLogger()
UseColors = true;
}
- ///
- /// N/A
- ///
- /// This exception is automatically thrown since does not support this property.
- public override IActorRefProvider Provider
- {
- get { throw new NotSupportedException("This logger does not provide."); }
- }
-
- ///
- /// The path where this logger currently resides.
- ///
- public override ActorPath Path
- {
- get { return _path; }
- }
-
///
/// Handles incoming log events by printing them to the console.
///
@@ -62,22 +76,20 @@ public override ActorPath Path
///
/// This exception is thrown if the given is undefined.
///
- protected override void TellInternal(object message, IActorRef sender)
+ protected override void Log(object message)
{
- if(message == null)
- throw new ArgumentNullException(nameof(message), "The message to log must not be null.");
-
- var logEvent = message as LogEvent;
- if (logEvent != null)
+ switch (message)
{
- PrintLogEvent(logEvent);
- }
- else
- {
- Console.WriteLine(message);
+ case LogEvent logEvent:
+ PrintLogEvent(logEvent);
+ break;
+
+ default:
+ Console.WriteLine(message);
+ break;
}
}
-
+
///
/// The foreground color to use when printing Debug events to the console.
///
@@ -107,7 +119,7 @@ protected override void TellInternal(object message, IActorRef sender)
/// Prints a specified event to the console.
///
/// The event to print
- public static void PrintLogEvent(LogEvent logEvent)
+ internal static void PrintLogEvent(LogEvent logEvent)
{
try
{