Skip to content

Commit

Permalink
Allow to configure log scope key (#68)
Browse files Browse the repository at this point in the history
* Make logging scope key configurable
  • Loading branch information
laurynasr authored Oct 6, 2023
1 parent b6b79d5 commit 1c0ca25
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 18 deletions.
2 changes: 1 addition & 1 deletion src/Correlate.AspNetCore/AspNetCore/CorrelateOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Correlate.AspNetCore;
/// <summary>
/// Options for handling correlation id on incoming requests.
/// </summary>
public sealed class CorrelateOptions
public sealed class CorrelateOptions: CorrelationManagerOptions
{
private static readonly string[] DefaultRequestHeaders = { CorrelationHttpHeaders.CorrelationId };

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Correlate.AspNetCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

namespace Correlate.DependencyInjection;

Expand All @@ -16,8 +17,18 @@ public static class ServiceCollectionExtensions
/// <returns>The <see cref="IServiceCollection" /> so that additional calls can be chained.</returns>
public static IServiceCollection AddCorrelate(this IServiceCollection services, Action<CorrelateOptions> configureOptions)
{
return services
.Configure(configureOptions)
services
.Configure(configureOptions);

services.AddOptions<CorrelationManagerOptions>()
.Configure((CorrelationManagerOptions cmo, IOptions<CorrelateOptions> co) =>
{
cmo.LoggingScopeKey = co.Value.LoggingScopeKey;
});

services
.AddCorrelate();

return services;
}
}
26 changes: 25 additions & 1 deletion src/Correlate.Core/CorrelationManager.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Diagnostics;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Correlate;

Expand All @@ -13,6 +14,7 @@ public class CorrelationManager : IAsyncCorrelationManager, ICorrelationManager,
private readonly ICorrelationIdFactory _correlationIdFactory;
private readonly DiagnosticListener? _diagnosticListener;
private readonly ILogger _logger;
private readonly CorrelationManagerOptions _options = new CorrelationManagerOptions();

/// <summary>
/// Initializes a new instance of the <see cref="CorrelationManager" /> class.
Expand Down Expand Up @@ -55,13 +57,35 @@ DiagnosticListener diagnosticListener
_diagnosticListener = diagnosticListener ?? throw new ArgumentNullException(nameof(diagnosticListener));
}

/// <summary>
/// Initializes a new instance of the <see cref="CorrelationManager" /> class.
/// </summary>
/// <param name="correlationContextFactory">The correlation context factory used to create new contexts.</param>
/// <param name="correlationIdFactory">The correlation id factory used to generate a new correlation id per context.</param>
/// <param name="correlationContextAccessor">The correlation context accessor.</param>
/// <param name="logger">The logger.</param>
/// <param name="diagnosticListener">The diagnostics listener to run activities on.</param>
/// <param name="options">The configuration options.</param>
public CorrelationManager
(
ICorrelationContextFactory correlationContextFactory,
ICorrelationIdFactory correlationIdFactory,
ICorrelationContextAccessor correlationContextAccessor,
ILogger<CorrelationManager> logger,
DiagnosticListener diagnosticListener,
IOptions<CorrelationManagerOptions> options
) : this(correlationContextFactory, correlationIdFactory, correlationContextAccessor, logger, diagnosticListener)
{
_options = options?.Value ?? throw new ArgumentNullException(nameof(options));
}

/// <summary>
/// Creates a new activity that can be started and stopped manually.
/// </summary>
/// <returns>The correlated activity.</returns>
public IActivity CreateActivity()
{
return new RootActivity(_correlationContextFactory, _logger, _diagnosticListener);
return new RootActivity(_correlationContextFactory, _logger, _diagnosticListener, _options);
}

/// <summary>
Expand Down
12 changes: 12 additions & 0 deletions src/Correlate.Core/CorrelationManagerOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Correlate;

/// <summary>
/// The options class for configuring <see cref="CorrelationManager"/>.
/// </summary>
public class CorrelationManagerOptions
{
/// <summary>
/// The scope key that will be used for adding correlation ID to log context. Default is <c>CorrelationId</c>.
/// </summary>
public string LoggingScopeKey { get; set; } = CorrelateConstants.CorrelationIdKey;
}
10 changes: 6 additions & 4 deletions src/Correlate.Core/Extensions/LoggerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@ namespace Correlate.Extensions;

