-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
JsonConsoleFormatter: Should not put the same key in the scopes multiple times (not allowed in Json) #39664
Comments
Tagging subscribers to this area: @maryamariyan |
As I was in search for a proper expected behavior, I looked up what Serilog would do. Here's a sample: Code: Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} " +
"{Properties:j}{Exception}")
.CreateLogger();
var exampleUser = new { Id = 1, Name = "Jess", Created = DateTime.Now };
using (LogContext.PushProperty("This is a scope message with number: {CustomNumber}", 11123))
using (LogContext.PushProperty("{AnotherNumber}", 42))
{
Log.Information("Created {@User} on {Created}", exampleUser, DateTime.Now);
}
Log.CloseAndFlush(); Output:
TakeawayFor the sample in the description of this issue, we could theoretically consider changing the below Actual behavior:
To below expected behavior
But, BeginScopes allows to have multiple named properties like below:
which I'm not sure if its possible with Serilog. Serilog has LogContext.PushProperties but that doesn't allow to take a single string with multiple named properties. |
I think the proper expected behavior could look like this:
|
Do you mean Also a similar problem will arise not only with the format but also with properties, if you have for example multiple scopes with the same property name, or if a property as the same name of one of the keys used in the json, which practically became banned property name, unless the properties are moved in a dedicated object. Serilog put the properties in a property bag and collapse all the properties, keeping the value of the last one. It also combines the properties between the scopes and the message. Of course this is an opinionated solution that looe expressiveness. Extending the example to also add duplicated properties: using (logger.BeginScope(new { Message = "Hello" }))
using (logger.BeginScope(new KeyValuePair<string, string>("key", "value")))
using (logger.BeginScope(new KeyValuePair<string, object>("anotherkey", "anothervalue")))
using (logger.BeginScope(new Dictionary<string, object> { ["yetanotherkey"] = "yetanothervalue" }))
using (logger.BeginScope("A string"))
using (logger.BeginScope("This is a scope message with number: {CustomNumber}", 11123))
using (logger.BeginScope("{AnotherNumber}{FinalNumber}", 2, 42))
using (logger.BeginScope("{AnotherNumber}", 3))
{
logger.LogInformation(new Exception(), "exception message with {0} and {CustomNumber}", "stacktrace", 123);
} MEL {
"Timestamp": "2020-07-22T08:44:01.1163808\u002B01:00",
"EventId": 0,
"LogLevel": "Information",
"Category": "JsonLogging.Program",
"Message": "exception message with stacktrace and 123",
"Exception": {
"Message": "Exception of type \u0027System.Exception\u0027 was thrown.",
"Type": "System.Exception",
"StackTrace": [],
"HResult": -2146233088
},
"0": "stacktrace",
"CustomNumber": "123",
"{OriginalFormat}": "exception message with {0} and {CustomNumber}",
"Scopes": {
"0": "{ Message = Hello }",
"1": "[key, value]",
"2": "[anotherkey, anothervalue]",
"yetanotherkey": "yetanothervalue",
"3": "A string",
"CustomNumber": "11123",
"{OriginalFormat}": "This is a scope message with number: {CustomNumber}",
"AnotherNumber": "2",
"FinalNumber": "42",
"{OriginalFormat}": "{AnotherNumber}{FinalNumber}",
"AnotherNumber": "3",
"{OriginalFormat}": "{AnotherNumber}"
}
} Serilog {
"Timestamp": "2020-07-22T08:44:01.1241557+01:00",
"Level": "Information",
"MessageTemplate": "exception message with {0} and {CustomNumber}",
"RenderedMessage": "exception message with \"stacktrace\" and 123",
"Exception": "System.Exception: Exception of type 'System.Exception' was thrown.",
"Properties": {
"0": "stacktrace",
"CustomNumber": 123,
"SourceContext": "JsonLogging.Program",
"AnotherNumber": 3,
"FinalNumber": 42,
"yetanotherkey": "yetanothervalue",
"Scope": [
"{ Message = Hello }",
"[key, value]",
"[anotherkey, anothervalue]",
"A string",
"This is a scope message with number: 11123",
"242",
"3"
]
}
} Another option for the JsonFormatter, to avoid having to collapse the properties and loosing information, could be to keep the scopes separated, however this would make aggregating logs harder so it's not probably a good idea: {
"Scopes": [
{
"Message": "{ Message = Hello }"
},
{
"Message": "[anotherkey, anothervalue]",
"yetanotherkey": "yetanothervalue"
},
{
"Message": "This is a scope message with number: {CustomNumber}",
"{OriginalFormat}": "This is a scope message with number: {CustomNumber}",
"Properties": { "CustomNumber": "11123" }
}
]
} I think your suggestion to aggregate the Also note the current implementation is missing the rendered message for some of the scopes, not sure if that should be tracked as separate issue? I've putted the sample code with this changes to have more edge cases here: https://github.com/alefranz/DotNetPlayground/blob/json-logging/JsonLogging/Program.cs Sorry if I am trying to push the scope of this feature. I've had to deal with formatting logs from ASP.NET Core microservices and it would be nice to get an out of the box solution to have logs in a great format that can be easily queried and aggregated in systems like Seq, Kibana, etc... Thank you, |
yes.
Thanks for trying out the preview. These are great feedback which should definitely be looked into.
Yes. Would you please add a separate issue to track this too? Though I just think the rendered message is just the value of Message in the Json logs. |
Hi @maryamariyan - sorry I'm late to reply; just a thought, we consider Not sure how relevant that is to the current issue (only skim read), but there could be more learnings in our newer JSON formatter worth considering, if it's not too late to change the schema of MEL's JSON output. In particular, the new format uses very short names for built-in properties like We also use a naming scheme that generally leaves user-defined properties at the root level in the resulting JSON document, e.g. if a user event carries a first-class property called Some format details in the linked repo, hope it helps! |
Thanks for the idea. It should make sense for MEL's JSON then to also rename Message to |
Cool! 🙂 FWIW, there are a few tools built to work with the Serilog ("CLEF") format that might be interesting to check out, if you'd consider using the same schema:
Could be food for thought, anyway :-) - HTH! |
@nblumhardt thanks for the links. I created #39917 in the meantime. But will check the above resources to make comparisons. |
Using those compact identifiers will only mitigate it as it would still cause a collision in the unlikely event that you use that name as a property e.g.
|
Description
From #34742 (comment):
If there are multiple scopes which are a string with a place holder, the {OriginalFormat} get added multiple time, but having duplicated keys in JSON, while correct, it is generally not recommended (see RFC8259 section 4. Also I'm unsure if it makes sense to have {OriginalFormat} when the content is only the place holder and it gets pulled out as key.
Repro code
Output:
Regression?
No
Other information
The text was updated successfully, but these errors were encountered: