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

Start Azure SDK log forwarder in AzMon OTel distro #42374

Merged
merged 9 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
// Licensed under the MIT License.

using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using Azure.Core.Diagnostics;
using Azure.Core.Shared;
Expand Down Expand Up @@ -49,6 +47,8 @@ private void LogEvent(EventWrittenEventArgs eventData)
return;
}

// The same logic is used in the Azure.Monitor.OpenTelemetry.AspNetCore.
jsquire marked this conversation as resolved.
Show resolved Hide resolved
// If you make any changes here, please check if they are applicable there as well.
var logger = _loggers.GetOrAdd(eventData.EventSource.Name, name => _loggerFactory.CreateLogger(ToLoggerName(name)));
logger.Log(MapLevel(eventData.Level), new EventId(eventData.EventId, eventData.EventName), new EventSourceEvent(eventData), null, _formatMessage);
}
Expand Down Expand Up @@ -89,32 +89,5 @@ private static string FormatMessage(EventSourceEvent eventSourceEvent, Exception
{
return EventSourceEventFormatting.Format(eventSourceEvent.EventData);
}

private readonly struct EventSourceEvent: IReadOnlyList<KeyValuePair<string, object>>
{
public EventWrittenEventArgs EventData { get; }

public EventSourceEvent(EventWrittenEventArgs eventData)
{
EventData = eventData;
}

public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
for (int i = 0; i < Count; i++)
{
yield return new KeyValuePair<string, object>(EventData.PayloadNames[i], EventData.Payload[i]);
}
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

public int Count => EventData.PayloadNames.Count;

public KeyValuePair<string, object> this[int index] => new KeyValuePair<string, object>(EventData.PayloadNames[index], EventData.Payload[index]);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Tracing;

namespace Microsoft.Extensions.Azure
{
internal readonly struct EventSourceEvent: IReadOnlyList<KeyValuePair<string, object>>
lmolkova marked this conversation as resolved.
Show resolved Hide resolved
{
public EventWrittenEventArgs EventData { get; }

public EventSourceEvent(EventWrittenEventArgs eventData)
{
EventData = eventData;
}

public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
for (int i = 0; i < Count; i++)
{
yield return new KeyValuePair<string, object>(EventData.PayloadNames[i], EventData.Payload[i]);
}
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

public int Count => EventData.PayloadNames.Count;

public KeyValuePair<string, object> this[int index] => new KeyValuePair<string, object>(EventData.PayloadNames[index], EventData.Payload[index]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@
<!--<ProjectReference Include="..\..\Azure.Monitor.OpenTelemetry.Exporter\src\Azure.Monitor.OpenTelemetry.Exporter.csproj" />-->
<!--<ProjectReference Include="..\..\Azure.Monitor.OpenTelemetry.LiveMetrics\src\Azure.Monitor.OpenTelemetry.LiveMetrics.csproj" />-->
</ItemGroup>
<!-- Shared source from Exporter -->

<!-- Shared sources -->
<ItemGroup>
<Compile Include="..\..\Azure.Monitor.OpenTelemetry.Exporter\src\Internals\ExceptionExtensions.cs" LinkBase="Shared" />
<Compile Include="..\..\..\extensions\Microsoft.Extensions.Azure\src\Internal\EventSourceEvent.cs" LinkBase="Shared" />
<Compile Include="$(AzureCoreSharedSources)\EventSourceEventFormatting.cs" LinkBase="Shared" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Diagnostics.Tracing;
using System.Runtime.CompilerServices;
using Azure.Monitor.OpenTelemetry.Exporter.Internals;
Expand Down Expand Up @@ -74,5 +73,17 @@ public void GetEnvironmentVariableFailed(string envVarName, Exception ex)

[Event(5, Message = "Failed to Read environment variable {0}, exception: {1}", Level = EventLevel.Error)]
public void GetEnvironmentVariableFailed(string envVarName, string exceptionMessage) => WriteEvent(5, envVarName, exceptionMessage);

[Event(6, Message = "Failed to map unknown EventSource log level {0}", Level = EventLevel.Warning)]
lmolkova marked this conversation as resolved.
Show resolved Hide resolved
public void MapLogLevelFailed(EventLevel level)
{
if (IsEnabled(EventLevel.Warning))
{
WriteEvent(6, level.ToString());
}
}
lmolkova marked this conversation as resolved.
Show resolved Hide resolved

[Event(7, Message = "Found existing Microsoft.Extensions.Azure.AzureEventSourceLogForwarder registration.", Level = EventLevel.Informational)]
public void LogForwarderIsAlreadyRegistered() => WriteEvent(7);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections.Concurrent;
using System.Diagnostics.Tracing;
using Azure.Core.Diagnostics;
using Azure.Core.Shared;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Azure;

namespace Azure.Monitor.OpenTelemetry.AspNetCore.Internals.AzureSdkCompat
{
internal sealed class AzureEventSourceLogForwarder : IHostedService, IDisposable
{
internal static readonly AzureEventSourceLogForwarder Noop = new AzureEventSourceLogForwarder(null);
private readonly ILoggerFactory _loggerFactory;

private readonly ConcurrentDictionary<string, ILogger> _loggers = new ConcurrentDictionary<string, ILogger>();

private readonly Func<EventSourceEvent, Exception, string> _formatMessage = FormatMessage;

private AzureEventSourceListener _listener;

public AzureEventSourceLogForwarder(ILoggerFactory loggerFactory)
{
_loggerFactory = loggerFactory;
}

private void LogEvent(EventWrittenEventArgs eventData)
{
var logger = _loggers.GetOrAdd(eventData.EventSource.Name, name => _loggerFactory!.CreateLogger(ToLoggerName(name)));
lmolkova marked this conversation as resolved.
Show resolved Hide resolved
logger.Log(MapLevel(eventData.Level), new EventId(eventData.EventId, eventData.EventName), new EventSourceEvent(eventData), null, _formatMessage);
}

private static string ToLoggerName(string name)
{
return name.Replace('-', '.');
}

private static LogLevel MapLevel(EventLevel level)
{
switch (level)
{
case EventLevel.Critical:
return LogLevel.Critical;
case EventLevel.Error:
return LogLevel.Error;
case EventLevel.Informational:
return LogLevel.Information;
case EventLevel.Verbose:
return LogLevel.Debug;
case EventLevel.Warning:
return LogLevel.Warning;
case EventLevel.LogAlways:
return LogLevel.Information;
default:
AzureMonitorAspNetCoreEventSource.Log.MapLogLevelFailed(level);
return LogLevel.None;
}
}

private static string FormatMessage(EventSourceEvent eventSourceEvent, Exception exception)
{
return EventSourceEventFormatting.Format(eventSourceEvent.EventData);
}

public Task StartAsync(CancellationToken cancellationToken)
{
if (_loggerFactory != null)
{
_listener ??= new AzureEventSourceListener((e, s) => LogEvent(e), EventLevel.Verbose);
rajkumar-rangaraj marked this conversation as resolved.
Show resolved Hide resolved
}

return Task.CompletedTask;
}

public Task StopAsync(CancellationToken cancellationToken)
{
_listener?.Dispose();
return Task.CompletedTask;
}

public void Dispose()
{
_listener?.Dispose();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#nullable enable

using System.Reflection;
using Azure.Monitor.OpenTelemetry.AspNetCore.Internals.AzureSdkCompat;
using Azure.Monitor.OpenTelemetry.AspNetCore.Internals.Profiling;
using Azure.Monitor.OpenTelemetry.Exporter;
using Azure.Monitor.OpenTelemetry.LiveMetrics;
Expand Down Expand Up @@ -184,6 +185,21 @@ public static OpenTelemetryBuilder UseAzureMonitor(this OpenTelemetryBuilder bui
azureMonitorOptions.Get(Options.DefaultName).SetValueToLiveMetricsExporterOptions(exporterOptions);
});

// Register Azure SDK log forwarder in the case it was not registered by the user application.
builder.Services.AddHostedService(sp =>
{
var logForwarderType = Type.GetType("Microsoft.Extensions.Azure.AzureEventSourceLogForwarder, Microsoft.Extensions.Azure", false);

if (logForwarderType != null && sp.GetService(logForwarderType) != null)
{
AzureMonitorAspNetCoreEventSource.Log.LogForwarderIsAlreadyRegistered();
return AzureEventSourceLogForwarder.Noop;
}

var loggerFactory = sp.GetRequiredService<ILoggerFactory>();
return new AzureEventSourceLogForwarder(loggerFactory);
});

return builder;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Azure" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
Expand All @@ -17,6 +18,7 @@
</ItemGroup>

<ItemGroup>
<Compile Include="$(AzureCoreSharedSources)AzureEventSource.cs" LinkBase="SharedSource\Azure.Core" />
<Compile Include="..\..\..\Azure.Monitor.OpenTelemetry.Exporter\tests\Azure.Monitor.OpenTelemetry.Exporter.Tests\CommonTestFramework\EventSourceTestHelper.cs" LinkBase="CommonTestFramework" />
<Compile Include="..\..\..\Azure.Monitor.OpenTelemetry.Exporter\tests\Azure.Monitor.OpenTelemetry.Exporter.Tests\CommonTestFramework\TestEventListener.cs" LinkBase="CommonTestFramework" />
</ItemGroup>
Expand Down
Loading
Loading