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;
}
}
21 changes: 14 additions & 7 deletions 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;

/// <summary>
/// Initializes a new instance of the <see cref="CorrelationManager" /> class.
Expand All @@ -21,18 +23,21 @@ public class CorrelationManager : IAsyncCorrelationManager, ICorrelationManager,
/// <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="options">The configuration options.</param>
public CorrelationManager
(
ICorrelationContextFactory correlationContextFactory,
ICorrelationIdFactory correlationIdFactory,
ICorrelationContextAccessor correlationContextAccessor,
ILogger<CorrelationManager> logger
ILogger<CorrelationManager> logger,
IOptions<CorrelationManagerOptions> options
)
{
_correlationContextFactory = correlationContextFactory ?? throw new ArgumentNullException(nameof(correlationContextFactory));
_correlationIdFactory = correlationIdFactory ?? throw new ArgumentNullException(nameof(correlationIdFactory));
_correlationContextAccessor = correlationContextAccessor ?? throw new ArgumentNullException(nameof(correlationContextAccessor));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_options = options.Value;
}

/// <summary>
Expand All @@ -43,14 +48,16 @@ ILogger<CorrelationManager> logger
/// <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
) : this(correlationContextFactory, correlationIdFactory, correlationContextAccessor, logger)
DiagnosticListener diagnosticListener,
IOptions<CorrelationManagerOptions> options
) : this(correlationContextFactory, correlationIdFactory, correlationContextAccessor, logger, options)
laurynasr marked this conversation as resolved.
Show resolved Hide resolved
{
_diagnosticListener = diagnosticListener ?? throw new ArgumentNullException(nameof(diagnosticListener));
}
Expand All @@ -61,7 +68,7 @@ DiagnosticListener diagnosticListener
/// <returns>The correlated activity.</returns>
public IActivity CreateActivity()
{
return new RootActivity(_correlationContextFactory, _logger, _diagnosticListener);
return new RootActivity(_correlationContextFactory, _logger, _diagnosticListener, _options);
}

/// <summary>
Expand Down Expand Up @@ -205,12 +212,12 @@ private T Execute<T>(string? correlationId, Func<T> correlatedFunc, OnException?
}
}

private static bool HandlesException<T>(OnException? onException, CorrelationContext correlationContext, Exception ex, out T result)
private bool HandlesException<T>(OnException? onException, CorrelationContext correlationContext, Exception ex, out T result)
{
if (!ex.Data.Contains(CorrelateConstants.CorrelationIdKey))
if (!ex.Data.Contains(_options.LoggingScopeKey))
{
// Because we're about to lose context scope, enrich exception with correlation id for reference by calling code.
ex.Data.Add(CorrelateConstants.CorrelationIdKey, correlationContext.CorrelationId);
ex.Data.Add(_options.LoggingScopeKey, correlationContext.CorrelationId);
laurynasr marked this conversation as resolved.
Show resolved Hide resolved
}

if (onException is not null)
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 @@ IEnumerator IEnumerable.GetEnumerator()
{
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
2 changes: 1 addition & 1 deletion src/Correlate.Core/Http/CorrelateClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/// <summary>
/// Client options for adding correlation id to outgoing requests.
/// </summary>
public class CorrelateClientOptions
public class CorrelateClientOptions : CorrelationManagerOptions
{
/// <summary>
/// Gets or sets the request header to set the correlation id in for outgoing requests. Default 'X-Correlation-ID'.
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
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ public static IHttpClientBuilder CorrelateRequests(this IHttpClientBuilder build
throw new ArgumentNullException(nameof(builder));
}

builder.Services.AddOptions<CorrelationManagerOptions>()
.Configure((CorrelationManagerOptions cmo) =>
{
var options = new CorrelateClientOptions();
configureOptions(options);
cmo.LoggingScopeKey = options.LoggingScopeKey;
});

laurynasr marked this conversation as resolved.
Show resolved Hide resolved
builder.Services.AddCorrelate();

builder.Services.TryAddTransient<CorrelatingHttpMessageHandler>();
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
26 changes: 21 additions & 5 deletions test/Correlate.Core.Tests/CorrelationManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
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 +16,11 @@ public class CorrelationManagerTests : IDisposable
private readonly CorrelationContextAccessor _correlationContextAccessor;
private readonly ICorrelationIdFactory _correlationIdFactoryMock;
private readonly ILogger<CorrelationManager> _logger;
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 +35,14 @@ protected CorrelationManagerTests()

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

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

Expand All @@ -50,6 +54,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 +154,7 @@ public async Task Should_create_log_scope()
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 @@ -231,7 +242,7 @@ Task ThrowingTask()
.Cast<DictionaryEntry>()
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
.Should()
.ContainKey(CorrelateConstants.CorrelationIdKey)
.ContainKey("ActivityId")
.WhoseValue.Should()
.Be(GeneratedCorrelationId);
}
Expand Down Expand Up @@ -361,6 +372,10 @@ public Task When_starting_correlationContext_when_another_context_is_active_shou

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 +689,8 @@ public static IEnumerable<object[]> NullArgumentTestCases()
Substitute.For<ICorrelationContextFactory>(),
Substitute.For<ICorrelationIdFactory>(),
Substitute.For<ICorrelationContextAccessor>(),
Substitute.For<ILogger<CorrelationManager>>()
Substitute.For<ILogger<CorrelationManager>>(),
Substitute.For<IOptions<CorrelationManagerOptions>>()
);

static Task CorrelatedTask()
Expand Down