Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 5e1527d

Browse files
Merge pull request #260 from AndreReise/improve-event-id-cache
Size-based `EventId` `LogEventProperty` cache
2 parents 7fc50b0 + 2bc0ea5 commit 5e1527d

File tree

5 files changed

+225
-43
lines changed

5 files changed

+225
-43
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Copyright (c) Serilog Contributors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
namespace Serilog.Extensions.Logging;
16+
17+
using System.Collections.Concurrent;
18+
using Microsoft.Extensions.Logging;
19+
using Serilog.Events;
20+
21+
class EventIdPropertyCache
22+
{
23+
readonly int _maxCachedProperties;
24+
readonly ConcurrentDictionary<EventKey, LogEventProperty> _propertyCache = new();
25+
26+
int _count;
27+
28+
public EventIdPropertyCache(int maxCachedProperties = 1024)
29+
{
30+
_maxCachedProperties = maxCachedProperties;
31+
}
32+
33+
public LogEventProperty GetOrCreateProperty(in EventId eventId)
34+
{
35+
var eventKey = new EventKey(eventId);
36+
37+
LogEventProperty? property;
38+
39+
if (_count >= _maxCachedProperties)
40+
{
41+
if (!_propertyCache.TryGetValue(eventKey, out property))
42+
{
43+
property = CreateProperty(in eventKey);
44+
}
45+
}
46+
else
47+
{
48+
if (!_propertyCache.TryGetValue(eventKey, out property))
49+
{
50+
// GetOrAdd is moved to a separate method to prevent closure allocation
51+
property = GetOrAddCore(in eventKey);
52+
}
53+
}
54+
55+
return property;
56+
}
57+
58+
static LogEventProperty CreateProperty(in EventKey eventKey)
59+
{
60+
var properties = new List<LogEventProperty>(2);
61+
62+
if (eventKey.Id != 0)
63+
{
64+
properties.Add(new LogEventProperty("Id", new ScalarValue(eventKey.Id)));
65+
}
66+
67+
if (eventKey.Name != null)
68+
{
69+
properties.Add(new LogEventProperty("Name", new ScalarValue(eventKey.Name)));
70+
}
71+
72+
return new LogEventProperty("EventId", new StructureValue(properties));
73+
}
74+
75+
LogEventProperty GetOrAddCore(in EventKey eventKey) =>
76+
_propertyCache.GetOrAdd(
77+
eventKey,
78+
key =>
79+
{
80+
Interlocked.Increment(ref _count);
81+
82+
return CreateProperty(in key);
83+
});
84+
85+
readonly record struct EventKey
86+
{
87+
public EventKey(EventId eventId)
88+
{
89+
Id = eventId.Id;
90+
Name = eventId.Name;
91+
}
92+
93+
public int Id { get; }
94+
95+
public string? Name { get; }
96+
}
97+
}

