Skip to content

Commit da3b1d0

Browse files
Fix backward compatibility issue (#232)
Co-authored-by: Aaron Stannard <[email protected]>
1 parent 701400e commit da3b1d0

File tree

4 files changed

+131
-6
lines changed

4 files changed

+131
-6
lines changed

README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,51 @@ var log = Context.GetLogger<SerilogLoggingAdapter>(); // correct
1212
log.Info("My boss makes me use {semantic} logging", "semantic"); // serilog semantic logging format
1313
```
1414

15+
or
16+
17+
```csharp
18+
var log = MyActorSystem.GetLogger<SerilogLoggingAdapter>(myContextObject); // correct
19+
log.Info("My boss makes me use {semantic} logging", "semantic"); // serilog semantic logging format
20+
```
21+
22+
or
23+
```csharp
24+
var log = MyActorSystem.GetLogger<SerilogLoggingAdapter>(contextName, contextType); // correct
25+
log.Info("My boss makes me use {semantic} logging", "semantic"); // serilog semantic logging format
26+
```
27+
1528
This will allow all logging events to be consumed anywhere inside the `ActorSystem`, including places like the Akka.NET TestKit, without throwing `FormatException`s when they encounter semantic logging syntax outside of the `SerilogLogger`.
1629

30+
### Adding Property Enricher To Your Logs
31+
32+
#### Default Properties
33+
You can add property enrichers to the logging adapter that will be added to all logging calls to that logging adapter.
34+
35+
```csharp
36+
var log = Context.GetLogger<SerilogLoggingAdapter>()
37+
.ForContext("Address", "No. 4 Privet Drive")
38+
.ForContext("Town", "Little Whinging")
39+
.ForContext("County", "Surrey")
40+
.ForContext("Country", "England");
41+
log.Info("My boss makes me use {Semantic} logging", "semantic");
42+
```
43+
44+
All logging done using the `log` `ILoggingAdapter` instance will append "Address", "Town", "County", and "Country" properties into the Serilog log.
45+
46+
#### One-off Properties
47+
48+
You can add one-off property to a single log message by appending `PropertyEnricher` instances at the end of your logging calls.
49+
50+
```csharp
51+
var log = Context.GetLogger<SerilogLoggingAdapter>();
52+
log.Info(
53+
"My boss makes me use {Semantic} logging", "semantic",
54+
new PropertyEnricher("County", "Surrey"),
55+
new PropertyEnricher("Country", "England"));
56+
```
57+
58+
This log entry will have "County" and "Country" properties added to it.
59+
1760
## Building this solution
1861
To run the build script associated with this solution, execute the following:
1962

src/Akka.Logger.Serilog.Tests/ForContextSpecs.cs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using FluentAssertions;
1010
using Serilog;
1111
using Serilog.Core;
12+
using Serilog.Core.Enrichers;
1213
using Serilog.Events;
1314
using Xunit;
1415
using Xunit.Abstractions;
@@ -33,7 +34,57 @@ public ForContextSpecs(ITestOutputHelper helper) : base(Config, output: helper)
3334
var logSource = Sys.Name;
3435
var logClass = typeof(ActorSystem);
3536

36-
_loggingAdapter = new SerilogLoggingAdapter(Sys.EventStream, logSource, logClass);
37+
_loggingAdapter = Sys.GetLogger<SerilogLoggingAdapter>(logSource, logClass);
38+
}
39+
40+
[Fact]
41+
public void ShouldLogMessageWithContextProperty()
42+
{
43+
var context = _loggingAdapter
44+
.ForContext("Address", "No. 4 Privet Drive")
45+
.ForContext("Town", "Little Whinging")
46+
.ForContext("County", "Surrey")
47+
.ForContext("Country", "England");
48+
49+
_sink.Clear();
50+
AwaitCondition(() => _sink.Writes.Count == 0);
51+
52+
context.Info("Hi {Person}", "Harry Potter");
53+
AwaitCondition(() => _sink.Writes.Count == 1);
54+
55+
_sink.Writes.TryDequeue(out var logEvent).Should().BeTrue();
56+
logEvent.Level.Should().Be(LogEventLevel.Information);
57+
logEvent.RenderMessage().Should().Contain("Hi \"Harry Potter\"");
58+
logEvent.Properties.Should().ContainKeys("Person", "Address", "Town", "County", "Country");
59+
logEvent.Properties["Person"].ToString().Should().Be("\"Harry Potter\"");
60+
logEvent.Properties["Address"].ToString().Should().Be("\"No. 4 Privet Drive\"");
61+
logEvent.Properties["Town"].ToString().Should().Be("\"Little Whinging\"");
62+
logEvent.Properties["County"].ToString().Should().Be("\"Surrey\"");
63+
logEvent.Properties["Country"].ToString().Should().Be("\"England\"");
64+
}
65+
66+
[Fact]
67+
public void ShouldLogMessageWithContextPropertyAndPropertyEnricher()
68+
{
69+
var context = _loggingAdapter
70+
.ForContext("Address", "No. 4 Privet Drive")
71+
.ForContext("Town", "Little Whinging");
72+
73+
_sink.Clear();
74+
AwaitCondition(() => _sink.Writes.Count == 0);
75+
76+
context.Info("Hi {Person}", "Harry Potter", new PropertyEnricher("County", "Surrey"), new PropertyEnricher("Country", "England"));
77+
AwaitCondition(() => _sink.Writes.Count == 1);
78+
79+
_sink.Writes.TryDequeue(out var logEvent).Should().BeTrue();
80+
logEvent.Level.Should().Be(LogEventLevel.Information);
81+
logEvent.RenderMessage().Should().Contain("Hi \"Harry Potter\"");
82+
logEvent.Properties.Should().ContainKeys("Person", "Address", "Town", "County", "Country");
83+
logEvent.Properties["Person"].ToString().Should().Be("\"Harry Potter\"");
84+
logEvent.Properties["Address"].ToString().Should().Be("\"No. 4 Privet Drive\"");
85+
logEvent.Properties["Town"].ToString().Should().Be("\"Little Whinging\"");
86+
logEvent.Properties["County"].ToString().Should().Be("\"Surrey\"");
87+
logEvent.Properties["Country"].ToString().Should().Be("\"England\"");
3788
}
3889

3990
[Fact]

src/Akka.Logger.Serilog/SerilogLogger.cs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,22 @@ public class SerilogLogger : ReceiveActor, IRequiresMessageQueue<ILoggerMessageQ
2929
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3030
private static string GetFormat(object message)
3131
{
32+
// Unwrap SerilogPayload
33+
if (message is SerilogPayload payload)
34+
message = payload.Message;
35+
3236
return message is LogMessage logMessage ? logMessage.Format : "{Message:l}";
3337
}
3438

3539
private static object[] GetArgs(object message)
3640
{
37-
var logMessage = message as LogMessage;
38-
return logMessage?.Parameters().Where(a => a is not PropertyEnricher).ToArray() ?? new[] { message };
41+
// Unwrap SerilogPayload
42+
if (message is SerilogPayload payload)
43+
message = payload.Message;
44+
45+
return message is LogMessage logMessage
46+
? logMessage.Parameters().Where(a => a is not PropertyEnricher).ToArray()
47+
: new[] { message };
3948
}
4049

4150
private static ILogger GetLogger(LogEvent logEvent) {
@@ -46,14 +55,20 @@ private static ILogger GetLogger(LogEvent logEvent) {
4655
.ForContext("LogSource", logEvent.LogSource)
4756
.ForContext("Thread", logEvent.Thread.ManagedThreadId.ToString("0000"));
4857

49-
if (logEvent.Message is SerilogPayload logMessage)
58+
if (logEvent.Message is SerilogPayload serilogPayload)
5059
{
51-
logger = logMessage.Enrichers.OfType<PropertyEnricher>().Aggregate(logger, (current, enricher) => current.ForContext(enricher));
60+
var enrichers = serilogPayload.Enrichers.ToList();
61+
if (serilogPayload.Message is LogMessage logMessage)
62+
enrichers.AddRange(logMessage.Parameters().OfType<ILogEventEnricher>());
63+
if (enrichers.Count > 0)
64+
logger = logger.ForContext(enrichers);
5265
}
5366

5467
if (logEvent.Message is LogMessage message)
5568
{
56-
logger = message.Parameters().Where(a => a is ILogEventEnricher).Aggregate(logger, (current, enricher) => current.ForContext((ILogEventEnricher)enricher));
69+
var enrichers = message.Parameters().OfType<ILogEventEnricher>().ToList();
70+
if (enrichers.Count > 0)
71+
logger = logger.ForContext(enrichers);
5772
}
5873

5974
return logger;

src/Akka.Logger.Serilog/SerilogLoggingAdapterExtensions.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,21 @@ public static ILoggingAdapter GetLogger<T>(this IActorContext context)
3131

3232
return new SerilogLoggingAdapter(context.System.EventStream, logSource, logClass);
3333
}
34+
35+
public static ILoggingAdapter GetLogger<T>(this ActorSystem system, object logSourceObj)
36+
where T : class, ILoggingAdapter
37+
{
38+
if (logSourceObj is null)
39+
throw new ArgumentNullException(nameof(logSourceObj));
40+
41+
var logSource = LogSource.Create(logSourceObj, system);
42+
return new SerilogLoggingAdapter(system.EventStream, logSource.Source, logSource.Type);
43+
}
44+
45+
public static ILoggingAdapter GetLogger<T>(this ActorSystem system, string logSource, Type logType)
46+
where T : class, ILoggingAdapter
47+
{
48+
return new SerilogLoggingAdapter(system.EventStream, logSource, logType);
49+
}
3450
}
3551
}

0 commit comments

Comments
 (0)