• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

thorstenalpers / OpenTelemetryExtension.Configuration / 27031086476

05 Jun 2026 05:52PM UTC coverage: 92.574% (-1.3%) from 93.878%
27031086476

push

github

thor
chore(sample): remove Endpoint from appsettings

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

48 of 62 branches covered (77.42%)

Branch coverage included in aggregate %.

139 of 140 relevant lines covered (99.29%)

36.76 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.67
/src/OpenTelemetryExtension.Configuration/TelemetryServiceCollectionExtensions.cs
1
namespace OpenTelemetryExtension.Configuration;
2

3
using System;
4
using System.ComponentModel.DataAnnotations;
5
using System.Linq;
6
using System.Reflection;
7
using Microsoft.Extensions.Configuration;
8
using Microsoft.Extensions.DependencyInjection;
9
using Microsoft.Extensions.Logging;
10
using OpenTelemetry.Exporter;
11
using OpenTelemetry.Logs;
12
using OpenTelemetry.Metrics;
13
using OpenTelemetry.Resources;
14
using OpenTelemetry.Trace;
15

16
/// <summary>
17
/// Extension methods for registering OpenTelemetry services on <see cref="IServiceCollection"/>.
18
/// </summary>
19
/// <remarks>
20
/// Two registration overloads are provided:
21
/// <list type="bullet">
22
///   <item><description><see cref="AddTelemetry(IServiceCollection, IConfiguration)"/> — binds from <c>appsettings.json</c>.</description></item>
23
///   <item><description><see cref="AddTelemetry(IServiceCollection, Action{TelemetryOptions})"/> — configures inline in code.</description></item>
24
/// </list>
25
/// When <see cref="TelemetryOptions.Enabled"/> is <c>false</c> no OpenTelemetry
26
/// services are registered and the method returns immediately.
27
/// </remarks>
28
public static class TelemetryServiceCollectionExtensions
29
{
30
    /// <summary>
31
    /// Registers OpenTelemetry tracing, metrics and logging using values
32
    /// from the <c>Telemetry</c> section of <paramref name="configuration"/>.
33
    /// </summary>
34
    /// <param name="services">The <see cref="IServiceCollection"/> to add services to.</param>
35
    /// <param name="configuration">
36
    /// The application configuration. Must contain a <c>Telemetry</c> section
37
    /// matching <see cref="TelemetryOptions"/>. See <see cref="TelemetryOptions.SectionName"/>.
38
    /// </param>
39
    /// <returns>The original <paramref name="services"/> for chaining.</returns>
40
    /// <exception cref="InvalidOperationException">
41
    /// Thrown when the <c>Telemetry</c> configuration section is missing or empty.
42
    /// </exception>
43
    /// <exception cref="ValidationException">
44
    /// Thrown when the bound <see cref="TelemetryOptions"/> fail validation,
45
    /// e.g. when <see cref="TelemetryOptions.Endpoint"/> is <c>null</c>.
46
    /// </exception>
47
    public static IServiceCollection AddTelemetry(this IServiceCollection services, IConfiguration configuration)
48
    {
49
        var section = configuration.GetSection(TelemetryOptions.SectionName);
8✔
50
        if (!section.Exists())
8✔
51
        {
52
            throw new InvalidOperationException($"Configuration section '{TelemetryOptions.SectionName}' is missing.");
1✔
53
        }
54

55
        var options = new TelemetryOptions();
7✔
56
        section.Bind(options);
7✔
57

58
        if (options.Enabled)
7✔
59
        {
60
            Validator.ValidateObject(options, new ValidationContext(options), validateAllProperties: true);
2✔
61
        }
62

63
        ConfigureTelemetry(services, options);
6✔
64
        return services;
6✔
65
    }
66

67
    /// <summary>
68
    /// Registers OpenTelemetry tracing, metrics and logging using a configuration callback.
69
    /// </summary>
70
    /// <param name="services">The <see cref="IServiceCollection"/> to add services to.</param>
71
    /// <param name="configure">
72
    /// A delegate to configure <see cref="TelemetryOptions"/>.
73
    /// At minimum <see cref="TelemetryOptions.Endpoint"/> must be set.
74
    /// </param>
75
    /// <returns>The original <paramref name="services"/> for chaining.</returns>
76
    /// <exception cref="ArgumentNullException">
77
    /// Thrown when <paramref name="configure"/> is <c>null</c>.
78
    /// </exception>
79
    /// <exception cref="ValidationException">
80
    /// Thrown when the configured <see cref="TelemetryOptions"/> fail validation,
81
    /// e.g. when <see cref="TelemetryOptions.Endpoint"/> is <c>null</c>.
82
    /// </exception>
83
    /// <example>
84
    /// <code>
85
    /// services.AddTelemetry(o =>
86
    /// {
87
    ///     o.Endpoint    = new Uri("http://localhost:4318");
88
    ///     o.ServiceName = "my-api";
89
    ///     o.ConfigureTracing = tracing => tracing.AddSource("MyApp");
90
    /// });
91
    /// </code>
92
    /// </example>
93
    public static IServiceCollection AddTelemetry(this IServiceCollection services, Action<TelemetryOptions> configure)
94
    {
95
        var options = new TelemetryOptions();
35✔
96
        configure(options);
35✔
97

98
        Validator.ValidateObject(options, new ValidationContext(options), validateAllProperties: true);
35✔
99

100
        ConfigureTelemetry(services, options);
34✔
101
        return services;
34✔
102
    }
103

104
    private static void ConfigureTelemetry(IServiceCollection services, TelemetryOptions options)
105
    {
106
        if (!options.Enabled)
40✔
107
        {
108
            return;
6✔
109
        }
110

111
        // Endpoint is [Required] and validated before ConfigureTelemetry is reached.
112
#pragma warning disable CS0618 // OtlpExportProtocol.Grpc is intentionally supported; warning only applies to netstandard2.0 without HttpClientFactory
113
        var endpoint = options.Endpoint!;
34✔
114
        var serviceVersion = Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
34!
115

116
        var builder = services.AddOpenTelemetry()
34✔
117
            .ConfigureResource(resource =>
34✔
118
            {
34✔
119
                if (!string.IsNullOrWhiteSpace(options.ServiceName))
9✔
120
                {
34✔
121
                    resource.AddService(serviceName: options.ServiceName!, serviceVersion: serviceVersion);
2✔
122
                }
34✔
123

34✔
124
                if (!string.IsNullOrWhiteSpace(options.EnvironmentName))
9✔
125
                {
34✔
126
                    resource.AddAttributes([new("deployment.environment", options.EnvironmentName!)]);
2✔
127
                }
34✔
128

34✔
129
                if (options.ResourceAttributes.Count > 0)
9!
130
                {
34✔
131
                    resource.AddAttributes(options.ResourceAttributes.Select(kv => new KeyValuePair<string, object>(kv.Key, kv.Value)));
×
132
                }
34✔
133
            });
43✔
134

135
        if (options.EnableTracing)
34✔
136
        {
137
            builder.WithTracing(tracing =>
32✔
138
            {
32✔
139
                tracing.SetSampler(new ParentBasedSampler(new TraceIdRatioBasedSampler(options.SampleRatio)));
32✔
140

32✔
141
                if (options.EnableAspNetCoreInstrumentation)
32✔
142
                {
32✔
143
                    tracing.AddAspNetCoreInstrumentation(opt =>
31!
144
                    {
31✔
145
                        opt.RecordException = options.RecordExceptions;
5✔
146
                        if (options.ExcludedPaths.Length > 0)
5!
147
                        {
31✔
148
                            opt.Filter = ctx => !options.ExcludedPaths.Any(p => ctx.Request.Path.StartsWithSegments(p));
5!
149
                        }
31✔
150
                    });
36✔
151
                }
32✔
152

32✔
153
                if (options.EnableHttpClientInstrumentation)
32✔
154
                {
32✔
155
                    tracing.AddHttpClientInstrumentation(opt => opt.RecordException = options.RecordExceptions);
35!
156
                }
32✔
157

32✔
158
                if (options.EnableSqlClientInstrumentation)
32✔
159
                {
32✔
160
                    tracing.AddSqlClientInstrumentation(opt => opt.RecordException = options.RecordExceptions);
1!
161
                }
32✔
162

32✔
163
                options.ConfigureTracing?.Invoke(tracing);
32✔
164

32✔
165
                tracing.AddOtlpExporter(exp =>
32!
166
                {
32✔
167
                    exp.Endpoint = options.Protocol == OtlpExportProtocol.Grpc ? endpoint : new Uri($"{endpoint}/v1/traces");
5!
168
                    exp.Protocol = options.Protocol;
5✔
169
                    exp.Headers = options.Headers;
5✔
170
                });
37✔
171
            });
64✔
172
        }
173

174
        if (options.EnableMetrics)
34✔
175
        {
176
            builder.WithMetrics(metrics =>
32✔
177
            {
32✔
178
                if (options.EnableAspNetCoreInstrumentation)
32✔
179
                {
32✔
180
                    metrics.AddAspNetCoreInstrumentation();
31✔
181
                }
32✔
182

32✔
183
                if (options.EnableHttpClientInstrumentation)
32✔
184
                {
32✔
185
                    metrics.AddHttpClientInstrumentation();
30✔
186
                }
32✔
187

32✔
188
                if (options.EnableRuntimeInstrumentation)
32✔
189
                {
32✔
190
                    metrics.AddRuntimeInstrumentation();
31✔
191
                }
32✔
192

32✔
193
                options.ConfigureMetrics?.Invoke(metrics);
32✔
194

32✔
195
                metrics.AddOtlpExporter(exp =>
32!
196
                {
32✔
197
                    exp.Endpoint = options.Protocol == OtlpExportProtocol.Grpc ? endpoint : new Uri($"{endpoint}/v1/metrics");
2!
198
                    exp.Protocol = options.Protocol;
2✔
199
                    exp.Headers = options.Headers;
2✔
200
                });
34✔
201
            });
64✔
202
        }
203

204
        if (options.EnableLogging)
34✔
205
        {
206
            services.AddLogging(logging =>
32✔
207
            {
32✔
208
                options.ConfigureLogging?.Invoke(logging);
32✔
209

32✔
210
                logging.AddOpenTelemetry(otel =>
32!
211
                {
32✔
212
                    otel.IncludeScopes = options.IncludeScopes;
2✔
213
                    otel.IncludeFormattedMessage = options.IncludeFormattedMessage;
2✔
214

32✔
215
                    otel.AddOtlpExporter(exp =>
2!
216
                    {
2✔
217
                        exp.Endpoint = options.Protocol == OtlpExportProtocol.Grpc ? endpoint : new Uri($"{endpoint}/v1/logs");
2!
218
                        exp.Protocol = options.Protocol;
2✔
219
                        exp.Headers = options.Headers;
2✔
220
                    });
4✔
221
                });
34✔
222
            });
64✔
223
        }
224
#pragma warning restore CS0618
225
    }
34✔
226
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc