Skip to content

Commit e83f2f3

Browse files
authored
Merge pull request #66 from adamchester/configurable-locking
User-configurable synchronized writes
2 parents 70d892b + dc2c98e commit e83f2f3

File tree

6 files changed

+114
-13
lines changed

6 files changed

+114
-13
lines changed

Build.ps1

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
echo "build: Build started"
1+
Write-Host "build: Build started"
22

33
Push-Location $PSScriptRoot
44

55
if(Test-Path .\artifacts) {
6-
echo "build: Cleaning .\artifacts"
6+
Write-Host "build: Cleaning .\artifacts"
77
Remove-Item .\artifacts -Force -Recurse
88
}
99

@@ -15,13 +15,13 @@ $suffix = @{ $true = ""; $false = "$($branch.Substring(0, [math]::Min(10,$branch
1515
$commitHash = $(git rev-parse --short HEAD)
1616
$buildSuffix = @{ $true = "$($suffix)-$($commitHash)"; $false = "$($branch)-$($commitHash)" }[$suffix -ne ""]
1717

18-
echo "build: Package version suffix is $suffix"
19-
echo "build: Build version suffix is $buildSuffix"
18+
Write-Host "build: Package version suffix is $suffix"
19+
Write-Host "build: Build version suffix is $buildSuffix"
2020

21-
foreach ($src in ls src/*) {
21+
foreach ($src in Get-ChildItem src/*) {
2222
Push-Location $src
2323

24-
echo "build: Packaging project in $src"
24+
Write-Host "build: Packaging project in $src"
2525

2626
& dotnet build -c Release --version-suffix=$buildSuffix
2727
if ($suffix) {
@@ -34,10 +34,21 @@ foreach ($src in ls src/*) {
3434
Pop-Location
3535
}
3636

37+
foreach ($sample in Get-ChildItem sample/*) {
38+
Push-Location $sample
39+
40+
Write-Host "build: Testing project in $sample"
41+
42+
& dotnet build -c Release --version-suffix=$buildSuffix
43+
if($LASTEXITCODE -ne 0) { exit 3 }
44+
45+
Pop-Location
46+
}
47+
3748
foreach ($test in ls test/*.Tests) {
3849
Push-Location $test
3950

40-
echo "build: Testing project in $test"
51+
Write-Host "build: Testing project in $test"
4152

4253
& dotnet test -c Release
4354
if($LASTEXITCODE -ne 0) { exit 3 }

sample/SyncWritesDemo/Program.cs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using Serilog;
2+
using Serilog.Sinks.SystemConsole.Themes;
3+
using System;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
7+
namespace SyncWritesDemo
8+
{
9+
public static class Program
10+
{
11+
public static void Main(string[] args)
12+
{
13+
Console.WriteLine("A sample of how to sync writes to the console sink.");
14+
15+
if (args != null && args.Length == 1)
16+
{
17+
switch (args[0])
18+
{
19+
case "--sync-root-default":
20+
SystemConsoleSyncTest(syncRootForLogger1: null, syncRootForLogger2: null);
21+
return;
22+
case "--sync-root-separate":
23+
SystemConsoleSyncTest(syncRootForLogger1: new object(), syncRootForLogger2: new object());
24+
return;
25+
case "--sync-root-same":
26+
var sameSyncRoot = new object();
27+
SystemConsoleSyncTest(syncRootForLogger1: sameSyncRoot, syncRootForLogger2: sameSyncRoot);
28+
return;
29+
}
30+
}
31+
32+
Console.WriteLine("Expecting one of the following arguments:{0}--sync-root-default{0}--sync-root-separate{0}--sync-root-same", Environment.NewLine);
33+
}
34+
35+
static void SystemConsoleSyncTest(object syncRootForLogger1, object syncRootForLogger2)
36+
{
37+
var logger1 = new LoggerConfiguration()
38+
.MinimumLevel.Verbose()
39+
.Enrich.WithProperty("Logger", "logger1")
40+
.WriteTo.Console(theme: SystemConsoleTheme.Literate, syncRoot: syncRootForLogger1)
41+
.CreateLogger();
42+
43+
var logger2 = new LoggerConfiguration()
44+
.MinimumLevel.Verbose()
45+
.Enrich.WithProperty("Logger", "logger2")
46+
.WriteTo.Console(theme: SystemConsoleTheme.Literate, syncRoot: syncRootForLogger2)
47+
.CreateLogger();
48+
49+
var options = new ParallelOptions { MaxDegreeOfParallelism = 8 };
50+
System.Threading.Tasks.Parallel.For(0, 1000, options, (i, loopState) =>
51+
{
52+
var logger = (i % 2 == 0) ? logger1 : logger2;
53+
logger.Information("Event {Iteration} generated by {ThreadId}", i, Thread.CurrentThread.ManagedThreadId);
54+
});
55+
}
56+
}
57+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>netcoreapp2.0</TargetFramework>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<ProjectReference Include="..\..\src\Serilog.Sinks.Console\Serilog.Sinks.Console.csproj" />
10+
</ItemGroup>
11+
12+
</Project>

serilog-sinks-console.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{CF8176
2727
EndProject
2828
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleDemo", "sample\ConsoleDemo\ConsoleDemo.csproj", "{DBF4907A-63A2-4895-8DEF-59F90C20380B}"
2929
EndProject
30+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SyncWritesDemo", "sample\SyncWritesDemo\SyncWritesDemo.csproj", "{633AE0AD-C9D4-440D-874A-C0F4632DB75F}"
31+
EndProject
3032
Global
3133
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3234
Debug|Any CPU = Debug|Any CPU
@@ -45,6 +47,10 @@ Global
4547
{DBF4907A-63A2-4895-8DEF-59F90C20380B}.Debug|Any CPU.Build.0 = Debug|Any CPU
4648
{DBF4907A-63A2-4895-8DEF-59F90C20380B}.Release|Any CPU.ActiveCfg = Release|Any CPU
4749
{DBF4907A-63A2-4895-8DEF-59F90C20380B}.Release|Any CPU.Build.0 = Release|Any CPU
50+
{633AE0AD-C9D4-440D-874A-C0F4632DB75F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
51+
{633AE0AD-C9D4-440D-874A-C0F4632DB75F}.Debug|Any CPU.Build.0 = Debug|Any CPU
52+
{633AE0AD-C9D4-440D-874A-C0F4632DB75F}.Release|Any CPU.ActiveCfg = Release|Any CPU
53+
{633AE0AD-C9D4-440D-874A-C0F4632DB75F}.Release|Any CPU.Build.0 = Release|Any CPU
4854
EndGlobalSection
4955
GlobalSection(SolutionProperties) = preSolution
5056
HideSolutionNode = FALSE
@@ -53,6 +59,7 @@ Global
5359
{866A028E-27DB-49A0-AC78-E5FEF247C099} = {037440DE-440B-4129-9F7A-09B42D00397E}
5460
{1D56534C-4009-42C2-A573-789CAE6B8AA9} = {7D0692CD-F95D-4BF9-8C63-B4A1C078DF23}
5561
{DBF4907A-63A2-4895-8DEF-59F90C20380B} = {CF817664-4CEC-4B6A-9C57-A0D687757D82}
62+
{633AE0AD-C9D4-440D-874A-C0F4632DB75F} = {CF817664-4CEC-4B6A-9C57-A0D687757D82}
5663
EndGlobalSection
5764
GlobalSection(ExtensibilityGlobals) = postSolution
5865
SolutionGuid = {43C32ED4-D39A-4E27-AE99-7BB8C883833C}

src/Serilog.Sinks.Console/ConsoleLoggerConfigurationExtensions.cs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ namespace Serilog
2828
/// </summary>
2929
public static class ConsoleLoggerConfigurationExtensions
3030
{
31+
static object DefaultSyncRoot = new object();
3132
const string DefaultConsoleOutputTemplate = "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}";
3233

3334
/// <summary>
@@ -38,6 +39,9 @@ public static class ConsoleLoggerConfigurationExtensions
3839
/// events passed through the sink. Ignored when <paramref name="levelSwitch"/> is specified.</param>
3940
/// <param name="outputTemplate">A message template describing the format used to write to the sink.
4041
/// The default is <code>"[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"</code>.</param>
42+
/// <param name="syncRoot">An object that will be used to `lock` (sync) access to the console output. If you specify this, you
43+
/// will have the ability to lock on this object, and guarantee that the console sink will not be about to output anything while
44+
/// the lock is held.</param>
4145
/// <param name="formatProvider">Supplies culture-specific formatting information, or null.</param>
4246
/// <param name="levelSwitch">A switch allowing the pass-through minimum level
4347
/// to be changed at runtime.</param>
@@ -52,7 +56,8 @@ public static LoggerConfiguration Console(
5256
IFormatProvider formatProvider = null,
5357
LoggingLevelSwitch levelSwitch = null,
5458
LogEventLevel? standardErrorFromLevel = null,
55-
ConsoleTheme theme = null)
59+
ConsoleTheme theme = null,
60+
object syncRoot = null)
5661
{
5762
if (sinkConfiguration == null) throw new ArgumentNullException(nameof(sinkConfiguration));
5863
if (outputTemplate == null) throw new ArgumentNullException(nameof(outputTemplate));
@@ -61,8 +66,10 @@ public static LoggerConfiguration Console(
6166
ConsoleTheme.None :
6267
theme ?? SystemConsoleThemes.Literate;
6368

69+
syncRoot = syncRoot ?? DefaultSyncRoot;
70+
6471
var formatter = new OutputTemplateRenderer(appliedTheme, outputTemplate, formatProvider);
65-
return sinkConfiguration.Sink(new ConsoleSink(appliedTheme, formatter, standardErrorFromLevel), restrictedToMinimumLevel, levelSwitch);
72+
return sinkConfiguration.Sink(new ConsoleSink(appliedTheme, formatter, standardErrorFromLevel, syncRoot), restrictedToMinimumLevel, levelSwitch);
6673
}
6774

6875
/// <summary>
@@ -71,6 +78,9 @@ public static LoggerConfiguration Console(
7178
/// <param name="sinkConfiguration">Logger sink configuration.</param>
7279
/// <param name="formatter">Controls the rendering of log events into text, for example to log JSON. To
7380
/// control plain text formatting, use the overload that accepts an output template.</param>
81+
/// <param name="syncRoot">An object that will be used to `lock` (sync) access to the console output. If you specify this, you
82+
/// will have the ability to lock on this object, and guarantee that the console sink will not be about to output anything while
83+
/// the lock is held.</param>
7484
/// <param name="restrictedToMinimumLevel">The minimum level for
7585
/// events passed through the sink. Ignored when <paramref name="levelSwitch"/> is specified.</param>
7686
/// <param name="levelSwitch">A switch allowing the pass-through minimum level
@@ -82,12 +92,14 @@ public static LoggerConfiguration Console(
8292
ITextFormatter formatter,
8393
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
8494
LoggingLevelSwitch levelSwitch = null,
85-
LogEventLevel? standardErrorFromLevel = null)
95+
LogEventLevel? standardErrorFromLevel = null,
96+
object syncRoot = null)
8697
{
8798
if (sinkConfiguration == null) throw new ArgumentNullException(nameof(sinkConfiguration));
8899
if (formatter == null) throw new ArgumentNullException(nameof(formatter));
89100

90-
return sinkConfiguration.Sink(new ConsoleSink(ConsoleTheme.None, formatter, standardErrorFromLevel), restrictedToMinimumLevel, levelSwitch);
101+
syncRoot = syncRoot ?? DefaultSyncRoot;
102+
return sinkConfiguration.Sink(new ConsoleSink(ConsoleTheme.None, formatter, standardErrorFromLevel, syncRoot), restrictedToMinimumLevel, levelSwitch);
91103
}
92104
}
93105
}

src/Serilog.Sinks.Console/Sinks/SystemConsole/ConsoleSink.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class ConsoleSink : ILogEventSink
2828
readonly LogEventLevel? _standardErrorFromLevel;
2929
readonly ConsoleTheme _theme;
3030
readonly ITextFormatter _formatter;
31-
static readonly object _syncRoot = new object();
31+
readonly object _syncRoot;
3232

3333
const int DefaultWriteBufferCapacity = 256;
3434

@@ -40,11 +40,13 @@ static ConsoleSink()
4040
public ConsoleSink(
4141
ConsoleTheme theme,
4242
ITextFormatter formatter,
43-
LogEventLevel? standardErrorFromLevel)
43+
LogEventLevel? standardErrorFromLevel,
44+
object syncRoot)
4445
{
4546
_standardErrorFromLevel = standardErrorFromLevel;
4647
_theme = theme ?? throw new ArgumentNullException(nameof(theme));
4748
_formatter = formatter;
49+
_syncRoot = syncRoot ?? throw new ArgumentNullException(nameof(syncRoot));
4850
}
4951

5052
public void Emit(LogEvent logEvent)

0 commit comments

Comments
 (0)