Skip to content
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

Add new mechanism to enable creation of DiagnosticContext scopes #49

Closed
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions samples/ContextAwareServiceSample/ContextAwareServiceSample.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk.Worker">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Serilog.Extensions.Hosting\Serilog.Extensions.Hosting.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.4" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.Seq" Version="5.0.1" />
</ItemGroup>
</Project>
45 changes: 45 additions & 0 deletions samples/ContextAwareServiceSample/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;

namespace ContextAwareServiceSample
{
public static class Program
{
public static int Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateBootstrapLogger();

try {
Log.Information("Getting the motors running...");
CreateHostBuilder(args).Build().Run();
return 0;
}
catch (Exception ex) {
Log.Fatal(ex, "Host terminated unexpectedly");
return 1;
}
finally {
Log.CloseAndFlush();
}
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
services.AddHostedService<Worker>()
.AddSingleton<WorkExecutor>())
.UseSerilog((context, services, loggerConfiguration) => loggerConfiguration
.ReadFrom.Configuration(context.Configuration)
.WriteTo.Seq("http://localhost:5341")
.Enrich.FromLogContext()
.WriteTo.Console());
}
}
11 changes: 11 additions & 0 deletions samples/ContextAwareServiceSample/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"profiles": {
"ContextAwareServiceSample": {
"commandName": "Project",
"dotnetRunMessages": "true",
"environmentVariables": {
"DOTNET_ENVIRONMENT": "Development"
}
}
}
}
73 changes: 73 additions & 0 deletions samples/ContextAwareServiceSample/Worker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog;
using ILogger = Serilog.ILogger;

namespace ContextAwareServiceSample
{
public class Worker : BackgroundService
{
readonly WorkExecutor _executor;
readonly IDiagnosticContext _diagnosticContext;

public Worker(WorkExecutor executor, IDiagnosticContext diagnosticContext)
{
_executor = executor;
_diagnosticContext = diagnosticContext;
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested) {
var outerOpId = Guid.NewGuid();
using (_diagnosticContext.Begin(
"Worker executed operation {OperationId} at time {Time}",
outerOpId, DateTimeOffset.Now)) {
await _executor.DoWork(outerOpId);

var innerOpId = Guid.NewGuid();
using (_diagnosticContext.Begin(
"Worker executed inner operation {OperationId} at time {Time}",
innerOpId,
DateTimeOffset.Now)) {
await _executor.DoWork(innerOpId);
}
}

await Task.Delay(1000, stoppingToken);
}
}
}

