Skip to content

Commit 1e1479e

Browse files
authored
Merge pull request #232 from jimbojim1997/beginscope-valuetuple
Support `ValueTuple<string, object?>` as state of scope.
2 parents 3b56cb4 + 85172ad commit 1e1479e

File tree

4 files changed

+145
-6
lines changed

4 files changed

+145
-6
lines changed

README.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ using (_logger.BeginScope("Transaction")) {
115115
If you simply want to add a "bag" of additional properties to your log events, however, this extension method approach can be overly verbose. For example, to add `TransactionId` and `ResponseJson` properties to your log events, you would have to do something like the following:
116116

117117
```csharp
118-
// WRONG! Prefer the dictionary approach below instead
118+
// WRONG! Prefer the dictionary or value tuple approach below instead
119119
using (_logger.BeginScope("TransactionId: {TransactionId}, ResponseJson: {ResponseJson}", 12345, jsonString)) {
120120
_logger.LogInformation("Completed in {DurationMs}ms...", 30);
121121
}
@@ -157,6 +157,23 @@ using (_logger.BeginScope(scopeProps) {
157157
// }
158158
```
159159

160+
Alternatively provide a `ValueTuple<string, object?>` to this method, where `Item1` is the property name and `Item2` is the property value. Note that `T2` _must_ be `object?`.
161+
162+
```csharp
163+
using (_logger.BeginScope(("TransactionId", (object?)12345)) {
164+
_logger.LogInformation("Transaction completed in {DurationMs}ms...", 30);
165+
}
166+
// Example JSON output:
167+
// {
168+
// "@t":"2020-10-29T19:05:56.4176816Z",
169+
// "@m":"Completed in 30ms...",
170+
// "@i":"51812baa",
171+
// "DurationMs":30,
172+
// "SourceContext":"SomeNamespace.SomeService",
173+
// "TransactionId": 12345
174+
// }
175+
```
176+
160177
### Versioning
161178

162179
This package tracks the versioning and target framework support of its [_Microsoft.Extensions.Logging_](https://nuget.org/packages/Microsoft.Extensions.Logging) dependency.

src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerScope.cs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,9 @@ public void Dispose()
4949

5050
public void EnrichAndCreateScopeItem(LogEvent logEvent, ILogEventPropertyFactory propertyFactory, out LogEventPropertyValue? scopeItem)
5151
{
52-
void AddProperty(KeyValuePair<string, object> stateProperty)
52+
void AddProperty(string key, object? value)
5353
{
54-
var key = stateProperty.Key;
5554
var destructureObject = false;
56-
var value = stateProperty.Value;
5755

5856
if (key.StartsWith("@"))
5957
{
@@ -86,7 +84,7 @@ void AddProperty(KeyValuePair<string, object> stateProperty)
8684
if (stateProperty.Key == SerilogLoggerProvider.OriginalFormatPropertyName && stateProperty.Value is string)
8785
scopeItem = new ScalarValue(_state.ToString());
8886
else
89-
AddProperty(stateProperty);
87+
AddProperty(stateProperty.Key, stateProperty.Value);
9088
}
9189
}
9290
else if (_state is IEnumerable<KeyValuePair<string, object>> stateProperties)
@@ -98,9 +96,18 @@ void AddProperty(KeyValuePair<string, object> stateProperty)
9896
if (stateProperty.Key == SerilogLoggerProvider.OriginalFormatPropertyName && stateProperty.Value is string)
9997
scopeItem = new ScalarValue(_state.ToString());
10098
else
101-
AddProperty(stateProperty);
99+
AddProperty(stateProperty.Key, stateProperty.Value);
102100
}
103101
}
102+
else if (_state is ValueTuple<string, object?> tuple)
103+
{
104+
scopeItem = null; // Unless it's `FormattedLogValues`, these are treated as property bags rather than scope items.
105+
106+
if (tuple.Item1 == SerilogLoggerProvider.OriginalFormatPropertyName && tuple.Item2 is string)
107+
scopeItem = new ScalarValue(_state.ToString());
108+
else
109+
AddProperty(tuple.Item1, tuple.Item2);
110+
}
104111
else
105112
{
106113
scopeItem = propertyFactory.CreateProperty(NoName, _state).Value;
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Copyright © Serilog Contributors
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Serilog.Events;
5+
using Serilog.Extensions.Logging.Tests.Support;
6+
7+
using Xunit;
8+
9+
namespace Serilog.Extensions.Logging.Tests;
10+
public class SerilogLoggerScopeTests
11+
{
12+
static (SerilogLoggerProvider, LogEventPropertyFactory, LogEvent) SetUp()
13+
{
14+
var loggerProvider = new SerilogLoggerProvider();
15+
16+
var logEventPropertyFactory = new LogEventPropertyFactory();
17+
18+
var dateTimeOffset = new DateTimeOffset(2000, 1, 1, 0, 0, 0, TimeSpan.Zero);
19+
var messageTemplate = new MessageTemplate(Enumerable.Empty<Parsing.MessageTemplateToken>());
20+
var properties = Enumerable.Empty<LogEventProperty>();
21+
var logEvent = new LogEvent(dateTimeOffset, LogEventLevel.Information, null, messageTemplate, properties);
22+
23+
return (loggerProvider, logEventPropertyFactory, logEvent);
24+
}
25+
26+
[Fact]
27+
public void EnrichWithDictionaryStringObject()
28+
{
29+
const string propertyName = "Foo";
30+
const string expectedValue = "Bar";
31+
32+
var(loggerProvider, logEventPropertyFactory, logEvent) = SetUp();
33+
34+
35+
var state = new Dictionary<string, object?>() { { propertyName, expectedValue } };
36+
37+
var loggerScope = new SerilogLoggerScope(loggerProvider, state);
38+
39+
loggerScope.EnrichAndCreateScopeItem(logEvent, logEventPropertyFactory, out LogEventPropertyValue? scopeItem);
40+
41+
Assert.Contains(propertyName, logEvent.Properties);
42+
43+
var scalarValue = logEvent.Properties[propertyName] as ScalarValue;
44+
Assert.NotNull(scalarValue);
45+
46+
var actualValue = scalarValue.Value as string;
47+
Assert.NotNull(actualValue);
48+
Assert.Equal(expectedValue, actualValue);
49+
}
50+
51+
[Fact]
52+
public void EnrichWithIEnumerableKeyValuePairStringObject()
53+
{
54+
const string propertyName = "Foo";
55+
const string expectedValue = "Bar";
56+
57+
var (loggerProvider, logEventPropertyFactory, logEvent) = SetUp();
58+
59+
60+
var state = new KeyValuePair<string, object?>[] { new KeyValuePair<string, object?>(propertyName, expectedValue) };
61+
62+
var loggerScope = new SerilogLoggerScope(loggerProvider, state);
63+
64+
loggerScope.EnrichAndCreateScopeItem(logEvent, logEventPropertyFactory, out LogEventPropertyValue? scopeItem);
65+
66+
Assert.Contains(propertyName, logEvent.Properties);
67+
68+
var scalarValue = logEvent.Properties[propertyName] as ScalarValue;
69+
Assert.NotNull(scalarValue);
70+
71+
var actualValue = scalarValue.Value as string;
72+
Assert.NotNull(actualValue);
73+
Assert.Equal(expectedValue, actualValue);
74+
}
75+
76+
[Fact]
77+
public void EnrichWithTupleStringObject()
78+
{
79+
const string propertyName = "Foo";
80+
const string expectedValue = "Bar";
81+
82+
var (loggerProvider, logEventPropertyFactory, logEvent) = SetUp();
83+
84+
85+
var state = (propertyName, (object)expectedValue);
86+
87+
var loggerScope = new SerilogLoggerScope(loggerProvider, state);
88+
89+
loggerScope.EnrichAndCreateScopeItem(logEvent, logEventPropertyFactory, out LogEventPropertyValue? scopeItem);
90+
91+
Assert.Contains(propertyName, logEvent.Properties);
92+
93+
var scalarValue = logEvent.Properties[propertyName] as ScalarValue;
94+
Assert.NotNull(scalarValue);
95+
96+
var actualValue = scalarValue.Value as string;
97+
Assert.NotNull(actualValue);
98+
Assert.Equal(expectedValue, actualValue);
99+
}
100+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright © Serilog Contributors
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Serilog.Core;
5+
using Serilog.Events;
6+
7+
namespace Serilog.Extensions.Logging.Tests.Support;
8+
internal class LogEventPropertyFactory : ILogEventPropertyFactory
9+
{
10+
public LogEventProperty CreateProperty(string name, object? value, bool destructureObjects = false)
11+
{
12+
var scalarValue = new ScalarValue(value);
13+
return new LogEventProperty(name, scalarValue);
14+
}
15+
}

0 commit comments

Comments
 (0)