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

Allow to configure log scope key #68

Merged
merged 12 commits into from
Oct 6, 2023
Merged
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 @@
{
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 All @@ -41,15 +44,15 @@

if (isDiagnosticsEnabled)
{
// TODO: add Activity support

Check warning on line 47 in src/Correlate.Core/RootActivity.cs

View workflow job for this annotation

GitHub Actions / analysis

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)

Check warning on line 47 in src/Correlate.Core/RootActivity.cs

View workflow job for this annotation

GitHub Actions / analysis

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)

Check warning on line 47 in src/Correlate.Core/RootActivity.cs

View workflow job for this annotation

GitHub Actions / analysis

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)
//var activity = new Activity("Correlated-Request");

Check warning on line 48 in src/Correlate.Core/RootActivity.cs

View workflow job for this annotation

GitHub Actions / analysis

Remove this commented out code. (https://rules.sonarsource.com/csharp/RSPEC-125)

Check warning on line 48 in src/Correlate.Core/RootActivity.cs

View workflow job for this annotation

GitHub Actions / analysis

Remove this commented out code. (https://rules.sonarsource.com/csharp/RSPEC-125)

Check warning on line 48 in src/Correlate.Core/RootActivity.cs

View workflow job for this annotation

GitHub Actions / analysis

Remove this commented out code. (https://rules.sonarsource.com/csharp/RSPEC-125)
//activity.SetParentId(correlationId);
//_diagnosticListener.StartActivity(activity, new {})
}

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 @@
// 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 All @@ -16,7 +33,7 @@
/// <returns>The <see cref="IServiceCollection" /> so that additional calls can be chained.</returns>
public static IServiceCollection AddCorrelate(this IServiceCollection services)
{
services.AddLogging();

Check warning on line 36 in src/Correlate.DependencyInjection/IServiceCollectionExtensions.cs

View workflow job for this annotation

GitHub Actions / analysis

Make sure that this logger's configuration is safe. (https://rules.sonarsource.com/csharp/RSPEC-4792)

services.TryAddSingleton<ICorrelationContextAccessor, CorrelationContextAccessor>();
services.TryAddTransient<ICorrelationContextFactory, CorrelationContextFactory>();
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"))
{
}
}
Loading