internal static class LoggerExtensions
{
public static IDisposable? BeginCorrelatedScope(this ILogger logger, string correlationId)
public static IDisposable? BeginCorrelatedScope(this ILogger logger, string scopeKey, string correlationId)
{
return logger.BeginScope(new CorrelatedLogScope(correlationId));
return logger.BeginScope(new CorrelatedLogScope(scopeKey, correlationId));
}

private sealed class CorrelatedLogScope : IReadOnlyList<KeyValuePair<string, object>>
{
private readonly string _scopeKey;
private readonly string _correlationId;

public CorrelatedLogScope(string correlationId)
public CorrelatedLogScope(string scopeKey, string correlationId)
{
_scopeKey = scopeKey;
_correlationId = correlationId;
}

Expand All @@ -41,7 +43,7 @@ public KeyValuePair<string, object> this[int index]
{
if (index == 0)
{
return new KeyValuePair<string, object>(CorrelateConstants.CorrelationIdKey, _correlationId);
return new KeyValuePair<string, object>(_scopeKey, _correlationId);
}

throw new ArgumentOutOfRangeException(nameof(index));
Expand Down
7 changes: 5 additions & 2 deletions src/Correlate.Core/RootActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,21 @@ internal class RootActivity : IActivity
{
private readonly ICorrelationContextFactory _correlationContextFactory;
private readonly DiagnosticListener? _diagnosticListener;
private readonly CorrelationManagerOptions _options;
private readonly ILogger _logger;
private IDisposable? _logScope;

public RootActivity
(
ICorrelationContextFactory correlationContextFactory,
ILogger logger,
DiagnosticListener? diagnosticListener)
DiagnosticListener? diagnosticListener,
CorrelationManagerOptions options)
{
_correlationContextFactory = correlationContextFactory ?? throw new ArgumentNullException(nameof(correlationContextFactory));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_diagnosticListener = diagnosticListener;
_options = options;
}

/// <summary>
Expand Down Expand Up @@ -49,7 +52,7 @@ public CorrelationContext Start(string correlationId)

if (isLoggingEnabled)
{
_logScope = _logger.BeginCorrelatedScope(correlationId);
_logScope = _logger.BeginCorrelatedScope(_options.LoggingScopeKey, correlationId);
}

return context;
Expand Down
17 changes: 17 additions & 0 deletions src/Correlate.DependencyInjection/IServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,23 @@ namespace Correlate.DependencyInjection;
// ReSharper disable once InconsistentNaming
public static class IServiceCollectionExtensions
{
/// <summary>
/// Adds services required for using correlation.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add the services to.</param>
/// <param name="configure">The callback to customize defaults.</param>
/// <returns>The <see cref="IServiceCollection" /> so that additional calls can be chained.</returns>
public static IServiceCollection AddCorrelate(this IServiceCollection services, Action<CorrelationManagerOptions> configure)
{
services
.AddOptions<CorrelationManagerOptions>()
.Configure(configure);

services.AddCorrelate();

return services;
}

/// <summary>
/// Adds services required for using correlation.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ public async Task When_correlating_has_started_it_should_create_logScope_with_co
.ContainSingle(le => le.MessageTemplate.Text.StartsWith("Setting response header"))
.Which.Properties
.Should()
// this tests the {CorrelationId} from log message template in CorrelateFeature.LogRequestHeaderFound, not the one from log scope added by IActivityFactory.CreateActivity
.ContainSingle(p => p.Key == expectedLogProperty)
.Which.Value
.Should()
Expand Down
29 changes: 25 additions & 4 deletions test/Correlate.Core.Tests/CorrelationManagerTests.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System.Collections;
using System.Diagnostics;
using Correlate.Testing;
using Correlate.Testing.TestCases;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Serilog;
using Serilog.Core;
using Serilog.Extensions.Logging;
Expand All @@ -15,10 +17,12 @@ public class CorrelationManagerTests : IDisposable
private readonly CorrelationContextAccessor _correlationContextAccessor;
private readonly ICorrelationIdFactory _correlationIdFactoryMock;
private readonly ILogger<CorrelationManager> _logger;
private readonly DiagnosticListener _diagnosticListener;
private readonly IOptions<CorrelationManagerOptions> _options;
private readonly SerilogLoggerProvider _logProvider;
private readonly CorrelationManager _sut;

protected CorrelationManagerTests()
protected CorrelationManagerTests(CorrelationManagerOptions options)
{
_correlationContextAccessor = new CorrelationContextAccessor();

Expand All @@ -33,12 +37,16 @@ protected CorrelationManagerTests()

_logProvider = new SerilogLoggerProvider(serilogLogger);
_logger = new TestLogger<CorrelationManager>(_logProvider.CreateLogger(nameof(CorrelationManager)));
_diagnosticListener = new DiagnosticListener("test");
_options = Options.Create(options);

_sut = new CorrelationManager(
new CorrelationContextFactory(_correlationContextAccessor),
_correlationIdFactoryMock,
_correlationContextAccessor,
_logger
_logger,
_diagnosticListener,
_options
);
}

Expand All @@ -50,6 +58,13 @@ public void Dispose()

public class Async : CorrelationManagerTests
{
public Async() : base(new()
{
LoggingScopeKey = "ActivityId"
})
{
}

[Fact]
public async Task Given_a_task_should_run_task_inside_correlated_context()
{
Expand Down Expand Up @@ -143,7 +158,7 @@ await _sut.CorrelateAsync(() =>
var logEvents = TestCorrelator.GetLogEventsFromCurrentContext().ToList();
logEvents.Should()
.HaveCount(3)
.And.ContainSingle(ev => ev.MessageTemplate.Text == "Message with correlation id." && ev.Properties.ContainsKey("CorrelationId"));
.And.ContainSingle(ev => ev.MessageTemplate.Text == "Message with correlation id." && ev.Properties.ContainsKey("ActivityId"));
}
}

Expand Down Expand Up @@ -361,6 +376,10 @@ await _sut.CorrelateAsync(innerContextId,

public class Sync : CorrelationManagerTests
{
public Sync() : base(new())
{
}

[Fact]
public void Given_a_action_should_run_action_inside_correlated_context()
{
Expand Down Expand Up @@ -674,7 +693,9 @@ public static IEnumerable<object[]> NullArgumentTestCases()
Substitute.For<ICorrelationContextFactory>(),
Substitute.For<ICorrelationIdFactory>(),
Substitute.For<ICorrelationContextAccessor>(),
Substitute.For<ILogger<CorrelationManager>>()
Substitute.For<ILogger<CorrelationManager>>(),
Substitute.For<DiagnosticListener>("test"),
Options.Create(new CorrelationManagerOptions())
);

static Task CorrelatedTask()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,20 @@ public class When_adding_correlate_to_container
private readonly IServiceCollection _services;
private readonly IServiceProvider _sut;

public When_adding_correlate_to_container()
protected When_adding_correlate_to_container(IServiceCollection services)
{
_services = new ServiceCollection()
.AddLogging()
.AddCorrelate();
_services = services;

_sut = _services.BuildServiceProvider();
}

public When_adding_correlate_to_container()
: this(new ServiceCollection()
.AddLogging()
.AddCorrelate())
{
}

[Theory]
[ClassData(typeof(ExpectedRegistrations))]
public void It_should_resolve(ExpectedRegistration registration)
Expand Down Expand Up @@ -58,3 +63,13 @@ protected virtual IEnumerable<ExpectedRegistration> TestCases()
}
}
}

public class When_adding_correlate_to_container_with_options : When_adding_correlate_to_container
{
public When_adding_correlate_to_container_with_options()
: base(new ServiceCollection()
.AddLogging()
.AddCorrelate(o => o.LoggingScopeKey = "CustomLoggingScopeKey"))
{
}
}

0 comments on commit 1c0ca25

Please sign in to comment.