-
Notifications
You must be signed in to change notification settings - Fork 123
Add NLog instrumentation for OpenTelemetry .NET Auto-Instrumentation #4371
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
base: main
Are you sure you want to change the base?
Changes from 8 commits
12b3643
dd0eee6
1b5ee77
489fa3b
638a6aa
206be87
799ef71
4401757
0fcf644
4ffa40a
f6cd7d5
665fb09
3a79c85
f6957e6
b078f85
4a0d5a3
bf571f5
16b004e
e650142
c41f73c
e66da6a
3d5aff8
ad1c62f
54dd6da
0602818
903ab69
4702a68
951c355
29d4251
29bde76
911fbd8
60311e1
d912c7e
c6b7bde
fad8ea4
e3d9274
e95e972
62f933d
33546ee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -246,6 +246,9 @@ EndProject | |
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SdkVersionAnalyzer", "tools\SdkVersionAnalyzer\SdkVersionAnalyzer.csproj", "{C75FA076-D460-414B-97F7-6F8D0E85AE74}" | ||
| EndProject | ||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApplication.Log4NetBridge", "test\test-applications\integrations\TestApplication.Log4NetBridge\TestApplication.Log4NetBridge.csproj", "{926B7C03-42C2-4192-94A7-CD0B1C693279}" | ||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApplication.NLogBridge", "test\test-applications\integrations\TestApplication.NLogBridge\TestApplication.NLogBridge.csproj", "{A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}" | ||
| EndProject | ||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTelemetry.AutoInstrumentation.NLogTarget", "src\OpenTelemetry.AutoInstrumentation.NLogTarget\OpenTelemetry.AutoInstrumentation.NLogTarget.csproj", "{3C7A3F7B-77E5-4C55-9B2D-1A4A9E7B1D33}" | ||
|
||
| EndProject | ||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApplication.SelectiveSampler", "test\test-applications\integrations\TestApplication.SelectiveSampler\TestApplication.SelectiveSampler.csproj", "{FD1A1ABD-6A48-4E94-B5F7-2081AFCD1BBB}" | ||
| EndProject | ||
|
|
@@ -1533,6 +1536,22 @@ Global | |
| {926B7C03-42C2-4192-94A7-CD0B1C693279}.Release|x64.Build.0 = Release|Any CPU | ||
| {926B7C03-42C2-4192-94A7-CD0B1C693279}.Release|x86.ActiveCfg = Release|Any CPU | ||
| {926B7C03-42C2-4192-94A7-CD0B1C693279}.Release|x86.Build.0 = Release|Any CPU | ||
| {A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| {A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| {A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Debug|ARM64.ActiveCfg = Debug|Any CPU | ||
| {A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Debug|ARM64.Build.0 = Debug|Any CPU | ||
| {A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Debug|x64.ActiveCfg = Debug|Any CPU | ||
| {A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Debug|x64.Build.0 = Debug|Any CPU | ||
| {A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Debug|x86.ActiveCfg = Debug|Any CPU | ||
| {A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Debug|x86.Build.0 = Debug|Any CPU | ||
| {A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
| {A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Release|Any CPU.Build.0 = Release|Any CPU | ||
| {A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Release|ARM64.ActiveCfg = Release|Any CPU | ||
| {A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Release|ARM64.Build.0 = Release|Any CPU | ||
| {A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Release|x64.ActiveCfg = Release|Any CPU | ||
| {A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Release|x64.Build.0 = Release|Any CPU | ||
| {A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Release|x86.ActiveCfg = Release|Any CPU | ||
| {A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Release|x86.Build.0 = Release|Any CPU | ||
| {FD1A1ABD-6A48-4E94-B5F7-2081AFCD1BBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| {FD1A1ABD-6A48-4E94-B5F7-2081AFCD1BBB}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| {FD1A1ABD-6A48-4E94-B5F7-2081AFCD1BBB}.Debug|ARM64.ActiveCfg = Debug|Any CPU | ||
|
|
@@ -1639,6 +1658,7 @@ Global | |
| {AA3E0C5C-A4E2-46AB-BD18-2D30D3ABF692} = {E409ADD3-9574-465C-AB09-4324D205CC7C} | ||
| {C75FA076-D460-414B-97F7-6F8D0E85AE74} = {00F4C92D-6652-4BD8-A334-B35D3E711BE6} | ||
| {926B7C03-42C2-4192-94A7-CD0B1C693279} = {E409ADD3-9574-465C-AB09-4324D205CC7C} | ||
| {A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D} = {E409ADD3-9574-465C-AB09-4324D205CC7C} | ||
| {FD1A1ABD-6A48-4E94-B5F7-2081AFCD1BBB} = {E409ADD3-9574-465C-AB09-4324D205CC7C} | ||
| EndGlobalSection | ||
| GlobalSection(ExtensibilityGlobals) = postSolution | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| #nullable enable |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| #nullable enable | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.Endpoint.get -> NLog.Layouts.Layout? | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.Endpoint.set -> void | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.Headers.get -> NLog.Layouts.Layout? | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.Headers.set -> void | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.IncludeEventParameters.get -> bool | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.IncludeEventParameters.set -> void | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.IncludeFormattedMessage.get -> bool | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.IncludeFormattedMessage.set -> void | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.MaxExportBatchSize.get -> int | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.MaxExportBatchSize.set -> void | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.MaxQueueSize.get -> int | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.MaxQueueSize.set -> void | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.OpenTelemetryTarget() -> void | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.Resources.get -> System.Collections.Generic.IList<NLog.Targets.TargetPropertyWithContext!>! | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.ScheduledDelayMilliseconds.get -> int | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.ScheduledDelayMilliseconds.set -> void | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.ServiceName.get -> NLog.Layouts.Layout? | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.ServiceName.set -> void | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.SpanIdLayout.get -> NLog.Layouts.Layout? | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.SpanIdLayout.set -> void | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.TraceIdLayout.get -> NLog.Layouts.Layout? | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.TraceIdLayout.set -> void | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.UseHttp.get -> bool | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.UseHttp.set -> void |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| #nullable enable |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| #nullable enable | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.Endpoint.get -> NLog.Layouts.Layout? | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.Endpoint.set -> void | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.Headers.get -> NLog.Layouts.Layout? | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.Headers.set -> void | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.IncludeEventParameters.get -> bool | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.IncludeEventParameters.set -> void | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.IncludeFormattedMessage.get -> bool | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.IncludeFormattedMessage.set -> void | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.MaxExportBatchSize.get -> int | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.MaxExportBatchSize.set -> void | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.MaxQueueSize.get -> int | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.MaxQueueSize.set -> void | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.OpenTelemetryTarget() -> void | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.Resources.get -> System.Collections.Generic.IList<NLog.Targets.TargetPropertyWithContext!>! | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.ScheduledDelayMilliseconds.get -> int | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.ScheduledDelayMilliseconds.set -> void | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.ServiceName.get -> NLog.Layouts.Layout? | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.ServiceName.set -> void | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.SpanIdLayout.get -> NLog.Layouts.Layout? | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.SpanIdLayout.set -> void | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.TraceIdLayout.get -> NLog.Layouts.Layout? | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.TraceIdLayout.set -> void | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.UseHttp.get -> bool | ||
| OpenTelemetry.AutoInstrumentation.NLogTarget.OpenTelemetryTarget.UseHttp.set -> void |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
| <PropertyGroup> | ||
| <TargetFrameworks>$(TargetFrameworks)</TargetFrameworks> | ||
| <GenerateDocumentationFile>false</GenerateDocumentationFile> | ||
| <IsPackable>true</IsPackable> | ||
| <Description>OpenTelemetry NLog target that forwards NLog LogEvents to OpenTelemetry Logs.</Description> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="..\OpenTelemetry.AutoInstrumentation\OpenTelemetry.AutoInstrumentation.csproj" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="NLog" /> | ||
| </ItemGroup> | ||
| </Project> | ||
|
|
zacharycmontoya marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,242 @@ | ||
| // Copyright The OpenTelemetry Authors | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| using System; | ||
| using System.Collections.Concurrent; | ||
| using System.Collections.Generic; | ||
| using System.Diagnostics; | ||
| using System.Linq; | ||
| using NLog; | ||
| using NLog.Config; | ||
| using NLog.Layouts; | ||
| using NLog.Targets; | ||
| using OpenTelemetry; | ||
| using OpenTelemetry.AutoInstrumentation; | ||
| using OpenTelemetry.AutoInstrumentation.Configurations; | ||
| using OpenTelemetry.Logs; | ||
|
|
||
| namespace OpenTelemetry.AutoInstrumentation.NLogTarget; | ||
|
|
||
| [Target("OpenTelemetryTarget")] | ||
| public sealed class OpenTelemetryTarget : TargetWithContext | ||
| { | ||
| private static readonly ConcurrentDictionary<string, object> LoggerCache = new(StringComparer.Ordinal); | ||
| private static readonly string EmptyTraceIdToHexString = default(ActivityTraceId).ToHexString(); | ||
| private static readonly string EmptySpanIdToHexString = default(ActivitySpanId).ToHexString(); | ||
| private static LoggerProvider? _loggerProvider; | ||
| private static Func<string?, object?>? _getLoggerFactory; | ||
|
|
||
| public OpenTelemetryTarget() | ||
| { | ||
| Layout = "${message}"; | ||
| TraceIdLayout = "${activity:property=TraceId}"; | ||
| SpanIdLayout = "${activity:property=SpanId}"; | ||
| } | ||
|
|
||
| [RequiredParameter] | ||
| public Layout? Endpoint { get; set; } | ||
|
|
||
| public Layout? Headers { get; set; } | ||
|
|
||
| public bool UseHttp { get; set; } = true; | ||
|
|
||
| public Layout? ServiceName { get; set; } | ||
|
|
||
| [ArrayParameter(typeof(TargetPropertyWithContext), "resource")] | ||
| public IList<TargetPropertyWithContext> Resources { get; } = new List<TargetPropertyWithContext>(); | ||
|
|
||
| public bool IncludeFormattedMessage { get; set; } = true; | ||
|
|
||
| public bool IncludeEventParameters { get; set; } = true; | ||
|
|
||
| public Layout? TraceIdLayout { get; set; } | ||
|
|
||
| public Layout? SpanIdLayout { get; set; } | ||
|
|
||
| public int ScheduledDelayMilliseconds { get; set; } = 5000; | ||
|
|
||
| public int MaxQueueSize { get; set; } = 2048; | ||
|
|
||
| public int MaxExportBatchSize { get; set; } = 512; | ||
|
|
||
| protected override void InitializeTarget() | ||
| { | ||
| base.InitializeTarget(); | ||
|
|
||
| if (_loggerProvider != null) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| var createLoggerProviderBuilderMethod = typeof(Sdk).GetMethod("CreateLoggerProviderBuilder", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)!; | ||
| var loggerProviderBuilder = (LoggerProviderBuilder)createLoggerProviderBuilderMethod.Invoke(null, null)!; | ||
|
|
||
| loggerProviderBuilder = loggerProviderBuilder | ||
| .SetResourceBuilder(ResourceConfigurator.CreateResourceBuilder(Instrumentation.GeneralSettings.Value.EnabledResourceDetectors)); | ||
|
|
||
| loggerProviderBuilder = loggerProviderBuilder.AddOtlpExporter(options => | ||
| { | ||
| var endpoint = RenderLogEvent(Endpoint, LogEventInfo.CreateNullEvent()); | ||
zacharycmontoya marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if (string.IsNullOrEmpty(endpoint)) | ||
| { | ||
| endpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT"); | ||
| } | ||
|
|
||
| if (!string.IsNullOrEmpty(endpoint)) | ||
| { | ||
| options.Endpoint = new Uri(endpoint!, UriKind.RelativeOrAbsolute); | ||
| } | ||
|
|
||
| var headers = RenderLogEvent(Headers, LogEventInfo.CreateNullEvent()); | ||
| if (!string.IsNullOrEmpty(headers)) | ||
| { | ||
| options.Headers = headers; | ||
| } | ||
|
|
||
| options.Protocol = UseHttp ? OpenTelemetry.Exporter.OtlpExportProtocol.HttpProtobuf : OpenTelemetry.Exporter.OtlpExportProtocol.Grpc; | ||
| options.BatchExportProcessorOptions.ScheduledDelayMilliseconds = ScheduledDelayMilliseconds; | ||
| options.BatchExportProcessorOptions.MaxQueueSize = MaxQueueSize; | ||
| options.BatchExportProcessorOptions.MaxExportBatchSize = MaxExportBatchSize; | ||
| }); | ||
|
|
||
| _loggerProvider = loggerProviderBuilder.Build(); | ||
| _getLoggerFactory = CreateGetLoggerDelegate(_loggerProvider); | ||
| } | ||
|
|
||
| protected override void Write(LogEventInfo logEvent) | ||
| { | ||
| if (_loggerProvider is null) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| if (Sdk.SuppressInstrumentation) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| var logger = GetOrCreateLogger(logEvent.LoggerName); | ||
| if (logger is null) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| var properties = GetLogEventProperties(logEvent); | ||
|
|
||
| var body = IncludeFormattedMessage ? RenderLogEvent(Layout, logEvent) : Convert.ToString(logEvent.Message); | ||
|
|
||
| var severityText = logEvent.Level.Name; | ||
| var severityNumber = MapLogLevelToSeverity(logEvent.Level); | ||
|
|
||
| // Resolve trace context using Layout properties (works with AsyncWrapper) | ||
| Activity? current = null; | ||
| var traceIdString = RenderLogEvent(TraceIdLayout, logEvent); | ||
| var spanIdString = RenderLogEvent(SpanIdLayout, logEvent); | ||
|
|
||
| if (!string.IsNullOrEmpty(traceIdString) && !string.IsNullOrEmpty(spanIdString) && | ||
| traceIdString != EmptyTraceIdToHexString && spanIdString != EmptySpanIdToHexString) | ||
| { | ||
| try | ||
| { | ||
| var traceId = ActivityTraceId.CreateFromString(traceIdString); | ||
| var spanId = ActivitySpanId.CreateFromString(spanIdString); | ||
| var activityContext = new ActivityContext(traceId, spanId, ActivityTraceFlags.Recorded); | ||
|
||
| current = new Activity("OpenTelemetryTarget").SetParentId(activityContext.TraceId, activityContext.SpanId, activityContext.TraceFlags); | ||
| } | ||
| catch | ||
| { | ||
| // If parsing fails, fall back to Activity.Current | ||
| current = Activity.Current; | ||
| } | ||
| } | ||
| else | ||
| { | ||
| current = Activity.Current; | ||
| } | ||
|
|
||
| // Emit using internal helpers via reflection delegate | ||
| var renderedMessage = IncludeFormattedMessage ? RenderLogEvent(Layout, logEvent) : logEvent.Message; | ||
| var args = IncludeEventParameters && !logEvent.HasProperties && logEvent.Parameters is object[] p ? p : null; | ||
|
|
||
| OpenTelemetry.AutoInstrumentation.Instrumentations.NLog.Bridge.OpenTelemetryLogHelpers.LogEmitter?.Invoke( | ||
| logger, | ||
| body, | ||
| logEvent.TimeStamp, | ||
| severityText, | ||
| severityNumber, | ||
| logEvent.Exception, | ||
| properties, | ||
| current, | ||
| args, | ||
| renderedMessage); | ||
| } | ||
|
|
||
| private static int MapLogLevelToSeverity(LogLevel level) | ||
| { | ||
| // Map NLog ordinals 0..5 to OTEL severity 1..24 approximate buckets | ||
| return level.Ordinal switch | ||
| { | ||
| 0 => 1, // Trace | ||
| 1 => 5, // Debug | ||
| 2 => 9, // Info | ||
| 3 => 13, // Warn | ||
| 4 => 17, // Error | ||
| 5 => 21, // Fatal | ||
| _ => 9 | ||
| }; | ||
| } | ||
|
|
||
| private static Func<string?, object?>? CreateGetLoggerDelegate(LoggerProvider loggerProvider) | ||
| { | ||
| try | ||
| { | ||
| var methodInfo = typeof(LoggerProvider) | ||
| .GetMethod("GetLogger", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance, null, new[] { typeof(string) }, null)!; | ||
| return (Func<string?, object?>)methodInfo.CreateDelegate(typeof(Func<string?, object?>), loggerProvider); | ||
| } | ||
| catch | ||
| { | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| private object? GetOrCreateLogger(string? loggerName) | ||
| { | ||
| var key = loggerName ?? string.Empty; | ||
| if (LoggerCache.TryGetValue(key, out var logger)) | ||
| { | ||
| return logger; | ||
| } | ||
|
|
||
| var factory = _getLoggerFactory; | ||
| if (factory is null) | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| logger = factory(loggerName); | ||
| if (logger is not null) | ||
| { | ||
| LoggerCache[key] = logger; | ||
| } | ||
|
|
||
| return logger; | ||
| } | ||
|
|
||
| private IEnumerable<KeyValuePair<string, object?>>? GetLogEventProperties(LogEventInfo logEvent) | ||
| { | ||
| // Check HasProperties first to avoid allocating empty dictionary | ||
| if (!logEvent.HasProperties && ContextProperties.Count == 0) | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| var allProperties = GetAllProperties(logEvent); | ||
| if (allProperties.Count == 0) | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| return allProperties.Select(kvp => new KeyValuePair<string, object?>(Convert.ToString(kvp.Key)!, kvp.Value)); | ||
|
||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.