‎src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLogger.cs

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,10 @@ internal static string GetKeyWithoutFirstSymbol(ConcurrentDictionary<string, str
2828

2929
readonly SerilogLoggerProvider _provider;
3030
readonly ILogger _logger;
31+
readonly EventIdPropertyCache _eventIdPropertyCache = new();
3132

3233
static readonly CachingMessageTemplateParser MessageTemplateParser = new();
3334

34-
// It's rare to see large event ids, as they are category-specific
35-
static readonly LogEventProperty[] LowEventIdValues = Enumerable.Range(0, 48)
36-
.Select(n => new LogEventProperty("Id", new ScalarValue(n)))
37-
.ToArray();
38-
3935
public SerilogLogger(
4036
SerilogLoggerProvider provider,
4137
ILogger? logger = null,
@@ -155,7 +151,7 @@ LogEvent PrepareWrite<TState>(LogEventLevel level, EventId eventId, TState state
155151
}
156152

157153
if (eventId.Id != 0 || eventId.Name != null)
158-
properties.Add(CreateEventIdProperty(eventId));
154+
properties.Add(_eventIdPropertyCache.GetOrCreateProperty(ineventId));
159155

160156
var (traceId, spanId) = Activity.Current is { } activity ?
161157
(activity.TraceId, activity.SpanId) :
@@ -172,25 +168,4 @@ LogEvent PrepareWrite<TState>(LogEventLevel level, EventId eventId, TState state
172168
stateObj = formatter(state, null);
173169
return stateObj ?? state;
174170
}
175-
176-
internal static LogEventProperty CreateEventIdProperty(EventId eventId)
177-
{
178-
var properties = new List<LogEventProperty>(2);
179-
180-
if (eventId.Id != 0)
181-
{
182-
if (eventId.Id >= 0 && eventId.Id < LowEventIdValues.Length)
183-
// Avoid some allocations
184-
properties.Add(LowEventIdValues[eventId.Id]);
185-
else
186-
properties.Add(new LogEventProperty("Id", new ScalarValue(eventId.Id)));
187-
}
188-
189-
if (eventId.Name != null)
190-
{
191-
properties.Add(new LogEventProperty("Name", new ScalarValue(eventId.Name)));
192-
}
193-
194-
return new LogEventProperty("EventId", new StructureValue(properties));
195-
}
196171
}

‎test/Serilog.Extensions.Logging.Benchmarks/LogEventBenchmark.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ private class Person
3434

3535
readonly IMelLogger _melLogger;
3636
readonly Person _bob, _alice;
37+
readonly EventId _eventId = new EventId(1, "Test");
3738

3839
public LogEventBenchmark()
3940
{
@@ -61,5 +62,16 @@ public void LogInformationScoped()
6162
using (var scope = _melLogger.BeginScope("Hi {@User} from {$Me}", _bob, _alice))
6263
_melLogger.LogInformation("Hi");
6364
}
65+
66+
[Benchmark]
67+
public void LogInformation_WithEventId()
68+
{
69+
this._melLogger.Log(
70+
LogLevel.Information,
71+
_eventId,
72+
"Hi {@User} from {$Me}",
73+
_bob,
74+
_alice);
75+
}
6476
}
6577
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright (c) Serilog Contributors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using Microsoft.Extensions.Logging;
16+
using Serilog.Events;
17+
using Xunit;
18+
19+
namespace Serilog.Extensions.Logging.Tests;
20+
21+
public class EventIdPropertyCacheTests
22+
{
23+
[Fact]
24+
public void CreatesPropertyWithCorrectIdAndName()
25+
{
26+
// Arrange
27+
const int id = 101;
28+
const string name = "TestEvent";
29+
var eventId = new EventId(id, name);
30+
31+
var cache = new EventIdPropertyCache();
32+
33+
// Act
34+
var eventProperty = cache.GetOrCreateProperty(eventId);
35+
36+
// Assert
37+
var value = Assert.IsType<StructureValue>(eventProperty.Value);
38+
39+
Assert.Equal(2, value.Properties.Count);
40+
41+
var idValue = value.Properties.Single(property => property.Name == "Id").Value;
42+
var nameValue = value.Properties.Single(property => property.Name == "Name").Value;
43+
44+
var scalarId = Assert.IsType<ScalarValue>(idValue);
45+
var scalarName = Assert.IsType<ScalarValue>(nameValue);
46+
47+
Assert.Equal(id, scalarId.Value);
48+
Assert.Equal(name, scalarName.Value);
49+
}
50+
51+
[Fact]
52+
public void EventsWithDSameKeysHaveSameReferences()
53+
{
54+
// Arrange
55+
var cache = new EventIdPropertyCache();
56+
57+
// Act
58+
var property1 = cache.GetOrCreateProperty(new EventId(1, "Name1"));
59+
var property2 = cache.GetOrCreateProperty(new EventId(1, "Name1"));
60+
61+
// Assert
62+
Assert.Same(property1, property2);
63+
}
64+
65+
[Theory]
66+
[InlineData(1, "SomeName", 1, "AnotherName")]
67+
[InlineData(1, "SomeName", 2, "SomeName")]
68+
[InlineData(1, "SomeName", 2, "AnotherName")]
69+
public void EventsWithDifferentKeysHaveDifferentReferences(int firstId, string firstName, int secondId, string secondName)
70+
{
71+
// Arrange
72+
var cache = new EventIdPropertyCache();
73+
74+
// Act
75+
var property1 = cache.GetOrCreateProperty(new EventId(firstId, firstName));
76+
var property2 = cache.GetOrCreateProperty(new EventId(secondId, secondName));
77+
78+
// Assert
79+
Assert.NotSame(property1, property2);
80+
}
81+
82+
83+
[Fact]
84+
public void WhenLimitIsNotOverSameEventsHaveSameReferences()
85+
{
86+
// Arrange
87+
var eventId = new EventId(101, "test");
88+
var cache = new EventIdPropertyCache();
89+
90+
// Act
91+
var property1 = cache.GetOrCreateProperty(eventId);
92+
var property2 = cache.GetOrCreateProperty(eventId);
93+
94+
// Assert
95+
Assert.Same(property1, property2);
96+
}
97+
98+
[Fact]
99+
public void WhenLimitIsOverSameEventsHaveDifferentReferences()
100+
{
101+
// Arrange
102+
var cache = new EventIdPropertyCache(maxCachedProperties: 1);
103+
cache.GetOrCreateProperty(new EventId(1, "InitialEvent"));
104+
105+
var eventId = new EventId(101, "DifferentEvent");
106+
107+
// Act
108+
var property1 = cache.GetOrCreateProperty(eventId);
109+
var property2 = cache.GetOrCreateProperty(eventId);
110+
111+
// Assert
112+
Assert.NotSame(property1, property2);
113+
}
114+
}

‎test/Serilog.Extensions.Logging.Tests/SerilogLoggerTests.cs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -517,22 +517,6 @@ public void Dispose()
517517
}
518518
}
519519

520-
[Theory]
521-
[InlineData(1)]
522-
[InlineData(10)]
523-
[InlineData(48)]
524-
[InlineData(100)]
525-
public void LowAndHighNumberedEventIdsAreMapped(int id)
526-
{
527-
var orig = new EventId(id, "test");
528-
var mapped = SerilogLogger.CreateEventIdProperty(orig);
529-
var value = Assert.IsType<StructureValue>(mapped.Value);
530-
Assert.Equal(2, value.Properties.Count);
531-
var idValue = value.Properties.Single(p => p.Name == "Id").Value;
532-
var scalar = Assert.IsType<ScalarValue>(idValue);
533-
Assert.Equal(id, scalar.Value);
534-
}
535-
536520
[Fact]
537521
public void MismatchedMessageTemplateParameterCountIsHandled()
538522
{

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /