-
Notifications
You must be signed in to change notification settings - Fork 2
/
ElasticOpenTelemetryBuilder.cs
178 lines (150 loc) · 6.8 KB
/
ElasticOpenTelemetryBuilder.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information
using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.Reflection;
using Elastic.OpenTelemetry.Configuration;
using Elastic.OpenTelemetry.Diagnostics;
using Elastic.OpenTelemetry.Diagnostics.Logging;
using Elastic.OpenTelemetry.Extensions;
using Elastic.OpenTelemetry.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using OpenTelemetry;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;
using static Elastic.OpenTelemetry.Configuration.ElasticOpenTelemetryOptions;
namespace Elastic.OpenTelemetry;
/// <summary>
/// An implementation of <see cref="IOpenTelemetryBuilder" /> which configures Elastic defaults, but can also be customised.
/// </summary>
/// <remarks>
/// Currently this builder enables both tracing and metrics, and configures the following:
/// <list type="bullet">
/// <item>
/// <term>Instrumentation</term>
/// <description>Enables commonly used instrumentation such as HTTP, gRPC and EntityFramework.</description>
/// </item>
/// <item>
/// <term>Processors</term>
/// <description>Enables Elastic processors to add additional features and to
/// ensure data is compatible with Elastic backends.</description>
/// </item>
/// <item>
/// <term>OTLP Exporter</term>
/// <description>Enables exporting of signals over OTLP to a configured endpoint(s).</description>
/// </item>
/// </list>
/// </remarks>
public class ElasticOpenTelemetryBuilder : IOpenTelemetryBuilder
{
internal CompositeLogger Logger { get; }
internal LoggingEventListener EventListener { get; }
/// <inheritdoc cref="IOpenTelemetryBuilder.Services"/>
public IServiceCollection Services { get; }
/// <summary>
/// Shared bootstrap routine for the Elastic Distribution of OpenTelemetry .NET.
/// Used to ensure auto instrumentation and manual instrumentation bootstrap the same way.
/// </summary>
public static (EventListener, ILogger) Bootstrap(ElasticOpenTelemetryBuilderOptions options)
{
var logger = new CompositeLogger(options);
// Enables logging of OpenTelemetry-SDK event source events
var eventListener = new LoggingEventListener(logger, options.DistroOptions);
logger.LogAgentPreamble();
logger.LogElasticOpenTelemetryBuilderInitialized(Environment.NewLine, new StackTrace(true));
options.DistroOptions.LogConfigSources(logger);
return (eventListener, logger);
}
/// <summary>
/// Creates an instance of the <see cref="ElasticOpenTelemetryBuilder" /> configured with default options.
/// </summary>
public ElasticOpenTelemetryBuilder()
: this(new ElasticOpenTelemetryBuilderOptions())
{ }
/// <summary>
/// Creates an instance of the <see cref="ElasticOpenTelemetryBuilder" /> configured with the provided
/// <see cref="ElasticOpenTelemetryBuilderOptions"/>.
/// </summary>
public ElasticOpenTelemetryBuilder(ElasticOpenTelemetryBuilderOptions options)
{
var (eventListener, logger) = Bootstrap(options);
Logger = (CompositeLogger)logger;
EventListener = (LoggingEventListener)eventListener;
Services = options.Services ?? new ServiceCollection();
if (options.Services is not null && !options.Services.Any(d => d.ImplementationType == typeof(ElasticOpenTelemetryService)))
Services.Insert(0, ServiceDescriptor.Singleton<IHostedService, ElasticOpenTelemetryService>());
Services.TryAddSingleton(this);
// Directly invoke the SDK extension method to ensure SDK components are registered.
var openTelemetry =
Microsoft.Extensions.DependencyInjection.OpenTelemetryServicesExtensions.AddOpenTelemetry(Services);
// We always add this so we can identify a distro is being used, even if all Elastic defaults are disabled.
openTelemetry.ConfigureResource(r => r.UseElasticDefaults());
if (options.DistroOptions.ElasticDefaults.Equals(ElasticDefaults.None))
{
Logger.LogNoElasticDefaults();
// We always add the distro attribute so that we can identify a distro is being used, even if all Elastic defaults are disabled.
openTelemetry.ConfigureResource(r => r.AddDistroAttributes());
return;
}
openTelemetry.ConfigureResource(r => r.UseElasticDefaults(Logger));
var distro = options.DistroOptions;
//https://github.com/open-telemetry/opentelemetry-dotnet/pull/5400
if (!distro.SkipOtlpExporter)
openTelemetry.UseOtlpExporter();
if (distro.Signals.HasFlag(Signals.Logs))
{
//TODO Move to WithLogging once it gets stable
Services.Configure<OpenTelemetryLoggerOptions>(logging =>
{
if (distro.ElasticDefaults.HasFlag(ElasticDefaults.Logs))
logging.UseElasticDefaults();
else
Logger.LogDefaultsDisabled(nameof(ElasticDefaults.Logs));
});
}
else
Logger.LogSignalDisabled(nameof(Signals.Logs));
if (distro.Signals.HasFlag(Signals.Traces))
{
openTelemetry.WithTracing(tracing =>
{
if (distro.ElasticDefaults.HasFlag(ElasticDefaults.Traces))
tracing.UseElasticDefaults(Logger);
else
Logger.LogDefaultsDisabled(nameof(ElasticDefaults.Traces));
});
}
else
Logger.LogSignalDisabled(nameof(Signals.Metrics));
if (distro.Signals.HasFlag(Signals.Metrics))
{
openTelemetry.WithMetrics(metrics =>
{
if (distro.ElasticDefaults.HasFlag(ElasticDefaults.Metrics))
metrics.UseElasticDefaults(Logger);
else
Logger.LogDefaultsDisabled(nameof(ElasticDefaults.Metrics));
});
}
else
Logger.LogSignalDisabled(nameof(Signals.Metrics));
}
}
internal static partial class LoggerMessages
{
[LoggerMessage(EventId = 0, Level = LogLevel.Information, Message = "ElasticOpenTelemetryBuilder initialized{newline}{StackTrace}.")]
public static partial void LogElasticOpenTelemetryBuilderInitialized(this ILogger logger, string newline, StackTrace stackTrace);
[LoggerMessage(EventId = 1, Level = LogLevel.Debug, Message = "ElasticOpenTelemetryBuilder configured {Signal} via the {Provider}.")]
public static partial void LogConfiguredSignalProvider(this ILogger logger, string signal, string provider);
[LoggerMessage(EventId = 2, Level = LogLevel.Information, Message = "No Elastic defaults were enabled.")]
public static partial void LogNoElasticDefaults(this ILogger logger);
[LoggerMessage(EventId = 1, Level = LogLevel.Information, Message = "ElasticOpenTelemetryBuilder {Signal} skipped, configured to be disabled")]
public static partial void LogSignalDisabled(this ILogger logger, string signal);
[LoggerMessage(EventId = 1, Level = LogLevel.Information, Message = "Elastic defaults for {Signal} skipped, configured to be disabled")]
public static partial void LogDefaultsDisabled(this ILogger logger, string signal);
}