-
Notifications
You must be signed in to change notification settings - Fork 1.1k
v1.5 logging (version 2) #6408
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
v1.5 logging (version 2) #6408
Conversation
had to add missing `Akka.Event` namespace reference
Going to run some benchmark numbers before I start calling the generic methods we support |
Current version's benchmarks - which is more or less performing the same work under the hood that the BenchmarkDotNet=v0.13.2, OS=Windows 10 (10.0.19044.2604/21H2/November2021Update)
AMD Ryzen 7 1700, 1 CPU, 16 logical and 8 physical cores
.NET SDK=7.0.100
[Host] : .NET 7.0.0 (7.0.22.51805), X64 RyuJIT AVX2
Job-KJHKWD : .NET 7.0.0 (7.0.22.51805), X64 RyuJIT AVX2
InvocationCount=1 UnrollFactor=1
|
Changed all method calls to use our new generic overloads (I'll need to compare before / after .DLL sizes to make sure we haven't done anything evil there) - performance for logging calls is basically now bounded at 260ish nanoseconds for up to 6 parameters. BenchmarkDotNet=v0.13.2, OS=Windows 10 (10.0.19044.2604/21H2/November2021Update)
AMD Ryzen 7 1700, 1 CPU, 16 logical and 8 physical cores
.NET SDK=7.0.100
[Host] : .NET 7.0.0 (7.0.22.51805), X64 RyuJIT AVX2
Job-PCKFPT : .NET 7.0.0 (7.0.22.51805), X64 RyuJIT AVX2
InvocationCount=1 UnrollFactor=1
|
Moved all generic methods out of the Now down to about 240 ns. BenchmarkDotNet=v0.13.2, OS=Windows 10 (10.0.19044.2604/21H2/November2021Update)
AMD Ryzen 7 1700, 1 CPU, 16 logical and 8 physical cores
.NET SDK=7.0.100
[Host] : .NET 7.0.0 (7.0.22.51805), X64 RyuJIT AVX2
Job-MAIQQP : .NET 7.0.0 (7.0.22.51805), X64 RyuJIT AVX2
InvocationCount=1 UnrollFactor=1
|
@Arkatufus does this mean I killed the multi-node testkit?
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Detailed my changes, their rationales, and their benefits.
@@ -48,7 +48,7 @@ private void Write(LogEvent e) | |||
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)}]"; | |||
$"Received a malformed formatted message. Log level: [{e.LogLevel()}], Template: [{msg.Format}], args: [{string.Join(",", msg.Unformatted())}]"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We now have a standard method on LogMessage
called Unformatted()
which returns the non-formatted string contents of the payload. This is designed for troubleshooting format exceptions that occur in user / system log statements akin to what we have in 1.4.
@@ -46,7 +47,7 @@ public static ServiceProviderSetup Create(IServiceProvider provider) | |||
/// The <see cref="IDependencyResolver"/> will be used to access previously registered services | |||
/// in the creation of actors and other pieces of infrastructure inside Akka.NET. | |||
/// | |||
/// The constructor is internal. Please use <see cref="Create"/> to create a new instance. | |||
/// The constructor is internal. Please use <see cref="Dispatch.SysMsg.Create"/> to create a new instance. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is wrong and needs to be updated - I think I ReSharper'd this when I was trying to add using Akka.Event
everywhere it was needed.
@@ -1383,7 +1384,7 @@ public IActorRef ShardRegion(string typeName) | |||
/// Retrieve the actor reference of the <see cref="Sharding.ShardRegion"/> actor that will act as a proxy to the | |||
/// named entity type running in another data center. A proxy within the same data center can be accessed | |||
/// with <see cref="Sharding.ShardRegion"/> instead of this method. The entity type must be registered with the | |||
/// <see cref="StartProxy"/> method before it can be used here. Messages to the entity is always sent | |||
/// <see cref="ClusterShardingGuardian.StartProxy"/> method before it can be used here. Messages to the entity is always sent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of these ClusterShardingGuardian
calls are wrong as well - I think this was a ReSharper'ing issue. I'll fix these.
|
||
private void AddLogMessage(LogEvent m) | ||
|
||
private LogEvent CreateLogEvent(LogLevel logLevel, object message, Exception cause = null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had to update the test logger in order to support the API changes made to ILoggingAdapter
.
Arg = arg; | ||
} | ||
|
||
public T Arg { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generic argument for the type of object that can be formatted - remember the generic constraint: must support IEnumerable<object>
.
/// <summary> | ||
/// Works akin to the original <see cref="LogMessage"/> class with an array of objects as the format args. | ||
/// </summary> | ||
internal sealed class DefaultLogMessage : LogMessage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the type we use for handling old object[]
calls on the logging APIs.
|
||
internal readonly struct LogValues<T1, T2> : IReadOnlyList<object> | ||
{ | ||
private readonly T1 _value1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't allocate any arrays internally - rather we produce an IEnumerable<object>
on-demand when the object is being read for formatting, rather than when it's originally allocated. This is the raison d'être for this entire design: defer allocations so they're out of the critical path of /user
and /system
actors.
We have 6 different generic versions of this readonly struct
- each one uses the same technique to prevent allocations and the benchmarks demonstrate how this works.
/// <param name="format">The message that is being logged.</param> | ||
/// <param name="args">An optional list of items used to format the message.</param> | ||
public void Info(string format, params object[] args) { } | ||
void Log(LogLevel logLevel, Exception cause, LogMessage message); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ILoggingAdapter
only supports two direct logging calls now:
void Log(LogLevel logLevel, Exception cause, LogMessage message);
void Log(LogLevel logLevel, Exception cause, string format);
Everything else is inputted via the extension methods earlier in the file.
@@ -110,12 +110,11 @@ internal static class ExpressionBasedParser | |||
} | |||
} | |||
} | |||
catch (Exception exception) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed some compilation warnings here.
Minus the issues with the MNTR, test suite passes. I'm going to need to fix a few other issues found in this PR as well. As for documentation - we'll need to document these changes in the https://github.com/akkadotnet/akka.net/blob/dev/docs/community/whats-new/akkadotnet-v1.5-upgrade-advisories.md area |
// /// <param name="logLevel">The level used to log the message.</param> | ||
// /// <param name="format">The message that is being logged.</param> | ||
// /// <param name="args">An optional list of items used to format the message.</param> | ||
// void Log(LogLevel logLevel, string format, params object[] args); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to delete this (can do in later PR)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Microsoft does have their own class called FormattableString
for these, but I think its too convoluted because it has to handle culture as well. This implementation should be better for our use case.
When we start work on 1.6, at which point I'm thinking we'll ditch .NET Standard 2.0 and go full-bore on .NET 7/8, we can start taking advantage of some of these newer APIs. |
Ok, going nuclear on API changes for Akka.NET v1.5 starting now ;) |
Changes
Fixes #6394
Checklist
For significant changes, please ensure that the following have been completed (delete if not relevant):
Latest
dev
BenchmarksThis PR's Benchmarks
Include data from after this change here.