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

[log4net-bridge] limit chances of duplicated logs export both bridges are enabled #3948

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions docs/internal/log4net.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Both bridge and trace context injection are supported for `log4net` in versions

## `log4net` [logs bridge](https://opentelemetry.io/docs/specs/otel/glossary/#log-appender--bridge)

If `log4net` is used as a [logging provider](https://learn.microsoft.com/en-us/dotnet/core/extensions/logging-providers), `log4net` bridge should not be enabled, in order
to reduce possibility of duplicated logs export.

The `log4net` logs bridge is disabled by default. In order to enable it, set `OTEL_DOTNET_AUTO_LOGS_ENABLE_LOG4NET_BRIDGE` to `true`.
When `log4net` logs bridge is enabled, and `log4net` is configured with at least 1 appender, application logs are exported in OTLP
format by default to the local instance of OpenTelemetry Collector, in addition to being written into their currently configured destination (e.g. a file).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

using OpenTelemetry.AutoInstrumentation.CallTarget;
using OpenTelemetry.AutoInstrumentation.Logging;
#if NET
using OpenTelemetry.AutoInstrumentation.Logger;
#endif
Expand All @@ -23,16 +24,26 @@ namespace OpenTelemetry.AutoInstrumentation.Instrumentations.Log4Net.Bridge.Inte
type: InstrumentationType.Log)]
public static class AppenderCollectionIntegration
{
private static readonly IOtelLogger Logger = OtelLogging.GetLogger();
#if NET
private static int _warningLogged;
#endif

internal static CallTargetReturn<TReturn> OnMethodEnd<TTarget, TReturn>(TTarget instance, TReturn returnValue, Exception exception, in CallTargetState state)
{
if (
Instrumentation.LogSettings.Value.EnableLog4NetBridge &&
#if NET
#pragma warning disable SA1003
!LoggerInitializer.IsInitializedAtLeastOnce &&
#pragma warning restore SA1003
if (LoggerInitializer.IsInitializedAtLeastOnce)
{
if (Interlocked.Exchange(ref _warningLogged, 1) != default)
{
return new CallTargetReturn<TReturn>(returnValue);
}

Logger.Warning("Disabling addition of log4net bridge due to ILogger bridge initialization.");
return new CallTargetReturn<TReturn>(returnValue);
}
#endif
returnValue is Array responseArray)
if (Instrumentation.LogSettings.Value.EnableLog4NetBridge && returnValue is Array responseArray)
{
var finalArray = OpenTelemetryAppenderInitializer<TReturn>.Initialize(responseArray);
return new CallTargetReturn<TReturn>(finalArray);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
using System.Reflection;
using OpenTelemetry.AutoInstrumentation.DuckTyping;
using OpenTelemetry.AutoInstrumentation.Instrumentations.Log4Net.TraceContextInjection;
#if NET
using OpenTelemetry.AutoInstrumentation.Logger;
#endif
using OpenTelemetry.AutoInstrumentation.Logging;
using OpenTelemetry.Logs;
using Exception = System.Exception;
Expand All @@ -28,7 +31,11 @@ internal class OpenTelemetryLog4NetAppender
private static readonly Lazy<OpenTelemetryLog4NetAppender> InstanceField = new(InitializeAppender, true);

private readonly Func<string?, object?>? _getLoggerFactory;
private readonly ConcurrentDictionary<string, object> _loggers = new();
private readonly ConcurrentDictionary<string, object> _loggers = new(StringComparer.Ordinal);

#if NET
private int _warningLogged;
#endif

private OpenTelemetryLog4NetAppender(LoggerProvider loggerProvider)
{
Expand All @@ -48,6 +55,19 @@ public void DoAppend(ILoggingEvent loggingEvent)
return;
}

#if NET
if (LoggerInitializer.IsInitializedAtLeastOnce)
{
if (Interlocked.Exchange(ref _warningLogged, 1) != default)
{
return;
}

Logger.Warning("Disabling log4net bridge due to ILogger bridge initialization.");
return;
}
#endif

var logger = GetLogger(loggingEvent.LoggerName);

var logEmitter = OpenTelemetryLogHelpers.LogEmitter;
Expand Down
2 changes: 1 addition & 1 deletion test/IntegrationTests/Log4NetBridgeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public async Task SubmitLogs_ThroughILoggerBridge_WhenLog4NetIsUsedAsILoggerAppe
using var collector = new MockLogsCollector(Output);
SetExporter(collector);

collector.ExpectCollected(records => records.Count == 2, "App logs should be exported once.");
collector.ExpectCollected(records => records.Count == 3, "App logs should be exported once.");

EnableBytecodeInstrumentation();
SetEnvironmentVariable("OTEL_DOTNET_AUTO_LOGS_ENABLE_LOG4NET_BRIDGE", "true");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Except
_log.Info(formatter(state, exception));
break;
case LogLevel.Error:
_log.Error(formatter(state, exception));
_log.Error(formatter(state, exception), exception);
break;
default:
throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

using System.Diagnostics;
using log4net;
using log4net.Config;
using Microsoft.Extensions.Logging;

Expand Down Expand Up @@ -39,6 +40,8 @@ private static void Main(string[] args)

private static void LogUsingILogger()
{
var l = LogManager.GetLogger("TestApplication.Log4NetBridge");
l.Warn("Before logger factory is built.");
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddProvider(new Log4NetLoggerProvider());
Expand Down
Loading