Support options change in plugins (#1542)

This commit is contained in:
Rasmus Kuusmann 2022-11-08 10:55:56 +02:00 committed by GitHub
parent c587f56bc5
commit 79ff7dc750
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 625 additions and 467 deletions

View File

@ -28,6 +28,8 @@ This component adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.h
- Error message on the native log if bytecode instrumentation type is missing all
instrumentation methods [#1499](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/issues/1499).
- Plugins can overwrite OpenTelemetry dotnet SDK instrumentation and exporter options.
See more at [plugins.md](docs/plugins.md).
## [0.4.0-beta.1](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/v0.4.0-beta.1)

View File

@ -186,36 +186,16 @@ Important environment variables include:
## Additional settings
| Environment variable | Description | Default value |
|------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|
| `OTEL_DOTNET_AUTO_TRACES_ENABLED` | Enables trace provider together with traces source instrumentations. | `true` |
| `OTEL_DOTNET_AUTO_OPENTRACING_ENABLED` | Enables OpenTracing tracer. | `false` |
| `OTEL_DOTNET_AUTO_METRICS_ENABLED` | Enables meter provider together with metrics source instrumentations. | `true` |
| `OTEL_DOTNET_AUTO_TRACES_ADDITIONAL_SOURCES` | Comma-separated list of additional `System.Diagnostics.ActivitySource` names to be added to the tracer at the startup. Use it to capture manually instrumented spans. | |
| `OTEL_DOTNET_AUTO_LEGACY_SOURCES` | Comma-separated list of additional legacy source names to be added to the tracer at the startup. Use it to capture `System.Diagnostics.Activity` objects created without using the `System.Diagnostics.ActivitySource` API. | |
| `OTEL_DOTNET_AUTO_FLUSH_ON_UNHANDLEDEXCEPTION` | Controls whether the telemetry data is flushed when an [AppDomain.UnhandledException](https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.unhandledexception) event is raised. Set to `true` when you suspect that you are experiencing a problem with missing telemetry data and also experiencing unhandled exceptions. | `false` |
| `OTEL_DOTNET_AUTO_METRICS_ADDITIONAL_SOURCES` | Comma-separated list of additional `System.Diagnostics.Metrics.Meter` names to be added to the meter at the startup. Use it to capture manually instrumented spans. | |
| `OTEL_DOTNET_AUTO_PLUGINS` | Colon-separated list of OTel SDK instrumentation plugin types, specified with the [assembly-qualified name](https://docs.microsoft.com/en-us/dotnet/api/system.type.assemblyqualifiedname?view=net-6.0#system-type-assemblyqualifiedname). _Note: This list must be colon-separated because the type names may include commas._ | |
You can use `OTEL_DOTNET_AUTO_PLUGINS` environment variable to extend the
configuration of the OpenTelemetry .NET SDK Tracer, Meter or Logs. A plugin
must be a non-static, non-abstract class which has a default constructor
and that implements at least one of the configuration methods below:
```csharp
public OpenTelemetry.Trace.TracerProviderBuilder ConfigureTracerProvider(OpenTelemetry.Trace.TracerProviderBuilder builder)
```
```csharp
public OpenTelemetry.Metrics.MeterProviderBuilder ConfigureMeterProvider(OpenTelemetry.Metrics.MeterProviderBuilder builder)
```
```csharp
public OpenTelemetry.Logs.OpenTelemetryLoggerOptions ConfigureLoggerOptions(OpenTelemetry.Logs.OpenTelemetryLoggerOptions builder)
```
The plugin must use the same version of the `OpenTelemetry` as the
OpenTelemetry .NET Automatic Instrumentation.
| Environment variable | Description | Default value |
|------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|
| `OTEL_DOTNET_AUTO_TRACES_ENABLED` | Enables trace provider together with traces source instrumentations. | `true` |
| `OTEL_DOTNET_AUTO_OPENTRACING_ENABLED` | Enables OpenTracing tracer. | `false` |
| `OTEL_DOTNET_AUTO_METRICS_ENABLED` | Enables meter provider together with metrics source instrumentations. | `true` |
| `OTEL_DOTNET_AUTO_TRACES_ADDITIONAL_SOURCES` | Comma-separated list of additional `System.Diagnostics.ActivitySource` names to be added to the tracer at the startup. Use it to capture manually instrumented spans. | |
| `OTEL_DOTNET_AUTO_LEGACY_SOURCES` | Comma-separated list of additional legacy source names to be added to the tracer at the startup. Use it to capture `System.Diagnostics.Activity` objects created without using the `System.Diagnostics.ActivitySource` API. | |
| `OTEL_DOTNET_AUTO_FLUSH_ON_UNHANDLEDEXCEPTION` | Controls whether the telemetry data is flushed when an [AppDomain.UnhandledException](https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.unhandledexception) event is raised. Set to `true` when you suspect that you are experiencing a problem with missing telemetry data and also experiencing unhandled exceptions. | `false` |
| `OTEL_DOTNET_AUTO_METRICS_ADDITIONAL_SOURCES` | Comma-separated list of additional `System.Diagnostics.Metrics.Meter` names to be added to the meter at the startup. Use it to capture manually instrumented spans. | |
| `OTEL_DOTNET_AUTO_PLUGINS` | Colon-separated list of OTel SDK instrumentation plugin types, specified with the [assembly-qualified name](https://docs.microsoft.com/en-us/dotnet/api/system.type.assemblyqualifiedname?view=net-6.0#system-type-assemblyqualifiedname). _Note: This list must be colon-separated because the type names may include commas._ See more info on how to write plugins at [plugins.md](plugins.md). | |
## .NET CLR Profiler

88
docs/plugins.md Normal file
View File

@ -0,0 +1,88 @@
# Plugins
You can use `OTEL_DOTNET_AUTO_PLUGINS` environment variable to extend the
configuration and overwrite options of the OpenTelemetry .NET SDK Tracer, Meter or
Logs. A plugin must be a non-static, non-abstract class which has a default constructor
and that implements at least one of the configuration methods below showed
in an example plugin class:
```csharp
public class MyPlugin
{
// To configure tracing SDK
public OpenTelemetry.Trace.TracerProviderBuilder ConfigureTracerProvider(OpenTelemetry.Trace.TracerProviderBuilder builder)
{
// My custom logic here
return builder;
}
// To configure metrics SDK
public OpenTelemetry.Metrics.MeterProviderBuilder ConfigureMeterProvider(OpenTelemetry.Metrics.MeterProviderBuilder builder)
{
// My custom logic here
return builder;
}
// To configure logs SDK
public void ConfigureOptions(OpenTelemetry.Logs.OpenTelemetryLoggerOptions options)
{
// My custom logic here
}
// To configure any options used by OpenTelemetry .NET Automatic Instrumentation
public void ConfigureOptions(OpenTelemetry.NameSpace.OptionType options)
{
// My custom logic here
// Find supported options below
}
}
```
## Supported Options
### Tracing
| Options type | NuGet package | NuGet version |
|------------------------------------------------------------------------------|----------------------------------------------|---------------|
| OpenTelemetry.Exporter.ConsoleExporterOptions | OpenTelemetry.Exporter.Console | 1.3.1 |
| OpenTelemetry.Exporter.ZipkinExporterOptions | OpenTelemetry.Exporter.Zipkin | 1.3.1 |
| OpenTelemetry.Exporter.JaegerExporterOptions | OpenTelemetry.Exporter.Jaeger | 1.3.1 |
| OpenTelemetry.Exporter.OtlpExporterOptions | OpenTelemetry.Exporter.OpenTelemetryProtocol | 1.3.1 |
| OpenTelemetry.Instrumentation.AspNet.AspNetInstrumentationOptions | OpenTelemetry.Instrumentation.AspNet | 1.0.0-rc9.6 |
| OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreInstrumentationOptions | OpenTelemetry.Instrumentation.AspNetCore | 1.0.0-rc9.4 |
| OpenTelemetry.Instrumentation.GrpcNetClient.GrpcClientInstrumentationOptions | OpenTelemetry.Instrumentation.GrpcNetClient | 1.0.0-rc9.4 |
| OpenTelemetry.Instrumentation.Http.HttpClientInstrumentationOptions | OpenTelemetry.Instrumentation.Http | 1.0.0-rc9.4 |
| OpenTelemetry.Instrumentation.Http.HttpWebRequestInstrumentationOptions | OpenTelemetry.Instrumentation.Http | 1.0.0-rc9.4 |
| OpenTelemetry.Instrumentation.SqlClient.SqlClientInstrumentationOptions | OpenTelemetry.Instrumentation.SqlClient | 1.0.0-rc9.4 |
| OpenTelemetry.Instrumentation.MySqlData.MySqlDataInstrumentationOptions | OpenTelemetry.Instrumentation.MySqlData | 1.0.0-beta.4 |
| OpenTelemetry.Instrumentation.Wcf.WcfInstrumentationOptions | OpenTelemetry.Instrumentation.Wcf | 1.0.0-rc7 |
### Metrics
| Options type | NuGet package | NuGet version |
|---------------------------------------------------------------------|----------------------------------------------|---------------|
| OpenTelemetry.Metrics.MetricReaderOptions | OpenTelemetry | 1.3.1 |
| OpenTelemetry.Exporter.ConsoleExporterOptions | OpenTelemetry.Exporter.Console | 1.3.1 |
| OpenTelemetry.Exporter.PrometheusExporterOptions | OpenTelemetry.Exporter.Prometheus | 1.3.0-rc.2 |
| OpenTelemetry.Exporter.OtlpExporterOptions | OpenTelemetry.Exporter.OpenTelemetryProtocol | 1.3.1 |
| OpenTelemetry.Exporter.OtlpExporterOptions | OpenTelemetry.Exporter.OpenTelemetryProtocol | 1.3.1 |
| OpenTelemetry.Instrumentation.Runtime.RuntimeInstrumentationOptions | OpenTelemetry.Instrumentation.Runtime | 1.0.0 |
| OpenTelemetry.Instrumentation.Process.ProcessInstrumentationOptions | OpenTelemetry.Instrumentation.Process | 0.1.0-alpha.1 |
### Logs
| Options type | NuGet package | NuGet version |
|-----------------------------------------------|----------------------------------------------|---------------|
| OpenTelemetry.Logs.OpenTelemetryLoggerOptions | OpenTelemetry | 1.3.1 |
| OpenTelemetry.Exporter.ConsoleExporterOptions | OpenTelemetry.Exporter.Console | 1.3.1 |
| OpenTelemetry.Exporter.OtlpExporterOptions | OpenTelemetry.Exporter.OpenTelemetryProtocol | 1.3.1 |
## Requirements
* The plugin must use the same version of the `OpenTelemetry` as the
OpenTelemetry .NET Automatic Instrumentation.
* The plugin must use the same options versions as the
OpenTelemetry .NET Automatic Instrumentation (found in the table above).

View File

@ -35,7 +35,7 @@ internal class BootstrapperHostingStartup : IHostingStartup
/// </summary>
public BootstrapperHostingStartup()
{
_settings = LogSettings.FromDefaultSources();
_settings = Instrumentation.LogSettings;
}
/// <summary>

View File

@ -18,6 +18,7 @@ using System;
using System.Linq;
using System.Runtime.CompilerServices;
using OpenTelemetry.AutoInstrumentation.Logging;
using OpenTelemetry.AutoInstrumentation.Plugins;
using OpenTelemetry.Metrics;
namespace OpenTelemetry.AutoInstrumentation.Configuration;
@ -26,10 +27,10 @@ internal static class EnvironmentConfigurationMetricHelper
{
private static readonly ILogger Logger = OtelLogging.GetLogger();
public static MeterProviderBuilder UseEnvironmentVariables(this MeterProviderBuilder builder, MetricSettings settings)
public static MeterProviderBuilder UseEnvironmentVariables(this MeterProviderBuilder builder, MetricSettings settings, PluginManager pluginManager)
{
builder
.SetExporter(settings)
.SetExporter(settings, pluginManager)
.AddMeter(settings.Meters.ToArray());
foreach (var enabledMeter in settings.EnabledInstrumentations)
@ -38,8 +39,8 @@ internal static class EnvironmentConfigurationMetricHelper
{
MetricInstrumentation.AspNet => Wrappers.AddSdkAspNetInstrumentation(builder),
MetricInstrumentation.HttpClient => Wrappers.AddHttpClientInstrumentation(builder),
MetricInstrumentation.NetRuntime => Wrappers.AddRuntimeInstrumentation(builder),
MetricInstrumentation.Process => Wrappers.AddProcessInstrumentation(builder),
MetricInstrumentation.NetRuntime => Wrappers.AddRuntimeInstrumentation(builder, pluginManager),
MetricInstrumentation.Process => Wrappers.AddProcessInstrumentation(builder, pluginManager),
_ => null,
};
}
@ -47,17 +48,17 @@ internal static class EnvironmentConfigurationMetricHelper
return builder;
}
private static MeterProviderBuilder SetExporter(this MeterProviderBuilder builder, MetricSettings settings)
private static MeterProviderBuilder SetExporter(this MeterProviderBuilder builder, MetricSettings settings, PluginManager pluginManager)
{
if (settings.ConsoleExporterEnabled)
{
Wrappers.AddConsoleExporter(builder, settings);
Wrappers.AddConsoleExporter(builder, settings, pluginManager);
}
return settings.MetricExporter switch
{
MetricsExporter.Prometheus => Wrappers.AddPrometheusExporter(builder),
MetricsExporter.Otlp => Wrappers.AddOtlpExporter(builder, settings),
MetricsExporter.Prometheus => Wrappers.AddPrometheusExporter(builder, pluginManager),
MetricsExporter.Otlp => Wrappers.AddOtlpExporter(builder, settings, pluginManager),
MetricsExporter.None => builder,
_ => throw new ArgumentOutOfRangeException($"Metrics exporter '{settings.MetricExporter}' is incorrect")
};
@ -90,33 +91,36 @@ internal static class EnvironmentConfigurationMetricHelper
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static MeterProviderBuilder AddRuntimeInstrumentation(MeterProviderBuilder builder)
public static MeterProviderBuilder AddRuntimeInstrumentation(MeterProviderBuilder builder, PluginManager pluginManager)
{
return builder.AddRuntimeInstrumentation();
return builder.AddRuntimeInstrumentation(pluginManager.ConfigureOptions);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static MeterProviderBuilder AddProcessInstrumentation(MeterProviderBuilder builder)
public static MeterProviderBuilder AddProcessInstrumentation(MeterProviderBuilder builder, PluginManager pluginManager)
{
return builder.AddProcessInstrumentation();
return builder.AddProcessInstrumentation(pluginManager.ConfigureOptions);
}
// Exporters
[MethodImpl(MethodImplOptions.NoInlining)]
public static MeterProviderBuilder AddConsoleExporter(MeterProviderBuilder builder, MetricSettings settings)
public static MeterProviderBuilder AddConsoleExporter(MeterProviderBuilder builder, MetricSettings settings, PluginManager pluginManager)
{
return builder.AddConsoleExporter((_, metricReaderOptions) =>
return builder.AddConsoleExporter((consoleExporterOptions, metricReaderOptions) =>
{
if (settings.MetricExportInterval != null)
{
metricReaderOptions.PeriodicExportingMetricReaderOptions.ExportIntervalMilliseconds = settings.MetricExportInterval;
}
pluginManager.ConfigureOptions(consoleExporterOptions);
pluginManager.ConfigureOptions(metricReaderOptions);
});
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static MeterProviderBuilder AddPrometheusExporter(MeterProviderBuilder builder)
public static MeterProviderBuilder AddPrometheusExporter(MeterProviderBuilder builder, PluginManager pluginManager)
{
Logger.Warning("Prometheus exporter is configured. It is intended for the inner dev loop. Do NOT use in production");
@ -124,11 +128,13 @@ internal static class EnvironmentConfigurationMetricHelper
{
options.StartHttpListener = true;
options.ScrapeResponseCacheDurationMilliseconds = 300;
pluginManager.ConfigureOptions(options);
});
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static MeterProviderBuilder AddOtlpExporter(MeterProviderBuilder builder, MetricSettings settings)
public static MeterProviderBuilder AddOtlpExporter(MeterProviderBuilder builder, MetricSettings settings, PluginManager pluginManager)
{
#if NETCOREAPP3_1
if (settings.Http2UnencryptedSupportEnabled)
@ -151,6 +157,9 @@ internal static class EnvironmentConfigurationMetricHelper
{
metricReaderOptions.PeriodicExportingMetricReaderOptions.ExportIntervalMilliseconds = settings.MetricExportInterval;
}
pluginManager.ConfigureOptions(options);
pluginManager.ConfigureOptions(metricReaderOptions);
});
}
}

View File

@ -17,26 +17,27 @@
using System;
using System.Linq;
using System.Runtime.CompilerServices;
using OpenTelemetry.AutoInstrumentation.Plugins;
using OpenTelemetry.Trace;
namespace OpenTelemetry.AutoInstrumentation.Configuration;
internal static class EnvironmentConfigurationTracerHelper
{
public static TracerProviderBuilder UseEnvironmentVariables(this TracerProviderBuilder builder, TracerSettings settings)
public static TracerProviderBuilder UseEnvironmentVariables(this TracerProviderBuilder builder, TracerSettings settings, PluginManager pluginManager)
{
builder.SetExporter(settings);
builder.SetExporter(settings, pluginManager);
foreach (var enabledInstrumentation in settings.EnabledInstrumentations)
{
_ = enabledInstrumentation switch
{
TracerInstrumentation.AspNet => Wrappers.AddSdkAspNetInstrumentation(builder),
TracerInstrumentation.GrpcNetClient => Wrappers.AddGrpcClientInstrumentation(builder),
TracerInstrumentation.HttpClient => Wrappers.AddHttpClientInstrumentation(builder),
TracerInstrumentation.AspNet => Wrappers.AddSdkAspNetInstrumentation(builder, pluginManager),
TracerInstrumentation.GrpcNetClient => Wrappers.AddGrpcClientInstrumentation(builder, pluginManager),
TracerInstrumentation.HttpClient => Wrappers.AddHttpClientInstrumentation(builder, pluginManager),
TracerInstrumentation.Npgsql => builder.AddSource("Npgsql"),
TracerInstrumentation.SqlClient => Wrappers.AddSqlClientInstrumentation(builder),
TracerInstrumentation.Wcf => Wrappers.AddWcfInstrumentation(builder),
TracerInstrumentation.SqlClient => Wrappers.AddSqlClientInstrumentation(builder, pluginManager),
TracerInstrumentation.Wcf => Wrappers.AddWcfInstrumentation(builder, pluginManager),
#if NETCOREAPP3_1_OR_GREATER
TracerInstrumentation.MassTransit => builder.AddSource("MassTransit"),
TracerInstrumentation.MongoDB => builder.AddSource("MongoDB.Driver.Core.Extensions.DiagnosticSources"),
@ -56,18 +57,18 @@ internal static class EnvironmentConfigurationTracerHelper
return builder;
}
private static TracerProviderBuilder SetExporter(this TracerProviderBuilder builder, TracerSettings settings)
private static TracerProviderBuilder SetExporter(this TracerProviderBuilder builder, TracerSettings settings, PluginManager pluginManager)
{
if (settings.ConsoleExporterEnabled)
{
Wrappers.AddConsoleExporter(builder);
Wrappers.AddConsoleExporter(builder, pluginManager);
}
return settings.TracesExporter switch
{
TracesExporter.Zipkin => Wrappers.AddZipkinExporter(builder),
TracesExporter.Jaeger => Wrappers.AddJaegerExporter(builder),
TracesExporter.Otlp => Wrappers.AddOtlpExporter(builder, settings),
TracesExporter.Zipkin => Wrappers.AddZipkinExporter(builder, pluginManager),
TracesExporter.Jaeger => Wrappers.AddJaegerExporter(builder, pluginManager),
TracesExporter.Otlp => Wrappers.AddOtlpExporter(builder, settings, pluginManager),
TracesExporter.None => builder,
_ => throw new ArgumentOutOfRangeException($"Traces exporter '{settings.TracesExporter}' is incorrect")
};
@ -82,22 +83,22 @@ internal static class EnvironmentConfigurationTracerHelper
// Instrumentations
[MethodImpl(MethodImplOptions.NoInlining)]
public static TracerProviderBuilder AddWcfInstrumentation(TracerProviderBuilder builder)
public static TracerProviderBuilder AddWcfInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager)
{
return builder.AddWcfInstrumentation();
return builder.AddWcfInstrumentation(pluginManager.ConfigureOptions);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static TracerProviderBuilder AddHttpClientInstrumentation(TracerProviderBuilder builder)
public static TracerProviderBuilder AddHttpClientInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager)
{
return builder.AddHttpClientInstrumentation();
return builder.AddHttpClientInstrumentation(pluginManager.ConfigureOptions);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static TracerProviderBuilder AddSdkAspNetInstrumentation(TracerProviderBuilder builder)
public static TracerProviderBuilder AddSdkAspNetInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager)
{
#if NET462
builder.AddAspNetInstrumentation();
builder.AddAspNetInstrumentation(pluginManager.ConfigureOptions);
#elif NETCOREAPP3_1_OR_GREATER
builder.AddSource("OpenTelemetry.Instrumentation.AspNetCore");
builder.AddLegacySource("Microsoft.AspNetCore.Hosting.HttpRequestIn");
@ -107,40 +108,43 @@ internal static class EnvironmentConfigurationTracerHelper
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static TracerProviderBuilder AddSqlClientInstrumentation(TracerProviderBuilder builder)
public static TracerProviderBuilder AddSqlClientInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager)
{
return builder.AddSqlClientInstrumentation();
return builder.AddSqlClientInstrumentation(pluginManager.ConfigureOptions);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static TracerProviderBuilder AddGrpcClientInstrumentation(TracerProviderBuilder builder)
public static TracerProviderBuilder AddGrpcClientInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager)
{
return builder.AddGrpcClientInstrumentation(options =>
options.SuppressDownstreamInstrumentation = !Instrumentation.TracerSettings.EnabledInstrumentations.Contains(TracerInstrumentation.HttpClient));
{
options.SuppressDownstreamInstrumentation = !Instrumentation.TracerSettings.EnabledInstrumentations.Contains(TracerInstrumentation.HttpClient);
pluginManager.ConfigureOptions(options);
});
}
// Exporters
[MethodImpl(MethodImplOptions.NoInlining)]
public static TracerProviderBuilder AddConsoleExporter(TracerProviderBuilder builder)
public static TracerProviderBuilder AddConsoleExporter(TracerProviderBuilder builder, PluginManager pluginManager)
{
return builder.AddConsoleExporter();
return builder.AddConsoleExporter(pluginManager.ConfigureOptions);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static TracerProviderBuilder AddZipkinExporter(TracerProviderBuilder builder)
public static TracerProviderBuilder AddZipkinExporter(TracerProviderBuilder builder, PluginManager pluginManager)
{
return builder.AddZipkinExporter();
return builder.AddZipkinExporter(pluginManager.ConfigureOptions);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static TracerProviderBuilder AddJaegerExporter(TracerProviderBuilder builder)
public static TracerProviderBuilder AddJaegerExporter(TracerProviderBuilder builder, PluginManager pluginManager)
{
return builder.AddJaegerExporter();
return builder.AddJaegerExporter(pluginManager.ConfigureOptions);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static TracerProviderBuilder AddOtlpExporter(TracerProviderBuilder builder, TracerSettings settings)
public static TracerProviderBuilder AddOtlpExporter(TracerProviderBuilder builder, TracerSettings settings, PluginManager pluginManager)
{
#if NETCOREAPP3_1
if (settings.Http2UnencryptedSupportEnabled)
@ -156,6 +160,7 @@ internal static class EnvironmentConfigurationTracerHelper
if (settings.OtlpExportProtocol.HasValue)
{
options.Protocol = settings.OtlpExportProtocol.Value;
pluginManager.ConfigureOptions(options);
}
});
}

View File

@ -0,0 +1,50 @@
// <copyright file="GeneralSettings.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.Collections.Generic;
namespace OpenTelemetry.AutoInstrumentation.Configuration;
internal class GeneralSettings : Settings
{
public GeneralSettings(IConfigurationSource source)
: base(source)
{
var providerPlugins = source.GetString(ConfigurationKeys.ProviderPlugins);
if (providerPlugins != null)
{
foreach (var pluginAssemblyQualifiedName in providerPlugins.Split(Constants.ConfigurationValues.DotNetQualifiedNameSeparator))
{
Plugins.Add(pluginAssemblyQualifiedName);
}
}
FlushOnUnhandledException = source.GetBool(ConfigurationKeys.FlushOnUnhandledException) ?? false;
}
/// <summary>
/// Gets the list of plugins represented by <see cref="Type.AssemblyQualifiedName"/>.
/// </summary>
public IList<string> Plugins { get; } = new List<string>();
/// <summary>
/// Gets a value indicating whether the <see cref="AppDomain.UnhandledException"/> event should trigger
/// the flushing of telemetry data.
/// Default is <c>false</c>.
/// </summary>
public bool FlushOnUnhandledException { get; }
}

View File

@ -30,7 +30,7 @@ internal class LogSettings : Settings
/// using the specified <see cref="IConfigurationSource"/> to initialize values.
/// </summary>
/// <param name="source">The <see cref="IConfigurationSource"/> to use when retrieving configuration values.</param>
private LogSettings(IConfigurationSource source)
public LogSettings(IConfigurationSource source)
: base(source)
{
LogExporter = ParseLogExporter(source);
@ -63,21 +63,6 @@ internal class LogSettings : Settings
/// </summary>
public IList<LogInstrumentation> EnabledInstrumentations { get; }
internal static LogSettings FromDefaultSources()
{
var configurationSource = new CompositeConfigurationSource
{
new EnvironmentConfigurationSource(),
#if NETFRAMEWORK
// on .NET Framework only, also read from app.config/web.config
new NameValueConfigurationSource(System.Configuration.ConfigurationManager.AppSettings)
#endif
};
return new LogSettings(configurationSource);
}
private static LogExporter ParseLogExporter(IConfigurationSource source)
{
var logExporterEnvVar = source.GetString(ConfigurationKeys.Logs.Exporter)

View File

@ -30,7 +30,7 @@ internal class MetricSettings : Settings
/// using the specified <see cref="IConfigurationSource"/> to initialize values.
/// </summary>
/// <param name="source">The <see cref="IConfigurationSource"/> to use when retrieving configuration values.</param>
private MetricSettings(IConfigurationSource source)
public MetricSettings(IConfigurationSource source)
: base(source)
{
MetricExporter = ParseMetricExporter(source);
@ -84,21 +84,6 @@ internal class MetricSettings : Settings
/// </summary>
public IList<string> Meters { get; } = new List<string>();
internal static MetricSettings FromDefaultSources()
{
var configurationSource = new CompositeConfigurationSource
{
new EnvironmentConfigurationSource(),
#if NETFRAMEWORK
// on .NET Framework only, also read from app.config/web.config
new NameValueConfigurationSource(System.Configuration.ConfigurationManager.AppSettings)
#endif
};
return new MetricSettings(configurationSource);
}
private static MetricsExporter ParseMetricExporter(IConfigurationSource source)
{
var metricsExporterEnvVar = source.GetString(ConfigurationKeys.Metrics.Exporter)

View File

@ -14,10 +14,7 @@
// limitations under the License.
// </copyright>
using System;
using System.Collections.Generic;
using OpenTelemetry.AutoInstrumentation.Logging;
using OpenTelemetry.Logs;
using OpenTelemetry.AutoInstrumentation.Plugins;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;
@ -25,92 +22,13 @@ namespace OpenTelemetry.AutoInstrumentation.Configuration;
internal static class PluginsConfigurationHelper
{
private static readonly ILogger Logger = OtelLogging.GetLogger();
public static TracerProviderBuilder InvokePlugins(this TracerProviderBuilder builder, IEnumerable<string> pluginsAssemblyQualifiedNames)
public static TracerProviderBuilder InvokePlugins(this TracerProviderBuilder builder, PluginManager pluginManager)
{
foreach (var assemblyQualifiedName in pluginsAssemblyQualifiedNames)
{
builder = builder.InvokePlugin(assemblyQualifiedName);
}
return builder;
return pluginManager.ConfigureTracerProviderBuilder(builder);
}
public static MeterProviderBuilder InvokePlugins(this MeterProviderBuilder builder, IEnumerable<string> pluginsAssemblyQualifiedNames)
public static MeterProviderBuilder InvokePlugins(this MeterProviderBuilder builder, PluginManager pluginManager)
{
foreach (var assemblyQualifiedName in pluginsAssemblyQualifiedNames)
{
builder = builder.InvokePlugin(assemblyQualifiedName);
}
return builder;
}
public static OpenTelemetryLoggerOptions InvokePlugins(this OpenTelemetryLoggerOptions options, IEnumerable<string> pluginsAssemblyQualifiedNames)
{
foreach (var assemblyQualifiedName in pluginsAssemblyQualifiedNames)
{
options = options.InvokePlugin(assemblyQualifiedName);
}
return options;
}
private static TracerProviderBuilder InvokePlugin(this TracerProviderBuilder builder, string pluginAssemblyQualifiedName)
{
const string configureTracerProviderMethodName = "ConfigureTracerProvider";
// get the type and method
var t = Type.GetType(pluginAssemblyQualifiedName, throwOnError: true);
var mi = t.GetMethod(configureTracerProviderMethodName, new Type[] { typeof(TracerProviderBuilder) });
if (mi is null)
{
Logger.Information(new MissingMethodException(t.Name, configureTracerProviderMethodName), $"{configureTracerProviderMethodName} is missing in {pluginAssemblyQualifiedName}");
return builder;
}
// execute
var obj = Activator.CreateInstance(t);
var result = mi.Invoke(obj, new object[] { builder });
return (TracerProviderBuilder)result;
}
private static MeterProviderBuilder InvokePlugin(this MeterProviderBuilder builder, string pluginAssemblyQualifiedName)
{
const string configureMeterProviderMethodName = "ConfigureMeterProvider";
// get the type and method
var t = Type.GetType(pluginAssemblyQualifiedName, throwOnError: true);
var mi = t.GetMethod(configureMeterProviderMethodName, new Type[] { typeof(MeterProviderBuilder) });
if (mi is null)
{
Logger.Information(new MissingMethodException(t.Name, configureMeterProviderMethodName), $"{configureMeterProviderMethodName} is missing in {pluginAssemblyQualifiedName}");
return builder;
}
// execute
var obj = Activator.CreateInstance(t);
var result = mi.Invoke(obj, new object[] { builder });
return (MeterProviderBuilder)result;
}
private static OpenTelemetryLoggerOptions InvokePlugin(this OpenTelemetryLoggerOptions options, string pluginAssemblyQualifiedName)
{
const string configureLoggerOptionsMethodName = "ConfigureLoggerOptions";
// get the type and method
var t = Type.GetType(pluginAssemblyQualifiedName, throwOnError: true);
var mi = t.GetMethod(configureLoggerOptionsMethodName, new Type[] { typeof(OpenTelemetryLoggerOptions) });
if (mi is null)
{
Logger.Information(new MissingMethodException(t.Name, configureLoggerOptionsMethodName), $"{configureLoggerOptionsMethodName} is missing in {pluginAssemblyQualifiedName}");
return options;
}
// execute
var obj = Activator.CreateInstance(t);
var result = mi.Invoke(obj, new object[] { options });
return (OpenTelemetryLoggerOptions)result;
return pluginManager.ConfigureMeterProviderBuilder(builder);
}
}

View File

@ -22,9 +22,10 @@ namespace OpenTelemetry.AutoInstrumentation.Configuration;
/// <summary>
/// Propagator Settings
/// </summary>
internal class SdkSettings
internal class SdkSettings : Settings
{
private SdkSettings(CompositeConfigurationSource source)
public SdkSettings(IConfigurationSource source)
: base(source)
{
var propagators = source.GetString(ConfigurationKeys.Sdk.Propagators);
@ -42,21 +43,6 @@ internal class SdkSettings
/// </summary>
public IList<Propagator> Propagators { get; } = new List<Propagator>();
internal static SdkSettings FromDefaultSources()
{
var configurationSource = new CompositeConfigurationSource
{
new EnvironmentConfigurationSource(),
#if NETFRAMEWORK
// on .NET Framework only, also read from app.config/web.config
new NameValueConfigurationSource(System.Configuration.ConfigurationManager.AppSettings)
#endif
};
return new SdkSettings(configurationSource);
}
private static Propagator ParsePropagator(string propagator)
{
switch (propagator)

View File

@ -15,7 +15,7 @@
// </copyright>
using System;
using System.Collections.Generic;
using System.Reflection;
using OpenTelemetry.Exporter;
namespace OpenTelemetry.AutoInstrumentation.Configuration;
@ -39,16 +39,6 @@ internal abstract class Settings
OtlpExportProtocol = GetExporterOtlpProtocol(source);
Http2UnencryptedSupportEnabled = source.GetBool(ConfigurationKeys.Http2UnencryptedSupportEnabled) ?? false;
FlushOnUnhandledException = source.GetBool(ConfigurationKeys.FlushOnUnhandledException) ?? false;
var providerPlugins = source.GetString(ConfigurationKeys.ProviderPlugins);
if (providerPlugins != null)
{
foreach (var pluginAssemblyQualifiedName in providerPlugins.Split(Constants.ConfigurationValues.DotNetQualifiedNameSeparator))
{
Plugins.Add(pluginAssemblyQualifiedName);
}
}
}
/// <summary>
@ -64,17 +54,31 @@ internal abstract class Settings
/// </summary>
public bool Http2UnencryptedSupportEnabled { get; }
/// <summary>
/// Gets a value indicating whether the <see cref="AppDomain.UnhandledException"/> event should trigger
/// the flushing of telemetry data.
/// Default is <c>false</c>.
/// </summary>
public bool FlushOnUnhandledException { get; }
public static T FromDefaultSources<T>()
where T : Settings
{
var configurationSource = new CompositeConfigurationSource
{
new EnvironmentConfigurationSource(),
/// <summary>
/// Gets the list of plugins represented by <see cref="Type.AssemblyQualifiedName"/>.
/// </summary>
public IList<string> Plugins { get; } = new List<string>();
#if NETFRAMEWORK
// on .NET Framework only, also read from app.config/web.config
new NameValueConfigurationSource(System.Configuration.ConfigurationManager.AppSettings)
#endif
};
try
{
return (T)typeof(T)
.GetConstructor(new[] { typeof(IConfigurationSource) })
.Invoke(new object[] { configurationSource });
}
catch (TargetInvocationException ex)
{
// Unwrap the more informative internal exception
throw ex.InnerException;
}
}
private static OtlpExportProtocol? GetExporterOtlpProtocol(IConfigurationSource source)
{

View File

@ -31,7 +31,7 @@ internal class TracerSettings : Settings
/// using the specified <see cref="IConfigurationSource"/> to initialize values.
/// </summary>
/// <param name="source">The <see cref="IConfigurationSource"/> to use when retrieving configuration values.</param>
private TracerSettings(IConfigurationSource source)
public TracerSettings(IConfigurationSource source)
: base(source)
{
TracesExporter = ParseTracesExporter(source);
@ -106,21 +106,6 @@ internal class TracerSettings : Settings
/// </summary>
public InstrumentationOptions InstrumentationOptions { get; private set; }
internal static TracerSettings FromDefaultSources()
{
var configurationSource = new CompositeConfigurationSource
{
new EnvironmentConfigurationSource(),
#if NETFRAMEWORK
// on .NET Framework only, also read from app.config/web.config
new NameValueConfigurationSource(System.Configuration.ConfigurationManager.AppSettings)
#endif
};
return new TracerSettings(configurationSource);
}
private static TracesExporter ParseTracesExporter(IConfigurationSource source)
{
var tracesExporterEnvVar = source.GetString(ConfigurationKeys.Traces.Exporter)

View File

@ -23,6 +23,7 @@ using OpenTelemetry.AutoInstrumentation.Diagnostics;
using OpenTelemetry.AutoInstrumentation.Loading;
#endif
using OpenTelemetry.AutoInstrumentation.Logging;
using OpenTelemetry.AutoInstrumentation.Plugins;
using OpenTelemetry.Context.Propagation;
using OpenTelemetry.Metrics;
using OpenTelemetry.Shims.OpenTracing;
@ -47,6 +48,7 @@ internal static class Instrumentation
private static TracerProvider _tracerProvider;
private static MeterProvider _meterProvider;
private static PluginManager _pluginManager;
/// <summary>
/// Gets a value indicating whether OpenTelemetry's profiler is attached to the current process.
@ -69,15 +71,21 @@ internal static class Instrumentation
}
}
internal static PluginManager PluginManager => _pluginManager;
#if NETCOREAPP3_1_OR_GREATER
internal static ILifespanManager LifespanManager => LazyInstrumentationLoader.LifespanManager;
#endif
internal static TracerSettings TracerSettings { get; } = TracerSettings.FromDefaultSources();
internal static GeneralSettings GeneralSettings { get; } = Settings.FromDefaultSources<GeneralSettings>();
internal static MetricSettings MetricSettings { get; } = MetricSettings.FromDefaultSources();
internal static TracerSettings TracerSettings { get; } = Settings.FromDefaultSources<TracerSettings>();
internal static SdkSettings SdkSettings { get; } = SdkSettings.FromDefaultSources();
internal static MetricSettings MetricSettings { get; } = Settings.FromDefaultSources<MetricSettings>();
internal static LogSettings LogSettings { get; } = Settings.FromDefaultSources<LogSettings>();
internal static SdkSettings SdkSettings { get; } = Settings.FromDefaultSources<SdkSettings>();
/// <summary>
/// Initialize the OpenTelemetry SDK with a pre-defined set of exporters, shims, and
@ -93,6 +101,8 @@ internal static class Instrumentation
try
{
_pluginManager = new PluginManager(GeneralSettings);
if (TracerSettings.TracesEnabled || MetricSettings.MetricsEnabled)
{
// Initialize SdkSelfDiagnosticsEventListener to create an EventListener for the OpenTelemetry SDK
@ -102,7 +112,7 @@ internal static class Instrumentation
AppDomain.CurrentDomain.ProcessExit += OnExit;
AppDomain.CurrentDomain.DomainUnload += OnExit;
if (TracerSettings.FlushOnUnhandledException || MetricSettings.FlushOnUnhandledException)
if (GeneralSettings.FlushOnUnhandledException)
{
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
}
@ -120,20 +130,20 @@ internal static class Instrumentation
if (TracerSettings.EnabledInstrumentations.Contains(TracerInstrumentation.AspNet))
{
LazyInstrumentationLoader.Add(new AspNetCoreInitializer());
LazyInstrumentationLoader.Add(new AspNetCoreInitializer(_pluginManager));
}
if (TracerSettings.EnabledInstrumentations.Contains(TracerInstrumentation.MySqlData))
{
LazyInstrumentationLoader.Add(new MySqlDataInitializer());
LazyInstrumentationLoader.Add(new MySqlDataInitializer(_pluginManager));
}
#endif
var builder = Sdk
.CreateTracerProviderBuilder()
.SetResourceBuilder(ResourceFactory.Create())
.UseEnvironmentVariables(TracerSettings)
.InvokePlugins(TracerSettings.Plugins);
.UseEnvironmentVariables(TracerSettings, _pluginManager)
.InvokePlugins(_pluginManager);
_tracerProvider = builder.Build();
Logger.Information("OpenTelemetry tracer initialized.");
@ -152,8 +162,8 @@ internal static class Instrumentation
var builder = Sdk
.CreateMeterProviderBuilder()
.SetResourceBuilder(ResourceFactory.Create())
.UseEnvironmentVariables(MetricSettings)
.InvokePlugins(MetricSettings.Plugins);
.UseEnvironmentVariables(MetricSettings, _pluginManager)
.InvokePlugins(_pluginManager);
_meterProvider = builder.Build();
Logger.Information("OpenTelemetry meter initialized.");

View File

@ -16,14 +16,18 @@
#if NETCOREAPP3_1_OR_GREATER
using System;
using OpenTelemetry.AutoInstrumentation.Plugins;
namespace OpenTelemetry.AutoInstrumentation.Loading;
internal class AspNetCoreInitializer : InstrumentationInitializer
{
public AspNetCoreInitializer()
private readonly PluginManager _pluginManager;
public AspNetCoreInitializer(PluginManager pluginManager)
: base("Microsoft.AspNetCore.Http")
{
_pluginManager = pluginManager;
}
public override void Initialize(ILifespanManager lifespanManager)
@ -31,7 +35,10 @@ internal class AspNetCoreInitializer : InstrumentationInitializer
var instrumentationType = Type.GetType("OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreInstrumentation, OpenTelemetry.Instrumentation.AspNetCore");
var httpInListenerType = Type.GetType("OpenTelemetry.Instrumentation.AspNetCore.Implementation.HttpInListener, OpenTelemetry.Instrumentation.AspNetCore");
var httpInListener = Activator.CreateInstance(httpInListenerType, args: new OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreInstrumentationOptions());
var options = new OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreInstrumentationOptions();
_pluginManager.ConfigureOptions(options);
var httpInListener = Activator.CreateInstance(httpInListenerType, args: options);
var instrumentation = Activator.CreateInstance(instrumentationType, args: httpInListener);
lifespanManager.Track(instrumentation);

View File

@ -17,22 +17,27 @@
#if NETCOREAPP3_1_OR_GREATER
using System;
using OpenTelemetry.AutoInstrumentation.Plugins;
namespace OpenTelemetry.AutoInstrumentation.Loading;
internal class MySqlDataInitializer : InstrumentationInitializer
{
public MySqlDataInitializer()
private readonly PluginManager _pluginManager;
public MySqlDataInitializer(PluginManager pluginManager)
: base("MySql.Data")
{
_pluginManager = pluginManager;
}
public override void Initialize(ILifespanManager lifespanManager)
{
var instrumentationType = Type.GetType("OpenTelemetry.Instrumentation.MySqlData.MySqlDataInstrumentation, OpenTelemetry.Instrumentation.MySqlData");
var optionsInstrumentationType = Type.GetType("OpenTelemetry.Instrumentation.MySqlData.MySqlDataInstrumentationOptions, OpenTelemetry.Instrumentation.MySqlData");
var options = Activator.CreateInstance(optionsInstrumentationType);
var options = new OpenTelemetry.Instrumentation.MySqlData.MySqlDataInstrumentationOptions();
_pluginManager.ConfigureOptions(options);
var instrumentation = Activator.CreateInstance(instrumentationType, options);
lifespanManager.Track(instrumentation);

View File

@ -43,21 +43,20 @@ internal static class LogBuilderExtensions
return builder;
}
var settings = LogSettings.FromDefaultSources();
var settings = Instrumentation.LogSettings;
var pluginManager = Instrumentation.PluginManager;
builder.AddOpenTelemetry(options =>
{
options.SetResourceBuilder(ResourceFactory.Create());
options.IncludeFormattedMessage = settings.IncludeFormattedMessage;
if (settings.Plugins.Count > 0)
{
options.InvokePlugins(settings.Plugins);
}
pluginManager.ConfigureOptions(options);
if (settings.ConsoleExporterEnabled)
{
options.AddConsoleExporter();
options.AddConsoleExporter(pluginManager.ConfigureOptions);
}
switch (settings.LogExporter)
@ -72,12 +71,14 @@ internal static class LogBuilderExtensions
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
}
#endif
options.AddOtlpExporter(options =>
options.AddOtlpExporter(otlpOptions =>
{
if (settings.OtlpExportProtocol.HasValue)
{
options.Protocol = settings.OtlpExportProtocol.Value;
otlpOptions.Protocol = settings.OtlpExportProtocol.Value;
}
pluginManager.ConfigureOptions(otlpOptions);
});
break;
case LogExporter.None:

View File

@ -0,0 +1,79 @@
// <copyright file="PluginManager.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.Collections.Generic;
using OpenTelemetry.AutoInstrumentation.Configuration;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;
namespace OpenTelemetry.AutoInstrumentation.Plugins;
internal class PluginManager
{
private readonly IReadOnlyList<(Type Type, object Instance)> _plugins;
public PluginManager(GeneralSettings settings)
{
var plugins = new List<(Type, object)>();
foreach (var assemblyQualifiedName in settings.Plugins)
{
var type = Type.GetType(assemblyQualifiedName, throwOnError: true);
var instance = Activator.CreateInstance(type);
plugins.Add((type, instance));
}
_plugins = plugins;
}
public TracerProviderBuilder ConfigureTracerProviderBuilder(TracerProviderBuilder builder)
{
return ConfigureBuilder(builder, "ConfigureTracerProvider");
}
public MeterProviderBuilder ConfigureMeterProviderBuilder(MeterProviderBuilder builder)
{
return ConfigureBuilder(builder, "ConfigureMeterProvider");
}
public void ConfigureOptions<T>(T options)
{
foreach (var plugin in _plugins)
{
var mi = plugin.Type.GetMethod("ConfigureOptions", new Type[] { typeof(T) });
if (mi is not null)
{
mi.Invoke(plugin.Instance, new object[] { options });
}
}
}
private T ConfigureBuilder<T>(T builder, string methodName)
{
foreach (var plugin in _plugins)
{
var mi = plugin.Type.GetMethod(methodName, new Type[] { typeof(T) });
if (mi is not null)
{
builder = (T)mi.Invoke(plugin.Instance, new object[] { builder });
}
}
return builder;
}
}

View File

@ -14,6 +14,7 @@
// limitations under the License.
// </copyright>
using System.Linq;
using System.Threading.Tasks;
using IntegrationTests.Helpers;
using Xunit;
@ -35,6 +36,11 @@ public class PluginsTests : TestHelper
using var collector = await MockSpansCollector.Start(Output);
SetExporter(collector);
collector.Expect("MyCompany.MyProduct.MyLibrary");
#if NETFRAMEWORK
collector.Expect("OpenTelemetry.HttpWebRequest", span => span.Attributes.Any(att => att.Key == "example.plugin"));
#else
collector.Expect("OpenTelemetry.Instrumentation.Http", span => span.Attributes.Any(att => att.Key == "example.plugin"));
#endif
SetEnvironmentVariable("OTEL_DOTNET_AUTO_PLUGINS", "TestApplication.Plugins.Plugin, TestApplication.Plugins, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
RunTestApplication();

View File

@ -0,0 +1,184 @@
// <copyright file="PluginManagerTests.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.Collections.Specialized;
using System.IO;
using FluentAssertions;
using FluentAssertions.Execution;
using Microsoft.Extensions.Logging;
using Moq;
using OpenTelemetry.AutoInstrumentation.Configuration;
using OpenTelemetry.AutoInstrumentation.Plugins;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;
using Xunit;
namespace OpenTelemetry.AutoInstrumentation.Tests.Configuration;
public class PluginManagerTests
{
[Fact]
public void MissingAssembly()
{
var pluginAssemblyQualifiedName = "Missing.Assembly.PluginType, Missing.Assembly";
var settings = GetSettings(pluginAssemblyQualifiedName);
var createAction = () => new PluginManager(settings);
using (new AssertionScope())
{
createAction.Should().Throw<FileNotFoundException>();
}
}
[Fact]
public void MissingPluginTypeFromAssembly()
{
var pluginAssemblyQualifiedName = "Missing.PluginType";
var settings = GetSettings(pluginAssemblyQualifiedName);
var createAction = () => new PluginManager(settings);
using (new AssertionScope())
{
createAction.Should().Throw<TypeLoadException>();
}
}
[Fact]
public void PluginTypeMissingMethodDoesNotThrow()
{
var pluginAssemblyQualifiedName = GetType().AssemblyQualifiedName;
var settings = GetSettings(pluginAssemblyQualifiedName);
var pluginManager = new PluginManager(settings);
var tracerAction = () => Sdk.CreateTracerProviderBuilder().InvokePlugins(pluginManager);
var meterAction = () => Sdk.CreateMeterProviderBuilder().InvokePlugins(pluginManager);
using (new AssertionScope())
{
tracerAction.Should().NotThrow();
meterAction.Should().NotThrow();
}
}
[Fact]
public void PluginTypeMissingDefaultConstructor()
{
var pluginAssemblyQualifiedName = typeof(MockPluginMissingDefaultConstructor).AssemblyQualifiedName;
var settings = GetSettings(pluginAssemblyQualifiedName);
var createAction = () => new PluginManager(settings);
using (new AssertionScope())
{
createAction.Should().Throw<MissingMethodException>();
}
}
[Fact]
public void InvokeProviderPluginSuccess()
{
var pluginAssemblyQualifiedName = typeof(MockPlugin).AssemblyQualifiedName;
var settings = GetSettings(pluginAssemblyQualifiedName);
var pluginManager = new PluginManager(settings);
var traceProviderBuilderMock = new Mock<TracerProviderBuilder>();
traceProviderBuilderMock.Setup(x => x.AddSource(It.Is<string>(x => x == "My.Custom.Source"))).Verifiable();
var meterProviderBuilderMock = new Mock<MeterProviderBuilder>();
meterProviderBuilderMock.Setup(x => x.AddMeter(It.Is<string>(x => x == "My.Custom.Meter"))).Verifiable();
var tracerAction = () => traceProviderBuilderMock.Object.InvokePlugins(pluginManager);
var meterAction = () => meterProviderBuilderMock.Object.InvokePlugins(pluginManager);
using (new AssertionScope())
{
tracerAction.Should().NotThrow();
meterAction.Should().NotThrow();
traceProviderBuilderMock.Verify();
meterProviderBuilderMock.Verify();
}
}
[Fact]
public void ConfigureOptionsSuccess()
{
var pluginAssemblyQualifiedName = typeof(MockPlugin).AssemblyQualifiedName;
var settings = GetSettings(pluginAssemblyQualifiedName);
var pluginManager = new PluginManager(settings);
var logsAction = () => LoggerFactory.Create(builder =>
{
builder.AddOpenTelemetry(options =>
{
options.IncludeFormattedMessage = false;
pluginManager.ConfigureOptions(options);
// Verify that plugin changes the state
options.IncludeFormattedMessage.Should().BeTrue();
});
});
using (new AssertionScope())
{
logsAction.Should().NotThrow();
}
}
private static GeneralSettings GetSettings(string assemblyQualifiedName)
{
var config = new NameValueConfigurationSource(new NameValueCollection()
{
{ ConfigurationKeys.ProviderPlugins, assemblyQualifiedName }
});
return new GeneralSettings(config);
}
public class MockPlugin
{
public TracerProviderBuilder ConfigureTracerProvider(TracerProviderBuilder builder)
{
builder.AddSource("My.Custom.Source");
return builder;
}
public MeterProviderBuilder ConfigureMeterProvider(MeterProviderBuilder builder)
{
builder.AddMeter("My.Custom.Meter");
return builder;
}
public OpenTelemetryLoggerOptions ConfigureOptions(OpenTelemetryLoggerOptions options)
{
// Dummy overwritten setting
options.IncludeFormattedMessage = true;
return options;
}
}
public class MockPluginMissingDefaultConstructor : MockPlugin
{
public MockPluginMissingDefaultConstructor(string ignored)
{
throw new InvalidOperationException("this plugin is not expected to be successfully constructed");
}
}
}

View File

@ -1,165 +0,0 @@
// <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 FluentAssertions.Execution;
using Microsoft.Extensions.Logging;
using OpenTelemetry.AutoInstrumentation.Configuration;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;
using Xunit;
namespace OpenTelemetry.AutoInstrumentation.Tests.Configuration;
public class PluginsConfigurationHelperTests
{
[Fact]
public void MissingAssembly()
{
var tracerAction = () => Sdk.CreateTracerProviderBuilder().InvokePlugins(new[] { "Missing.Assembly.PluginType, Missing.Assembly" });
var meterAction = () => Sdk.CreateMeterProviderBuilder().InvokePlugins(new[] { "Missing.Assembly.PluginType, Missing.Assembly" });
var logsAction = () => LoggerFactory.Create(builder =>
{
builder.AddOpenTelemetry(options =>
{
options.InvokePlugins(new[] { "Missing.Assembly.PluginType, Missing.Assembly" });
});
});
using (new AssertionScope())
{
tracerAction.Should().Throw<FileNotFoundException>();
meterAction.Should().Throw<FileNotFoundException>();
logsAction.Should().Throw<FileNotFoundException>();
}
}
[Fact]
public void MissingPluginTypeFromAssembly()
{
var tracerAction = () => Sdk.CreateTracerProviderBuilder().InvokePlugins(new[] { "Missing.PluginType" });
var meterAction = () => Sdk.CreateMeterProviderBuilder().InvokePlugins(new[] { "Missing.PluginType" });
var logsAction = () => LoggerFactory.Create(builder =>
{
builder.AddOpenTelemetry(options =>
{
options.InvokePlugins(new[] { "Missing.PluginType" });
});
});
using (new AssertionScope())
{
tracerAction.Should().Throw<TypeLoadException>();
meterAction.Should().Throw<TypeLoadException>();
logsAction.Should().Throw<TypeLoadException>();
}
}
[Fact]
public void PluginTypeMissingMethodDoesNotThrow()
{
var pluginAssemblyQualifiedName = GetType().AssemblyQualifiedName;
var tracerAction = () => Sdk.CreateTracerProviderBuilder().InvokePlugins(new[] { pluginAssemblyQualifiedName });
var meterAction = () => Sdk.CreateMeterProviderBuilder().InvokePlugins(new[] { pluginAssemblyQualifiedName });
var logsAction = () => LoggerFactory.Create(builder =>
{
builder.AddOpenTelemetry(options =>
{
options.InvokePlugins(new[] { pluginAssemblyQualifiedName });
});
});
using (new AssertionScope())
{
tracerAction.Should().NotThrow();
meterAction.Should().NotThrow();
logsAction.Should().NotThrow();
}
}
[Fact]
public void PluginTypeMissingDefaultConstructor()
{
var pluginAssemblyQualifiedName = typeof(MockPluginMissingDefaultConstructor).AssemblyQualifiedName;
var tracerAction = () => Sdk.CreateTracerProviderBuilder().InvokePlugins(new[] { pluginAssemblyQualifiedName });
var meterAction = () => Sdk.CreateMeterProviderBuilder().InvokePlugins(new[] { pluginAssemblyQualifiedName });
var logsAction = () => LoggerFactory.Create(builder =>
{
builder.AddOpenTelemetry(options =>
{
options.InvokePlugins(new[] { pluginAssemblyQualifiedName });
});
});
using (new AssertionScope())
{
tracerAction.Should().Throw<MissingMethodException>();
meterAction.Should().Throw<MissingMethodException>();
logsAction.Should().Throw<MissingMethodException>();
}
}
[Fact]
public void InvokePluginSuccess()
{
var pluginAssemblyQualifiedName = typeof(MockPlugin).AssemblyQualifiedName;
var tracerAction = () => Sdk.CreateTracerProviderBuilder().InvokePlugins(new[] { pluginAssemblyQualifiedName });
var meterAction = () => Sdk.CreateMeterProviderBuilder().InvokePlugins(new[] { pluginAssemblyQualifiedName });
var logsAction = () => LoggerFactory.Create(builder =>
{
builder.AddOpenTelemetry(options =>
{
options.InvokePlugins(new[] { pluginAssemblyQualifiedName });
});
});
using (new AssertionScope())
{
tracerAction.Should().NotThrow();
meterAction.Should().NotThrow();
logsAction.Should().NotThrow();
}
}
public class MockPlugin
{
public TracerProviderBuilder ConfigureTracerProvider(TracerProviderBuilder builder)
{
return builder;
}
public MeterProviderBuilder ConfigureMeterProvider(MeterProviderBuilder builder)
{
return builder;
}
public OpenTelemetryLoggerOptions ConfigureLoggerOptions(OpenTelemetryLoggerOptions options)
{
return options;
}
}
public class MockPluginMissingDefaultConstructor : MockPlugin
{
public MockPluginMissingDefaultConstructor(string ignored)
{
throw new InvalidOperationException("this plugin is not expected to be successfully constructed");
}
}
}

View File

@ -33,12 +33,12 @@ public class SettingsTests : IDisposable
public static IEnumerable<object[]> ExporterEnvVarAndLoadSettingsAction()
{
yield return new object[] { ConfigurationKeys.Traces.Exporter, new Action(() => TracerSettings.FromDefaultSources()) };
yield return new object[] { ConfigurationKeys.Traces.Instrumentations, new Action(() => TracerSettings.FromDefaultSources()) };
yield return new object[] { ConfigurationKeys.Metrics.Exporter, new Action(() => MetricSettings.FromDefaultSources()) };
yield return new object[] { ConfigurationKeys.Metrics.Instrumentations, new Action(() => MetricSettings.FromDefaultSources()) };
yield return new object[] { ConfigurationKeys.Logs.Exporter, new Action(() => LogSettings.FromDefaultSources()) };
yield return new object[] { ConfigurationKeys.Sdk.Propagators, new Action(() => SdkSettings.FromDefaultSources()) };
yield return new object[] { ConfigurationKeys.Traces.Exporter, new Action(() => Settings.FromDefaultSources<TracerSettings>()) };
yield return new object[] { ConfigurationKeys.Traces.Instrumentations, new Action(() => Settings.FromDefaultSources<TracerSettings>()) };
yield return new object[] { ConfigurationKeys.Metrics.Exporter, new Action(() => Settings.FromDefaultSources<MetricSettings>()) };
yield return new object[] { ConfigurationKeys.Metrics.Instrumentations, new Action(() => Settings.FromDefaultSources<MetricSettings>()) };
yield return new object[] { ConfigurationKeys.Logs.Exporter, new Action(() => Settings.FromDefaultSources<LogSettings>()) };
yield return new object[] { ConfigurationKeys.Sdk.Propagators, new Action(() => Settings.FromDefaultSources<SdkSettings>()) };
}
public void Dispose()
@ -46,10 +46,24 @@ public class SettingsTests : IDisposable
ClearEnvVars();
}
[Fact]
internal void GeneralSettings_DefaultValues()
{
var settings = Settings.FromDefaultSources<GeneralSettings>();
using (new AssertionScope())
{
settings.Plugins.Should().BeEmpty();
settings.FlushOnUnhandledException.Should().BeFalse();
settings.OtlpExportProtocol.Should().Be(OtlpExportProtocol.HttpProtobuf);
settings.Http2UnencryptedSupportEnabled.Should().BeFalse();
}
}
[Fact]
internal void TracerSettings_DefaultValues()
{
var settings = TracerSettings.FromDefaultSources();
var settings = Settings.FromDefaultSources<TracerSettings>();
using (new AssertionScope())
{
@ -58,11 +72,9 @@ public class SettingsTests : IDisposable
settings.OtlpExportProtocol.Should().Be(OtlpExportProtocol.HttpProtobuf);
settings.ConsoleExporterEnabled.Should().BeFalse();
settings.EnabledInstrumentations.Should().NotBeEmpty();
settings.Plugins.Should().BeEmpty();
settings.ActivitySources.Should().BeEquivalentTo(new List<string> { "OpenTelemetry.AutoInstrumentation.*" });
settings.LegacySources.Should().BeEmpty();
settings.Http2UnencryptedSupportEnabled.Should().BeFalse();
settings.FlushOnUnhandledException.Should().BeFalse();
// Instrumentation options tests
settings.InstrumentationOptions.GraphQLSetDocument.Should().BeFalse();
@ -72,7 +84,7 @@ public class SettingsTests : IDisposable
[Fact]
internal void MeterSettings_DefaultValues()
{
var settings = MetricSettings.FromDefaultSources();
var settings = Settings.FromDefaultSources<MetricSettings>();
using (new AssertionScope())
{
@ -81,17 +93,15 @@ public class SettingsTests : IDisposable
settings.OtlpExportProtocol.Should().Be(OtlpExportProtocol.HttpProtobuf);
settings.ConsoleExporterEnabled.Should().BeFalse();
settings.EnabledInstrumentations.Should().NotBeEmpty();
settings.Plugins.Should().BeEmpty();
settings.Meters.Should().BeEmpty();
settings.Http2UnencryptedSupportEnabled.Should().BeFalse();
settings.FlushOnUnhandledException.Should().BeFalse();
}
}
[Fact]
internal void LogSettings_DefaultValues()
{
var settings = LogSettings.FromDefaultSources();
var settings = Settings.FromDefaultSources<LogSettings>();
using (new AssertionScope())
{
@ -99,17 +109,15 @@ public class SettingsTests : IDisposable
settings.OtlpExportProtocol.Should().Be(OtlpExportProtocol.HttpProtobuf);
settings.ConsoleExporterEnabled.Should().BeFalse();
settings.EnabledInstrumentations.Should().NotBeEmpty();
settings.Plugins.Should().BeEmpty();
settings.IncludeFormattedMessage.Should().BeFalse();
settings.Http2UnencryptedSupportEnabled.Should().BeFalse();
settings.FlushOnUnhandledException.Should().BeFalse();
}
}
[Fact]
internal void SdkSettings_DefaultValues()
{
var settings = SdkSettings.FromDefaultSources();
var settings = Settings.FromDefaultSources<SdkSettings>();
using (new AssertionScope())
{
@ -126,7 +134,7 @@ public class SettingsTests : IDisposable
{
Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.Exporter, tracesExporter);
var settings = TracerSettings.FromDefaultSources();
var settings = Settings.FromDefaultSources<TracerSettings>();
settings.TracesExporter.Should().Be(expectedTracesExporter);
}
@ -139,7 +147,7 @@ public class SettingsTests : IDisposable
{
Environment.SetEnvironmentVariable(ConfigurationKeys.Metrics.Exporter, metricExporter);
var settings = MetricSettings.FromDefaultSources();
var settings = Settings.FromDefaultSources<MetricSettings>();
settings.MetricExporter.Should().Be(expectedMetricsExporter);
}
@ -151,7 +159,7 @@ public class SettingsTests : IDisposable
{
Environment.SetEnvironmentVariable(ConfigurationKeys.Logs.Exporter, logExporter);
var settings = LogSettings.FromDefaultSources();
var settings = Settings.FromDefaultSources<LogSettings>();
settings.LogExporter.Should().Be(expectedLogExporter);
}
@ -167,7 +175,7 @@ public class SettingsTests : IDisposable
{
Environment.SetEnvironmentVariable(ConfigurationKeys.Sdk.Propagators, propagators);
var settings = SdkSettings.FromDefaultSources();
var settings = Settings.FromDefaultSources<SdkSettings>();
settings.Propagators.Should().BeEquivalentTo(expectedPropagators);
}
@ -191,7 +199,7 @@ public class SettingsTests : IDisposable
{
Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.Instrumentations, tracerInstrumentation);
var settings = TracerSettings.FromDefaultSources();
var settings = Settings.FromDefaultSources<TracerSettings>();
settings.EnabledInstrumentations.Should().BeEquivalentTo(new List<TracerInstrumentation> { expectedTracerInstrumentation });
}
@ -202,7 +210,7 @@ public class SettingsTests : IDisposable
Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.Instrumentations, $"{nameof(TracerInstrumentation.AspNet)},{nameof(TracerInstrumentation.GraphQL)}");
Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.DisabledInstrumentations, nameof(TracerInstrumentation.GraphQL));
var settings = TracerSettings.FromDefaultSources();
var settings = Settings.FromDefaultSources<TracerSettings>();
settings.EnabledInstrumentations.Should().BeEquivalentTo(new List<TracerInstrumentation> { TracerInstrumentation.AspNet });
}
@ -216,7 +224,7 @@ public class SettingsTests : IDisposable
{
Environment.SetEnvironmentVariable(ConfigurationKeys.Metrics.Instrumentations, meterInstrumentation);
var settings = MetricSettings.FromDefaultSources();
var settings = Settings.FromDefaultSources<MetricSettings>();
settings.EnabledInstrumentations.Should().BeEquivalentTo(new List<MetricInstrumentation> { expectedMetricInstrumentation });
}
@ -227,7 +235,7 @@ public class SettingsTests : IDisposable
Environment.SetEnvironmentVariable(ConfigurationKeys.Metrics.Instrumentations, $"{nameof(MetricInstrumentation.NetRuntime)},{nameof(MetricInstrumentation.AspNet)}");
Environment.SetEnvironmentVariable(ConfigurationKeys.Metrics.DisabledInstrumentations, nameof(MetricInstrumentation.AspNet));
var settings = MetricSettings.FromDefaultSources();
var settings = Settings.FromDefaultSources<MetricSettings>();
settings.EnabledInstrumentations.Should().BeEquivalentTo(new List<MetricInstrumentation> { MetricInstrumentation.NetRuntime });
}
@ -238,7 +246,7 @@ public class SettingsTests : IDisposable
{
Environment.SetEnvironmentVariable(ConfigurationKeys.Metrics.Instrumentations, logInstrumentation);
var settings = LogSettings.FromDefaultSources();
var settings = Settings.FromDefaultSources<LogSettings>();
settings.EnabledInstrumentations.Should().BeEquivalentTo(new List<LogInstrumentation> { expectedLogInstrumentation });
}
@ -248,7 +256,7 @@ public class SettingsTests : IDisposable
{
Environment.SetEnvironmentVariable(ConfigurationKeys.Logs.DisabledInstrumentations, nameof(LogInstrumentation.ILogger));
var settings = LogSettings.FromDefaultSources();
var settings = Settings.FromDefaultSources<LogSettings>();
settings.EnabledInstrumentations.Should().BeEmpty();
}
@ -260,7 +268,7 @@ public class SettingsTests : IDisposable
{
Environment.SetEnvironmentVariable(ConfigurationKeys.Logs.IncludeFormattedMessage, includeFormattedMessage);
var settings = LogSettings.FromDefaultSources();
var settings = Settings.FromDefaultSources<LogSettings>();
settings.IncludeFormattedMessage.Should().Be(expectedValue);
}
@ -283,7 +291,7 @@ public class SettingsTests : IDisposable
{
Environment.SetEnvironmentVariable(ConfigurationKeys.ExporterOtlpProtocol, otlpProtocol);
var settings = TracerSettings.FromDefaultSources();
var settings = Settings.FromDefaultSources<TracerSettings>();
// null values for expected data will be handled by OTel .NET SDK
settings.OtlpExportProtocol.Should().Be(expectedOtlpExportProtocol);
@ -297,7 +305,7 @@ public class SettingsTests : IDisposable
{
Environment.SetEnvironmentVariable(ConfigurationKeys.Http2UnencryptedSupportEnabled, http2UnencryptedSupportEnabled);
var settings = TracerSettings.FromDefaultSources();
var settings = Settings.FromDefaultSources<TracerSettings>();
settings.Http2UnencryptedSupportEnabled.Should().Be(expectedValue);
}
@ -310,7 +318,7 @@ public class SettingsTests : IDisposable
{
Environment.SetEnvironmentVariable(ConfigurationKeys.FlushOnUnhandledException, flushOnUnhandledException);
var settings = TracerSettings.FromDefaultSources();
var settings = Settings.FromDefaultSources<GeneralSettings>();
settings.FlushOnUnhandledException.Should().Be(expectedValue);
}

View File

@ -14,6 +14,7 @@
// limitations under the License.
// </copyright>
using OpenTelemetry.Instrumentation.Http;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;
@ -30,4 +31,28 @@ public class Plugin
{
return builder.AddMeter(TestApplication.Smoke.Program.SourceName);
}
public void ConfigureOptions(HttpClientInstrumentationOptions options)
{
options.Enrich = (activity, eventName, rawObject) =>
{
if (eventName.Equals("OnStartActivity"))
{
activity.SetTag("example.plugin", "MyExamplePlugin");
}
};
}
#if NETFRAMEWORK
public void ConfigureOptions(HttpWebRequestInstrumentationOptions options)
{
options.Enrich = (activity, eventName, rawObject) =>
{
if (eventName.Equals("OnStartActivity"))
{
activity.SetTag("example.plugin", "MyExamplePlugin");
}
};
}
#endif
}

View File

@ -5,6 +5,7 @@
<ItemGroup>
<PackageReference Include="OpenTelemetry" Version="1.3.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.0.0-rc9.4" />
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="6.0.0" />
</ItemGroup>