Implementation and test of SdkSelfDiagnosticsEventListener, which emulates the SDK's own SelfDiagnosticsEventListener to capture all event sources that begin with the prefix 'OpenTelemetry-' (#458)
Co-authored-by: Paulo Janotti <pjanotti@splunk.com>
This commit is contained in:
parent
d02924167f
commit
6588bbc567
|
|
@ -0,0 +1,111 @@
|
|||
// <copyright file="SdkSelfDiagnosticsEventListener.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
// Source originated from https://github.com/open-telemetry/opentelemetry-dotnet/blob/23609730ddd73c860553de847e67c9b2226cff94/src/OpenTelemetry/Internal/SelfDiagnosticsEventListener.cs
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Tracing;
|
||||
using OpenTelemetry.AutoInstrumentation.Logging;
|
||||
|
||||
namespace OpenTelemetry.AutoInstrumentation.Diagnostics;
|
||||
|
||||
/// <summary>
|
||||
/// SdkSelfDiagnosticsEventListener class enables the events from OpenTelemetry event sources
|
||||
/// and write the events to the OpenTelemetry.AutoInstrumentation logger
|
||||
/// </summary>
|
||||
internal class SdkSelfDiagnosticsEventListener : EventListener
|
||||
{
|
||||
private const string EventSourceNamePrefix = "OpenTelemetry-";
|
||||
|
||||
private static readonly ILogger Log = ConsoleLogger.Create(typeof(SdkSelfDiagnosticsEventListener));
|
||||
private readonly object lockObj = new();
|
||||
private readonly EventLevel logLevel;
|
||||
private readonly List<EventSource> eventSourcesBeforeConstructor = new();
|
||||
|
||||
public SdkSelfDiagnosticsEventListener(EventLevel eventLevel)
|
||||
{
|
||||
logLevel = eventLevel;
|
||||
|
||||
List<EventSource> eventSources;
|
||||
lock (lockObj)
|
||||
{
|
||||
eventSources = this.eventSourcesBeforeConstructor;
|
||||
eventSourcesBeforeConstructor = null;
|
||||
}
|
||||
|
||||
foreach (var eventSource in eventSources)
|
||||
{
|
||||
EnableEvents(eventSource, logLevel, EventKeywords.All);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnEventSourceCreated(EventSource eventSource)
|
||||
{
|
||||
if (eventSource.Name.StartsWith(EventSourceNamePrefix, StringComparison.Ordinal))
|
||||
{
|
||||
// If there are EventSource classes already initialized as of now, this method would be called from
|
||||
// the base class constructor before the first line of code in SelfDiagnosticsEventListener constructor.
|
||||
// In this case logLevel is always its default value, "LogAlways".
|
||||
// Thus we should save the event source and enable them later, when code runs in constructor.
|
||||
if (eventSourcesBeforeConstructor != null)
|
||||
{
|
||||
lock (lockObj)
|
||||
{
|
||||
if (eventSourcesBeforeConstructor != null)
|
||||
{
|
||||
eventSourcesBeforeConstructor.Add(eventSource);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EnableEvents(eventSource, logLevel, EventKeywords.All);
|
||||
}
|
||||
|
||||
base.OnEventSourceCreated(eventSource);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method records the events from event sources to a local file, which is provided as a stream object by
|
||||
/// SelfDiagnosticsConfigRefresher class. The file size is bound to a upper limit. Once the write position
|
||||
/// reaches the end, it will be reset to the beginning of the file.
|
||||
/// </summary>
|
||||
/// <param name="eventData">Data of the EventSource event.</param>
|
||||
protected override void OnEventWritten(EventWrittenEventArgs eventData)
|
||||
{
|
||||
var payloadArray = new object[eventData.Payload.Count];
|
||||
eventData.Payload.CopyTo(payloadArray, 0);
|
||||
|
||||
switch (eventData.Level)
|
||||
{
|
||||
case EventLevel.Critical:
|
||||
case EventLevel.Error:
|
||||
Log.Error("EventSource={0}, Message={1}", eventData.EventSource.Name, string.Format(eventData.Message, payloadArray));
|
||||
break;
|
||||
case EventLevel.Warning:
|
||||
Log.Warning("EventSource={0}, Message={1}", eventData.EventSource.Name, string.Format(eventData.Message, payloadArray));
|
||||
break;
|
||||
case EventLevel.LogAlways:
|
||||
case EventLevel.Informational:
|
||||
Log.Information("EventSource={0}, Message={1}", eventData.EventSource.Name, string.Format(eventData.Message, payloadArray));
|
||||
break;
|
||||
case EventLevel.Verbose:
|
||||
Log.Debug("EventSource={0}, Message={1}", eventData.EventSource.Name, string.Format(eventData.Message, payloadArray));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,8 +16,10 @@
|
|||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.Tracing;
|
||||
using System.Threading;
|
||||
using OpenTelemetry.AutoInstrumentation.Configuration;
|
||||
using OpenTelemetry.AutoInstrumentation.Diagnostics;
|
||||
using OpenTelemetry.Context.Propagation;
|
||||
using OpenTelemetry.Shims.OpenTracing;
|
||||
using OpenTelemetry.Trace;
|
||||
|
|
@ -32,6 +34,7 @@ public static class Instrumentation
|
|||
private static readonly Process _process = Process.GetCurrentProcess();
|
||||
private static int _firstInitialization = 1;
|
||||
private static int _isExiting = 0;
|
||||
private static SdkSelfDiagnosticsEventListener _sdkEventListener;
|
||||
|
||||
private static TracerProvider _tracerProvider;
|
||||
|
||||
|
|
@ -74,6 +77,9 @@ public static class Instrumentation
|
|||
{
|
||||
if (TracerSettings.LoadTracerAtStartup)
|
||||
{
|
||||
// Initialize SdkSelfDiagnosticsEventListener to create an EventListener for the OpenTelemetry SDK
|
||||
_sdkEventListener = new(EventLevel.Warning);
|
||||
|
||||
var builder = Sdk
|
||||
.CreateTracerProviderBuilder()
|
||||
.UseEnvironmentVariables(TracerSettings)
|
||||
|
|
@ -133,6 +139,7 @@ public static class Instrumentation
|
|||
try
|
||||
{
|
||||
_tracerProvider.Dispose();
|
||||
_sdkEventListener.Dispose();
|
||||
|
||||
Log("OpenTelemetry tracer exit.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ internal class ConsoleLogger : ILogger
|
|||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"[{_name}] {level}:{string.Format(messageTemplate, args)}");
|
||||
Console.WriteLine($"[{_name}] {level}: {string.Format(messageTemplate, args)}");
|
||||
|
||||
if (exception != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
// <copyright file="SdkSelfDiagnosticsEventListenerTests.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
// Source originated from https://github.com/open-telemetry/opentelemetry-dotnet/blob/23609730ddd73c860553de847e67c9b2226cff94/test/OpenTelemetry.Tests/Internal/SelfDiagnosticsEventListenerTest.cs
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.Tracing;
|
||||
using System.IO;
|
||||
using FluentAssertions;
|
||||
using OpenTelemetry.AutoInstrumentation.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
public class SdkSelfDiagnosticsEventListenerTests
|
||||
{
|
||||
[Fact]
|
||||
public void EventSourceSetup_LowerSeverity()
|
||||
{
|
||||
var originalConsoleOut = Console.Out; // preserve the original stream
|
||||
using var writer = new StringWriter();
|
||||
var listener = new SdkSelfDiagnosticsEventListener(EventLevel.Error);
|
||||
|
||||
// Emitting a Verbose event. Or any EventSource event with lower severity than Error.
|
||||
AspNetTelemetryEventSource.Log.ActivityRestored("123");
|
||||
OpenTelemetrySdkEventSource.Log.ActivityStarted("Activity started", "1");
|
||||
|
||||
// Prepare the output for assertion
|
||||
writer.Flush();
|
||||
var outputString = writer.GetStringBuilder().ToString();
|
||||
outputString.Should().NotContain("EventSource=OpenTelemetry-Instrumentation-AspNet-Telemetry, Message=Activity restored, Id='123'");
|
||||
outputString.Should().NotContain("EventSource=OpenTelemetry-Sdk, Message=Activity started.");
|
||||
|
||||
// Cleanup
|
||||
Console.SetOut(originalConsoleOut);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EventSourceSetup_HigherSeverity()
|
||||
{
|
||||
// Redirect the ConsoleLogger
|
||||
var originalConsoleOut = Console.Out;
|
||||
using var writer = new StringWriter();
|
||||
Console.SetOut(writer);
|
||||
var listener = new SdkSelfDiagnosticsEventListener(EventLevel.Verbose);
|
||||
|
||||
// Emitting a Verbose event. Or any EventSource event with lower severity than Error.
|
||||
AspNetTelemetryEventSource.Log.ActivityRestored("123");
|
||||
OpenTelemetrySdkEventSource.Log.ActivityStarted("Activity started", "1");
|
||||
|
||||
// Prepare the output for assertion
|
||||
writer.Flush();
|
||||
var outputString = writer.GetStringBuilder().ToString();
|
||||
outputString.Should().Contain("EventSource=OpenTelemetry-Instrumentation-AspNet-Telemetry, Message=Activity restored, Id='123'");
|
||||
outputString.Should().Contain("EventSource=OpenTelemetry-Sdk, Message=Activity started.");
|
||||
|
||||
// Cleanup
|
||||
Console.SetOut(originalConsoleOut);
|
||||
}
|
||||
|
||||
[EventSource(Name = "OpenTelemetry-Instrumentation-AspNet-Telemetry", Guid = "1de158cc-f7ce-4293-bd19-2358c93c8186")]
|
||||
internal sealed class AspNetTelemetryEventSource : EventSource
|
||||
{
|
||||
public static readonly AspNetTelemetryEventSource Log = new();
|
||||
|
||||
[Event(4, Message = "Activity restored, Id='{0}'", Level = EventLevel.Informational)]
|
||||
public void ActivityRestored(string id)
|
||||
{
|
||||
this.WriteEvent(4, id);
|
||||
}
|
||||
}
|
||||
|
||||
[EventSource(Name = "OpenTelemetry-Sdk")]
|
||||
internal class OpenTelemetrySdkEventSource : EventSource
|
||||
{
|
||||
public static readonly OpenTelemetrySdkEventSource Log = new();
|
||||
|
||||
[Event(24, Message = "Activity started. OperationName = '{0}', Id = '{1}'.", Level = EventLevel.Verbose)]
|
||||
public void ActivityStarted(string operationName, string id)
|
||||
{
|
||||
this.WriteEvent(24, operationName, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue