Skip to content

Commit d243aa0

Browse files
authored
Merge pull request #153 from serilog/dev
4.1.0 Release
2 parents a967446 + 87e1ade commit d243aa0

File tree

13 files changed

+204
-23
lines changed

13 files changed

+204
-23
lines changed

README.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ setups, and looks like:
4545
```csharp
4646
Log.Logger = new LoggerConfiguration()
4747
.WriteTo.OpenTelemetry(
48-
endpoint: "http://127.0.0.1:4318/v1/logs",
48+
endpoint: "http://127.0.0.1:4318",
4949
protocol: OtlpProtocol.HttpProtobuf)
5050
.CreateLogger();
5151
```
@@ -57,7 +57,7 @@ configuration, which looks like:
5757
Log.Logger = new LoggerConfiguration()
5858
.WriteTo.OpenTelemetry(options =>
5959
{
60-
options.Endpoint = "http://127.0.0.1:4318/v1/logs";
60+
options.Endpoint = "http://127.0.0.1:4318";
6161
options.Protocol = OtlpProtocol.HttpProtobuf;
6262
})
6363
.CreateLogger();
@@ -183,6 +183,20 @@ Serilog `LogEvent` | OpenTelemetry `Span` | Comments
183183
`Properties["SpanStartTimestamp"]` | `StartTimeUnixNano` | Value must be of type `DateTime`; .NET provides 100-nanosecond precision |
184184
`Timestamp` | `EndTimeUnixNano` | .NET provides 100-nanosecond precision |
185185

186+
## Suppressing other instrumentation
187+
188+
If the sink is used in an application that also instruments HTTP or gRPC requests using the OpenTelemetry libraries,
189+
this can be suppressed for outbound requests made by the sink using `OnBeginSuppressInstrumentation`:
190+
191+
```csharp
192+
Log.Logger = new LoggerConfiguration()
193+
.WriteTo.OpenTelemetry(options =>
194+
{
195+
options.OnBeginSuppressInstrumentation =
196+
OpenTelemetry.SuppressInstrumentationScope.Begin;
197+
// ...
198+
```
199+
186200
## Example
187201

188202
The `example/Example` subdirectory contains an example application that logs

