Skip to content

Commit

Permalink
merge main
Browse files Browse the repository at this point in the history
  • Loading branch information
lachmatt committed Apr 8, 2022
1 parent 537dfe7 commit beeb224
Show file tree
Hide file tree
Showing 6 changed files with 307 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,15 @@ public static TracerProviderBuilder InvokePlugins(this TracerProviderBuilder bui

private static TracerProviderBuilder InvokePlugin(this TracerProviderBuilder builder, string pluginAssemblyQualifiedName)
{
const string configureTracerProviderMethodName = "ConfigureTracerProvider";

// get the type and method
var t = Type.GetType(pluginAssemblyQualifiedName);
var mi = t.GetMethod("ConfigureTracerProvider", new Type[] { typeof(TracerProviderBuilder) });
var t = Type.GetType(pluginAssemblyQualifiedName, throwOnError: true);
var mi = t.GetMethod(configureTracerProviderMethodName, new Type[] { typeof(TracerProviderBuilder) });
if (mi is null)
{
throw new MissingMethodException(t.Name, configureTracerProviderMethodName);
}

// execute
var obj = Activator.CreateInstance(t);
Expand Down
Original file line number Diff line number Diff line change
@@ -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 = OtelLogging.GetLogger();
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;
}
}
}
8 changes: 8 additions & 0 deletions src/OpenTelemetry.AutoInstrumentation/Instrumentation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
// </copyright>

using System;
using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.Threading;
using OpenTelemetry.AutoInstrumentation.Configuration;
using OpenTelemetry.AutoInstrumentation.Diagnostics;
using OpenTelemetry.AutoInstrumentation.Logging;
using OpenTelemetry.Context.Propagation;
using OpenTelemetry.Shims.OpenTracing;
Expand All @@ -34,6 +37,7 @@ public static class Instrumentation

private static int _firstInitialization = 1;
private static int _isExiting = 0;
private static SdkSelfDiagnosticsEventListener _sdkEventListener;

private static TracerProvider _tracerProvider;

Expand Down Expand Up @@ -76,6 +80,9 @@ public static void Initialize()
{
if (TracerSettings.LoadTracerAtStartup)
{
// Initialize SdkSelfDiagnosticsEventListener to create an EventListener for the OpenTelemetry SDK
_sdkEventListener = new(EventLevel.Warning);

var builder = Sdk
.CreateTracerProviderBuilder()
.UseEnvironmentVariables(TracerSettings)
Expand Down Expand Up @@ -135,6 +142,7 @@ private static void OnExit(object sender, EventArgs e)
try
{
_tracerProvider.Dispose();
_sdkEventListener.Dispose();

Logger.Information("OpenTelemetry tracer exit.");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// <copyright file="PluginsConfigurationHelperTests.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>

using System;
using System.IO;
using FluentAssertions;
using OpenTelemetry.AutoInstrumentation.Configuration;
using OpenTelemetry.Trace;
using Xunit;

namespace OpenTelemetry.AutoInstrumentation.Tests.Configuration;

public class PluginsConfigurationHelperTests
{
[Fact]
public void MissingAssembly()
{
var action = () => Sdk.CreateTracerProviderBuilder().InvokePlugins(new[] { "Missing.Assembly.PluginType, Missing.Assembly" });

action.Should().Throw<FileNotFoundException>();
}

[Fact]
public void MissingPluginTypeFromAssembly()
{
var action = () => Sdk.CreateTracerProviderBuilder().InvokePlugins(new[] { "Missing.PluginType" });

action.Should().Throw<TypeLoadException>();
}

[Fact]
public void PluginTypeMissingExpectedMethod()
{
var pluginAssemblyQualifiedName = GetType().AssemblyQualifiedName;
var action = () => Sdk.CreateTracerProviderBuilder().InvokePlugins(new[] { pluginAssemblyQualifiedName });

action.Should().Throw<MissingMethodException>();
}

[Fact]
public void PluginTypeMissingDefaultConstructor()
{
var pluginAssemblyQualifiedName = typeof(MockPluginMissingDefaultConstructor).AssemblyQualifiedName;
var action = () => Sdk.CreateTracerProviderBuilder().InvokePlugins(new[] { pluginAssemblyQualifiedName });

action.Should().Throw<MissingMethodException>();
}

[Fact]
public void InvokePluginSuccess()
{
var pluginAssemblyQualifiedName = typeof(MockPlugin).AssemblyQualifiedName;
Sdk.CreateTracerProviderBuilder().InvokePlugins(new[] { pluginAssemblyQualifiedName });
}

public class MockPlugin
{
public TracerProviderBuilder ConfigureTracerProvider(TracerProviderBuilder builder)
{
return builder;
}
}

public class MockPluginMissingDefaultConstructor : MockPlugin
{
public MockPluginMissingDefaultConstructor(string ignored)
{
throw new InvalidOperationException("this plugin is not expected to be successfully constructed");
}
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.0">
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down

0 comments on commit beeb224

Please sign in to comment.