public class WorkExecutor
{
readonly IDiagnosticContext _diagnosticContext;

public WorkExecutor(IDiagnosticContext diagnosticContext)
{
_diagnosticContext = diagnosticContext;
}

public async Task DoWork(Guid operationId)
{
var R = new Random();

var operationNames = new[] { "SaveUser", "LoadUser", "AddRole", "RemoveRole", "DeactivateUser" };

var randomOperation = operationNames[R.Next(0, operationNames.Length - 1)];

_diagnosticContext.Set("OperationName", randomOperation);
_diagnosticContext.Set("ThisOperationId", operationId);

await Task.Delay(R.Next(100, 1000));

Log.Information("Completed operation {OperationId}: {OperationName}", operationId,
randomOperation);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
9 changes: 9 additions & 0 deletions samples/ContextAwareServiceSample/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
7 changes: 7 additions & 0 deletions serilog-extensions-hosting.sln
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleServiceSample", "samp
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApplicationSample", "samples\WebApplicationSample\WebApplicationSample.csproj", "{1ACDCA67-F404-45AB-9348-98E55E03CB8C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ContextAwareServiceSample", "samples\ContextAwareServiceSample\ContextAwareServiceSample.csproj", "{41535D34-1DC8-4BC9-A085-208AF948ED00}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -48,6 +50,10 @@ Global
{1ACDCA67-F404-45AB-9348-98E55E03CB8C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1ACDCA67-F404-45AB-9348-98E55E03CB8C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1ACDCA67-F404-45AB-9348-98E55E03CB8C}.Release|Any CPU.Build.0 = Release|Any CPU
{41535D34-1DC8-4BC9-A085-208AF948ED00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{41535D34-1DC8-4BC9-A085-208AF948ED00}.Debug|Any CPU.Build.0 = Debug|Any CPU
{41535D34-1DC8-4BC9-A085-208AF948ED00}.Release|Any CPU.ActiveCfg = Release|Any CPU
{41535D34-1DC8-4BC9-A085-208AF948ED00}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -57,6 +63,7 @@ Global
{AD51759B-CD58-473F-9620-0B0E56A123A1} = {E30F638E-BBBE-4AD1-93CE-48CC69CFEFE1}
{E5A82756-4619-4E6B-8B26-6D83E00E99F0} = {F2407211-6043-439C-8E06-3641634332E7}
{1ACDCA67-F404-45AB-9348-98E55E03CB8C} = {F2407211-6043-439C-8E06-3641634332E7}
{41535D34-1DC8-4BC9-A085-208AF948ED00} = {F2407211-6043-439C-8E06-3641634332E7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {811E61C5-3871-4633-AFAE-B35B619C8A10}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

using System;
using System.Threading;
using Serilog.Events;

namespace Serilog.Extensions.Hosting
{
Expand Down Expand Up @@ -56,5 +57,16 @@ public void Set(string propertyName, object value, bool destructureObjects = fal
collector.AddOrUpdate(property);
}
}

/// <summary>
///
/// </summary>
/// <param name="messageTemplate"></param>
/// <param name="properties"></param>
/// <returns></returns>
public DiagnosticContextScope Begin(string messageTemplate, params object[] properties)
{
return new DiagnosticContextScope(this, LogEventLevel.Information, messageTemplate, properties);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System;
using System.Linq;
using Serilog.Events;
using Serilog.Extensions.Hosting;
using Serilog.Parsing;

namespace Serilog.Extensions
{
/// <summary>
///
/// </summary>
public sealed class DiagnosticContextScope : IDisposable
{
readonly LogEventLevel _level;
readonly string _messageTemplate;
readonly object[] _properties;
readonly DiagnosticContextCollector _collector;

static readonly LogEventProperty[] NoProperties = new LogEventProperty[0];

/// <summary>
///
/// </summary>
/// <param name="diagnosticContext"></param>
/// <param name="level"></param>
/// <param name="messageTemplate"></param>
/// <param name="properties"></param>
public DiagnosticContextScope(DiagnosticContext diagnosticContext,
LogEventLevel level,
string messageTemplate,
object[] properties)
{
_level = level;
_messageTemplate = messageTemplate;
_properties = properties;

_collector = diagnosticContext.BeginCollection();
}

/// <summary>
///
/// </summary>
public void Dispose()
{
var logger = Log.ForContext<DiagnosticContextScope>();

if (!_collector.TryComplete(out var collectedProperties))
collectedProperties = NoProperties;

foreach(var collectedProp in collectedProperties) {
gkinsman marked this conversation as resolved.
Show resolved Hide resolved
logger = logger.ForContext(collectedProp.Name, collectedProp.Value);
}

logger.Write(_level, (Exception)null, _messageTemplate, _properties);
}
}
}
11 changes: 10 additions & 1 deletion src/Serilog.Extensions.Hosting/IDiagnosticContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using Serilog.Events;
using Serilog.Extensions;

namespace Serilog
{
/// <summary>
Expand All @@ -27,6 +30,12 @@ public interface IDiagnosticContext
/// <param name="value">The property value.</param>
/// <param name="destructureObjects">If true, the value will be serialized as structured
/// data if possible; if false, the object will be recorded as a scalar or simple array.</param>
void Set(string propertyName, object value, bool destructureObjects = false);
void Set(string propertyName, object value, bool destructureObjects = false);

/// <summary>
///
/// </summary>
/// <returns></returns>
DiagnosticContextScope Begin(string messageTemplate, params object[] properties);
}
}