example/Example/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ static void SendLogs(ILogger logger, string protocol)
6363
static Logger CreateLogger(OtlpProtocol protocol)
6464
{
6565
var endpoint = protocol == OtlpProtocol.HttpProtobuf ?
66-
"http://localhost:4318/v1/logs" :
66+
"http://localhost:4318" :
6767
"http://localhost:4317";
6868

6969
return new LoggerConfiguration()
-6 KB
Binary file not shown.

src/Serilog.Sinks.OpenTelemetry/OpenTelemetryLoggerConfigurationExtensions.cs

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public static class OpenTelemetryLoggerConfigurationExtensions
4343
/// Send log events to an OTLP exporter.
4444
/// </summary>
4545
/// <param name="loggerSinkConfiguration">
46-
/// The `WriteTo` configuration object.
46+
/// The <c>WriteTo</c> configuration object.
4747
/// </param>
4848
/// <param name="configure">The configuration callback.</param>
4949
/// <param name="ignoreEnvironment">If false the configuration will be overridden with values
@@ -54,22 +54,46 @@ public static LoggerConfiguration OpenTelemetry(
5454
Action<BatchedOpenTelemetrySinkOptions> configure,
5555
bool ignoreEnvironment = false)
5656
{
57+
return loggerSinkConfiguration.OpenTelemetry(
58+
configure,
59+
ignoreEnvironment ? null : Environment.GetEnvironmentVariable
60+
);
61+
}
62+
63+
/// <summary>
64+
/// Send log events to an OTLP exporter.
65+
/// </summary>
66+
/// <param name="loggerSinkConfiguration">
67+
/// The `WriteTo` configuration object.
68+
/// </param>
69+
/// <param name="configure">The configuration callback.</param>
70+
/// <param name="getConfigurationVariable">Provides <see href="https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/">OTLP Exporter
71+
/// Configuration variables</see> that will override other options when present.</param>
72+
public static LoggerConfiguration OpenTelemetry(
73+
this LoggerSinkConfiguration loggerSinkConfiguration,
74+
Action<BatchedOpenTelemetrySinkOptions> configure,
75+
Func<string, string?>? getConfigurationVariable)
76+
{
77+
if (loggerSinkConfiguration == null) throw new ArgumentNullException(nameof(loggerSinkConfiguration));
5778
if (configure == null) throw new ArgumentNullException(nameof(configure));
5879

5980
var options = new BatchedOpenTelemetrySinkOptions();
6081
configure(options);
6182

62-
if (!ignoreEnvironment)
83+
if (getConfigurationVariable != null)
6384
{
64-
OpenTelemetryEnvironment.Configure(options, Environment.GetEnvironmentVariable);
85+
OpenTelemetryEnvironment.Configure(options, getConfigurationVariable);
6586
}
6687

6788
var exporter = Exporter.Create(
6889
logsEndpoint: options.LogsEndpoint,
6990
tracesEndpoint: options.TracesEndpoint,
7091
protocol: options.Protocol,
7192
headers: new Dictionary<string, string>(options.Headers),
72-
httpMessageHandler: options.HttpMessageHandler ?? CreateDefaultHttpMessageHandler());
93+
httpMessageHandler: options.HttpMessageHandler ?? CreateDefaultHttpMessageHandler(),
94+
onBeginSuppressInstrumentation: options.OnBeginSuppressInstrumentation != null ?
95+
() => options.OnBeginSuppressInstrumentation(true)
96+
: null);
7397

7498
ILogEventSink? logsSink = null, tracesSink = null;
7599

@@ -103,7 +127,7 @@ public static LoggerConfiguration OpenTelemetry(
103127
/// Send log events to an OTLP exporter.
104128
/// </summary>
105129
/// <param name="loggerSinkConfiguration">
106-
/// The `WriteTo` configuration object.
130+
/// The <c>WriteTo</c> configuration object.
107131
/// </param>
108132
/// <param name="endpoint">
109133
/// The full URL of the OTLP exporter endpoint.
@@ -156,7 +180,7 @@ public static LoggerConfiguration OpenTelemetry(
156180
/// Audit to an OTLP exporter, waiting for each event to be acknowledged, and propagating errors to the caller.
157181
/// </summary>
158182
/// <param name="loggerAuditSinkConfiguration">
159-
/// The `AuditTo` configuration object.
183+
/// The <c>AuditTo</c> configuration object.
160184
/// </param>
161185
/// <param name="configure">The configuration callback.</param>
162186
public static LoggerConfiguration OpenTelemetry(
@@ -173,7 +197,10 @@ public static LoggerConfiguration OpenTelemetry(
173197
tracesEndpoint: options.TracesEndpoint,
174198
protocol: options.Protocol,
175199
headers: new Dictionary<string, string>(options.Headers),
176-
httpMessageHandler: options.HttpMessageHandler ?? CreateDefaultHttpMessageHandler());
200+
httpMessageHandler: options.HttpMessageHandler ?? CreateDefaultHttpMessageHandler(),
201+
onBeginSuppressInstrumentation: options.OnBeginSuppressInstrumentation != null ?
202+
() => options.OnBeginSuppressInstrumentation(true)
203+
: null);
177204

178205
ILogEventSink? logsSink = null, tracesSink = null;
179206

src/Serilog.Sinks.OpenTelemetry/Serilog.Sinks.OpenTelemetry.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<PropertyGroup>
33
<Description>This Serilog sink transforms Serilog events into OpenTelemetry
44
logs and sends them to an OTLP (gRPC or HTTP) endpoint.</Description>
5-
<VersionPrefix>4.0.0</VersionPrefix>
5+
<VersionPrefix>4.1.0</VersionPrefix>
66
<Authors>Serilog Contributors</Authors>
77
<!-- .NET Framework version targeting is frozen at these two TFMs. -->
88
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT'">net471;net462</TargetFrameworks>

src/Serilog.Sinks.OpenTelemetry/Sinks/OpenTelemetry/Configuration/OpenTelemetryEnvironment.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,23 @@ static class OpenTelemetryEnvironment
2222
const string ResourceAttributesVarName = "OTEL_RESOURCE_ATTRIBUTES";
2323
const string ServiceNameVarName = "OTEL_SERVICE_NAME";
2424

25-
public static void Configure(BatchedOpenTelemetrySinkOptions options, Func<string, string?> getEnvironmentVariable)
25+
public static void Configure(BatchedOpenTelemetrySinkOptions options, Func<string, string?> getConfigurationVariable)
2626
{
27-
options.Protocol = getEnvironmentVariable(ProtocolVarName) switch
27+
options.Protocol = getConfigurationVariable(ProtocolVarName) switch
2828
{
2929
"http/protobuf" => OtlpProtocol.HttpProtobuf,
3030
"grpc" => OtlpProtocol.Grpc,
3131
_ => options.Protocol
3232
};
3333

34-
if (getEnvironmentVariable(EndpointVarName) is { Length: > 1 } endpoint)
34+
if (getConfigurationVariable(EndpointVarName) is { Length: > 1 } endpoint)
3535
options.Endpoint = endpoint;
3636

37-
FillHeadersIfPresent(getEnvironmentVariable(HeaderVarName), options.Headers);
37+
FillHeadersIfPresent(getConfigurationVariable(HeaderVarName), options.Headers);
3838

39-
FillHeadersResourceAttributesIfPresent(getEnvironmentVariable(ResourceAttributesVarName), options.ResourceAttributes);
39+
FillHeadersResourceAttributesIfPresent(getConfigurationVariable(ResourceAttributesVarName), options.ResourceAttributes);
4040

41-
if (getEnvironmentVariable(ServiceNameVarName) is { Length: > 1 } serviceName)
41+
if (getConfigurationVariable(ServiceNameVarName) is { Length: > 1 } serviceName)
4242
{
4343
options.ResourceAttributes[SemanticConventions.AttributeServiceName] = serviceName;
4444
}

src/Serilog.Sinks.OpenTelemetry/Sinks/OpenTelemetry/Exporters/Exporter.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,18 @@ public static IExporter Create(
2121
string? tracesEndpoint,
2222
OtlpProtocol protocol,
2323
IReadOnlyDictionary<string, string> headers,
24-
HttpMessageHandler? httpMessageHandler)
24+
HttpMessageHandler? httpMessageHandler,
25+
Func<IDisposable>? onBeginSuppressInstrumentation)
2526
{
26-
return protocol switch
27+
var exporter = protocol switch
2728
{
28-
OtlpProtocol.HttpProtobuf => new HttpExporter(logsEndpoint, tracesEndpoint, headers, httpMessageHandler),
29+
OtlpProtocol.HttpProtobuf => (IExporter)new HttpExporter(logsEndpoint, tracesEndpoint, headers, httpMessageHandler),
2930
OtlpProtocol.Grpc => new GrpcExporter(logsEndpoint, tracesEndpoint, headers, httpMessageHandler),
3031
_ => throw new NotSupportedException($"OTLP protocol {protocol} is unsupported.")
3132
};
33+
34+
return onBeginSuppressInstrumentation != null
35+
? new InstrumentationSuppressingExporter(exporter, onBeginSuppressInstrumentation)
36+
: exporter;
3237
}
33-
}
38+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright © Serilog Contributors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using OpenTelemetry.Proto.Collector.Logs.V1;
16+
using OpenTelemetry.Proto.Collector.Trace.V1;
17+
18+
namespace Serilog.Sinks.OpenTelemetry.Exporters;
19+
20+
/// <summary>
21+
/// Uses a callback to suppress instrumentation while telemetry is being exported by the wrapped exporter.
22+
/// </summary>
23+
sealed class InstrumentationSuppressingExporter : IExporter, IDisposable
24+
{
25+
readonly IExporter _exporter;
26+
readonly Func<IDisposable> _onBeginSuppressInstrumentation;
27+
28+
public InstrumentationSuppressingExporter(IExporter exporter, Func<IDisposable> onBeginSuppressInstrumentation)
29+
{
30+
_exporter = exporter;
31+
_onBeginSuppressInstrumentation = onBeginSuppressInstrumentation;
32+
}
33+
34+
public void Export(ExportLogsServiceRequest request)
35+
{
36+
using (_onBeginSuppressInstrumentation())
37+
{
38+
_exporter.Export(request);
39+
}
40+
}
41+
42+
public async Task ExportAsync(ExportLogsServiceRequest request)
43+
{
44+
using (_onBeginSuppressInstrumentation())
45+
{
46+
await _exporter.ExportAsync(request);
47+
}
48+
}
49+
50+
public void Export(ExportTraceServiceRequest request)
51+
{
52+
using (_onBeginSuppressInstrumentation())
53+
{
54+
_exporter.Export(request);
55+
}
56+
}
57+
58+
public async Task ExportAsync(ExportTraceServiceRequest request)
59+
{
60+
using (_onBeginSuppressInstrumentation())
61+
{
62+
await _exporter.ExportAsync(request);
63+
}
64+
}
65+
66+
public void Dispose()
67+
{
68+
(_exporter as IDisposable)?.Dispose();
69+
}
70+
}

src/Serilog.Sinks.OpenTelemetry/Sinks/OpenTelemetry/OpenTelemetrySinkOptions.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,4 +158,16 @@ public string? TracesEndpoint
158158
/// to be changed at runtime.
159159
/// </summary>
160160
public LoggingLevelSwitch? LevelSwitch { get; set; }
161+
162+
/// <summary>
163+
/// A callback used by the sink before triggering behaviors that may themselves generate log or trace information.
164+
/// Set this value to <c>OpenTelemetry.SuppressInstrumentationScope.Begin</c> when using this sink in applications
165+
/// that instrument HTTP or gRPC requests using OpenTelemetry.
166+
/// </summary>
167+
/// <example>
168+
/// options.OnBeginSuppressInstrumentation = OpenTelemetry.SuppressInstrumentationScope.Begin;
169+
/// </example>
170+
/// <remarks>This callback accepts a <c langword="bool"/> in order to match the signature of the OpenTelemetry SDK method
171+
/// that is typically assigned to it. The sink always provides the callback with the value <c langword="true" />.</remarks>
172+
public Func<bool, IDisposable>? OnBeginSuppressInstrumentation { get; set; }
161173
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using OpenTelemetry.Proto.Collector.Logs.V1;
2+
using Serilog.Sinks.OpenTelemetry.Exporters;
3+
using Serilog.Sinks.OpenTelemetry.Tests.Support;
4+
using Xunit;
5+
6+
namespace Serilog.Sinks.OpenTelemetry.Tests;
7+
8+
public class InstrumentationSuppressingExporterTests
9+
{
10+
[Fact]
11+
public void RequestsAreNotInstrumentedWhenSuppressed()
12+
{
13+
var exporter = new CollectingExporter();
14+
15+
exporter.Export(new ExportLogsServiceRequest());
16+
Assert.Equal(1, exporter.InstrumentedRequestCount);
17+
Assert.Single(exporter.ExportLogsServiceRequests);
18+
19+
var wrapper = new InstrumentationSuppressingExporter(exporter, TestSuppressInstrumentationScope.Begin);
20+
wrapper.Export(new ExportLogsServiceRequest());
21+
Assert.Equal(1, exporter.InstrumentedRequestCount);
22+
Assert.Equal(2, exporter.ExportLogsServiceRequests.Count);
23+
}
24+
}

0 commit comments

Comments
 (0)