Skip to content

Commit 1448b7f

Browse files
committed
Exposed parameter to pass collection instead of forcing reliance on singleton collection. Added unit tests. Updated documentation.
1 parent 0613a16 commit 1448b7f

File tree

7 files changed

+179
-11
lines changed

7 files changed

+179
-11
lines changed

README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
A Serilog sink that exposes a simple interface to retrieve and change LoggingLevelSwitches at runtime.
99

10-
## Using
10+
## Installing via NuGet
1111

1212
To get started install the *Serilog.Sinks.DynamicSwitch* package:
1313

@@ -18,10 +18,14 @@ PM> Install-Package Serilog.Sinks.DynamicSwitch
1818
or
1919

2020
```bash
21-
$ dotnet add package Serilog.Sinks.DynamicSwitch
21+
dotnet add package Serilog.Sinks.DynamicSwitch
2222
```
2323

24-
To start using the DynamicSwitch, the sink can be setup as follows:
24+
## Using
25+
26+
### Setup sinks
27+
28+
A DynamicSwitch sink can be set up in the standard way while constructing a `LoggerConfiguration`. A specific `LoggingLevelSwitchCollection` object can be passed to the configuration; if one is not specified, a static global collection is used that be referenced using `LoggingLevelSwitchCollection.Current`. Each DynamicSwitch must be uniquely named. The following is a simple example:
2529

2630
```csharp
2731
var logLevel = new LoggingLevelSwitch(LogEventLevel.Warning);
@@ -32,7 +36,7 @@ var log = new LoggerConfiguration()
3236
.CreateLogger();
3337
```
3438

35-
If using `appsettings.json` for configuration, the following example shows how to configure DynamicSwitch sinks:
39+
If using `appsettings.json` for configuration, the following example shows how to configure DynamicSwitch sinks along with attached level switches:
3640

3741
```javascript
3842
{
@@ -82,7 +86,9 @@ If using `appsettings.json` for configuration, the following example shows how t
8286
}
8387
```
8488

85-
Next, add the level switch collection to your services collection:
89+
#### Using Dependency Injection
90+
91+
To support the retrieval of switches through dependency injection, there is a simple extention method to add the collection to the services container. If a collection is not passed, the global `LoggingLevelSwitchCollection.Current` will be added to the container:
8692

8793
```csharp
8894
.ConfigureServices((hostContext, services) =>

src/Serilog.Sinks.DynamicSwitch/DynamicSwitchSinkExtensions.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,20 @@ public static LoggerConfiguration DynamicSwitch(
1414
string switchName,
1515
LoggingLevelSwitch levelSwitch,
1616
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum)
17+
{
18+
return DynamicSwitch(configuration, null, switchName, levelSwitch, restrictedToMinimumLevel);
19+
}
20+
21+
public static LoggerConfiguration DynamicSwitch(
22+
this LoggerSinkConfiguration configuration,
23+
LoggingLevelSwitchCollection collection,
24+
string switchName,
25+
LoggingLevelSwitch levelSwitch,
26+
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum)
1727
{
1828
configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
1929
levelSwitch = levelSwitch ?? throw new ArgumentNullException(nameof(levelSwitch));
20-
var collection = LoggingLevelSwitchCollection.Current;
30+
collection ??= LoggingLevelSwitchCollection.Current;
2131

2232
if (!collection.TryAdd(switchName, levelSwitch))
2333
{

src/Serilog.Sinks.DynamicSwitch/Serilog.Sinks.DynamicSwitch.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<TargetFramework>netstandard2.0</TargetFramework>
55
<LangVersion>latest</LangVersion>
66
<NoWarn>$(NoWarn);1591</NoWarn>
7-
<Version>0.1.0</Version>
7+
<Version>1.0.0</Version>
88
<Authors>Gowon Patterson</Authors>
99
<Description>A Serilog sink that exposes a simple interface to retrieve and change LoggingLevelSwitches at runtime.</Description>
1010
<Copyright>© Gowon Patterson. All rights reserved.</Copyright>
Lines changed: 94 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,111 @@
11
namespace Serilog.Sinks.DynamicSwitch.Tests
22
{
3+
using System;
4+
using Core;
35
using Events;
6+
using Microsoft.Extensions.DependencyInjection;
7+
using Support;
48
using Xunit;
59
using Xunit.Categories;
10+
using static Support.LogEventHelper;
611

712
public class DynamicSwitchSinkTests
813
{
9-
private const string SwitchName = "switch";
10-
private const LogEventLevel LogLevel = LogEventLevel.Fatal;
14+
[UnitTest]
15+
[Fact]
16+
public void AddMultipleDynamicSwitches()
17+
{
18+
const string switchName1 = "switch1";
19+
const string switchName2 = "switch2";
20+
var collection = new LoggingLevelSwitchCollection();
21+
var logSwitch = new LoggingLevelSwitch(LogEventLevel.Error);
22+
var logSwitch2 = new LoggingLevelSwitch(LogEventLevel.Debug);
23+
24+
var log = new LoggerConfiguration()
25+
.WriteTo.DynamicSwitch(collection, switchName1, logSwitch)
26+
.WriteTo.DynamicSwitch(collection, switchName2, logSwitch2)
27+
.CreateLogger();
28+
29+
Assert.Equal(2, collection.Count);
30+
Assert.Equal(LogEventLevel.Error, collection[switchName1].MinimumLevel);
31+
Assert.Equal(LogEventLevel.Debug, collection[switchName2].MinimumLevel);
32+
}
33+
34+
[UnitTest]
35+
[Fact]
36+
public void AddMultipleDynamicSwitches_ThrowOnDuplicateNameKey()
37+
{
38+
const string switchName = "switch";
39+
var collection = new LoggingLevelSwitchCollection();
40+
var logSwitch = new LoggingLevelSwitch(LogEventLevel.Error);
41+
var logSwitch2 = new LoggingLevelSwitch(LogEventLevel.Debug);
42+
43+
Assert.Throws<ArgumentException>(() =>
44+
{
45+
new LoggerConfiguration()
46+
.WriteTo.DynamicSwitch(collection, switchName, logSwitch)
47+
.WriteTo.DynamicSwitch(collection, switchName, logSwitch2)
48+
.CreateLogger();
49+
});
50+
}
1151

1252
[UnitTest]
1353
[Fact]
1454
public void ConstructDynamicSwitchSink()
1555
{
16-
var sink = new DynamicSwitchSink(SwitchName);
56+
const string switchName = "switch";
57+
var sink = new DynamicSwitchSink(switchName);
58+
Assert.Equal(sink.Name, switchName);
59+
}
60+
61+
[UnitTest]
62+
[Fact]
63+
public void UseSingletonCollectionDependencyInjection()
64+
{
65+
const string switchName = "switch";
66+
var logSwitch = new LoggingLevelSwitch(LogEventLevel.Verbose);
67+
68+
var log = new LoggerConfiguration()
69+
.WriteTo.DynamicSwitch(switchName, logSwitch)
70+
.CreateLogger();
71+
72+
var provider = new ServiceCollection()
73+
.AddLoggingLevelSwitchCollection()
74+
.BuildServiceProvider();
75+
76+
var collection = provider.GetRequiredService<LoggingLevelSwitchCollection>();
77+
78+
Assert.Equal(LoggingLevelSwitchCollection.Current, collection);
79+
Assert.Single(collection);
80+
Assert.Equal(LogEventLevel.Verbose, collection[switchName].MinimumLevel);
81+
}
82+
83+
[UnitTest]
84+
[Fact]
85+
public void UpdateSwitchFromCollection_ChangeEmitBehavior()
86+
{
87+
const string switchName = "switch";
88+
var collection = new LoggingLevelSwitchCollection();
89+
var logSwitch = new LoggingLevelSwitch(LogEventLevel.Error);
90+
var a = LogEvent(LogEventLevel.Error, null, "Hello, {Name}!", "Alice");
91+
var b = LogEvent(LogEventLevel.Information, null, "Hello, {Name}!", "Bob");
92+
var c = LogEvent(LogEventLevel.Warning, null, "Hello, {Name}!", "Charlie");
93+
94+
var log = new LoggerConfiguration()
95+
.WriteTo.Sink(new DummyWithLevelSwitchSink(logSwitch), levelSwitch: logSwitch)
96+
.WriteTo.DynamicSwitch(collection, switchName, logSwitch)
97+
.CreateLogger();
98+
99+
log.Write(a);
100+
Assert.Single(DummyWithLevelSwitchSink.Emitted);
101+
102+
log.Write(b);
103+
Assert.Single(DummyWithLevelSwitchSink.Emitted);
104+
105+
collection[switchName].MinimumLevel = LogEventLevel.Debug;
17106

18-
Assert.Equal(sink.Name, SwitchName);
107+
log.Write(c);
108+
Assert.Equal(2, DummyWithLevelSwitchSink.Emitted.Count);
19109
}
20110
}
21111
}

test/Serilog.Sinks.DynamicSwitch.Tests/Serilog.Sinks.DynamicSwitch.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.4" />
1011
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
1112
<PackageReference Include="xunit" Version="2.4.1" />
1213
<PackageReference Include="xunit.categories" Version="2.0.4" />
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
namespace Serilog.Sinks.DynamicSwitch.Tests.Support
2+
{
3+
using System;
4+
using System.Collections.Generic;
5+
using Core;
6+
using Events;
7+
8+
public class DummyWithLevelSwitchSink : ILogEventSink
9+
{
10+
[ThreadStatic]
11+
public static LoggingLevelSwitch ControlLevelSwitch;
12+
13+
[ThreadStatic]
14+
// ReSharper disable ThreadStaticFieldHasInitializer
15+
public static List<LogEvent> Emitted = new List<LogEvent>();
16+
// ReSharper restore ThreadStaticFieldHasInitializer
17+
18+
public DummyWithLevelSwitchSink(LoggingLevelSwitch loggingControlLevelSwitch)
19+
{
20+
ControlLevelSwitch = loggingControlLevelSwitch;
21+
}
22+
23+
public void Emit(LogEvent logEvent)
24+
{
25+
Emitted.Add(logEvent);
26+
}
27+
}
28+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
namespace Serilog.Sinks.DynamicSwitch.Tests.Support
2+
{
3+
using System;
4+
using System.Diagnostics.CodeAnalysis;
5+
using Events;
6+
using Xunit.Sdk;
7+
8+
[ExcludeFromCodeCoverage]
9+
public static class LogEventHelper
10+
{
11+
public static LogEvent LogEvent(string messageTemplate, params object[] propertyValues)
12+
{
13+
return LogEvent(null, messageTemplate, propertyValues);
14+
}
15+
16+
public static LogEvent LogEvent(Exception exception, string messageTemplate, params object[] propertyValues)
17+
{
18+
return LogEvent(LogEventLevel.Information, exception, messageTemplate, propertyValues);
19+
}
20+
21+
public static LogEvent LogEvent(LogEventLevel level, Exception exception, string messageTemplate,
22+
params object[] propertyValues)
23+
{
24+
var log = new LoggerConfiguration().CreateLogger();
25+
if (!log.BindMessageTemplate(messageTemplate, propertyValues, out var template, out var properties))
26+
{
27+
throw new XunitException("Template could not be bound.");
28+
}
29+
30+
return new LogEvent(DateTimeOffset.Now, level, exception, template, properties);
31+
}
32+
}
33+
}

0 commit comments

Comments
 (0)