-
Notifications
You must be signed in to change notification settings - Fork 770
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
LogEmitter System.Diagnostics.Tracing extension project #3305
Changes from 50 commits
6d2f153
101ce19
7eb0006
20479f5
c64ca11
53a6854
fd5a87e
035823b
80cbdbb
744c399
df93cf5
1ba8afe
9a0ee32
888eb54
558e8d2
4d76b87
617d88d
ca7e319
6fae76d
a92e78e
5e33474
6b15b3b
2114dca
0d34533
bb4293c
380f139
87597f8
345e1a2
ea63af4
a12d432
3b35268
57775a5
9ee18df
f347e51
5537c73
e0c2347
dd80cc9
4f6b9e0
1dcd193
226facf
d3c5443
31f5a97
cb69e49
8693d93
b71006f
6269015
7647145
4f498af
55537b5
d1a4b3b
ffda1ce
12c92ad
cf69b8c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\src\OpenTelemetry.Exporter.Console\OpenTelemetry.Exporter.Console.csproj" /> | ||
<ProjectReference Include="..\..\src\OpenTelemetry.Extensions.Tracing\OpenTelemetry.Extensions.Tracing.csproj" /> | ||
<ProjectReference Include="..\..\src\OpenTelemetry.Extensions.Serilog\OpenTelemetry.Extensions.Serilog.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
// <copyright file="Program.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.Diagnostics.Tracing; | ||
using OpenTelemetry.Logs; | ||
using OpenTelemetry.Resources; | ||
using Serilog; | ||
|
||
var resourceBuilder = ResourceBuilder.CreateDefault().AddService("Examples.LogEmitter"); | ||
|
||
using var openTelemetryLoggerProvider = new OpenTelemetryLoggerProvider(options => | ||
{ | ||
options.IncludeFormattedMessage = true; | ||
options | ||
.SetResourceBuilder(resourceBuilder) | ||
.AddConsoleExporter(); | ||
}); | ||
|
||
// Creates an OpenTelemetryEventSourceLogEmitter for routing EventSources with | ||
// names matching OpenTelemetry* into logs | ||
using var openTelemetryEventSourceLogEmitter = new OpenTelemetryEventSourceLogEmitter( | ||
openTelemetryLoggerProvider, | ||
(name) => name.StartsWith("OpenTelemetry") ? EventLevel.LogAlways : null); | ||
|
||
// Configure Serilog global logger | ||
Log.Logger = new LoggerConfiguration() | ||
.WriteTo.OpenTelemetry(openTelemetryLoggerProvider) // <- Register OpenTelemetry Serilog sink | ||
.CreateLogger(); | ||
|
||
// Note: Serilog ForContext API is used to set "CategoryName" on log messages | ||
ILogger programLogger = Log.Logger.ForContext<Program>(); | ||
|
||
programLogger.Information("Application started {Greeting} {Location}", "Hello", "World"); | ||
|
||
programLogger.Information("Message {Array}", new string[] { "value1", "value2" }); | ||
|
||
// Note: ForceFlush is only called here to demo | ||
// OpenTelemetryEventSourceLogEmitter converting SDK events into log messages | ||
openTelemetryLoggerProvider.ForceFlush(); | ||
|
||
Console.WriteLine("Press ENTER to exit..."); | ||
|
||
Console.ReadLine(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# OpenTelemetry LogEmitter Example | ||
|
||
This project contains examples of the `LogEmitter` API being used to extend | ||
existing logging platforms to write into OpenTelemetry logs. | ||
|
||
* Serilog: Using OpenTelemetry.Extensions.Serilog | ||
* EventSource: Using OpenTelemetry.Extensions.EventSource | ||
|
||
## References | ||
|
||
* [OpenTelemetry Project](https://opentelemetry.io/) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
#nullable enable |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
#nullable enable | ||
Serilog.OpenTelemetrySerilogExtensions | ||
static Serilog.OpenTelemetrySerilogExtensions.OpenTelemetry(this Serilog.Configuration.LoggerSinkConfiguration! loggerConfiguration, OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider) -> Serilog.LoggerConfiguration! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// <copyright file="AssemblyInfo.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.Runtime.CompilerServices; | ||
|
||
[assembly: CLSCompliant(false)] | ||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2" + AssemblyInfo.MoqPublicKey)] | ||
|
||
#if SIGNED | ||
internal static class AssemblyInfo | ||
{ | ||
public const string PublicKey = ", PublicKey=002400000480000094000000060200000024000052534131000400000100010051C1562A090FB0C9F391012A32198B5E5D9A60E9B80FA2D7B434C9E5CCB7259BD606E66F9660676AFC6692B8CDC6793D190904551D2103B7B22FA636DCBB8208839785BA402EA08FC00C8F1500CCEF28BBF599AA64FFB1E1D5DC1BF3420A3777BADFE697856E9D52070A50C3EA5821C80BEF17CA3ACFFA28F89DD413F096F898"; | ||
public const string MoqPublicKey = ", PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7"; | ||
} | ||
#else | ||
internal static class AssemblyInfo | ||
{ | ||
public const string PublicKey = ""; | ||
public const string MoqPublicKey = ""; | ||
} | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Changelog | ||
|
||
## Unreleased | ||
|
||
Initial release. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
<PropertyGroup> | ||
<!-- OmniSharp/VS Code requires TargetFrameworks to be in descending order for IntelliSense and analysis. --> | ||
<TargetFrameworks>netstandard2.0</TargetFrameworks> | ||
<Description>Extensions to enable OpenTelemetry logging when using the Serilog library</Description> | ||
<Nullable>enable</Nullable> | ||
<AnalysisMode>AllEnabledByDefault</AnalysisMode> | ||
<AnalysisLevel>latest</AnalysisLevel> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry\OpenTelemetry.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\Guard.cs" Link="Includes\Guard.cs" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Serilog" Version="2.8.0" /> | ||
</ItemGroup> | ||
|
||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// <copyright file="OpenTelemetrySerilogExtensions.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 OpenTelemetry.Internal; | ||
using OpenTelemetry.Logs; | ||
using Serilog.Configuration; | ||
|
||
namespace Serilog | ||
{ | ||
/// <summary> | ||
/// Contains Serilog extension methods. | ||
/// </summary> | ||
public static class OpenTelemetrySerilogExtensions | ||
{ | ||
/// <summary> | ||
/// Adds a sink to Serilog <see cref="LoggerConfiguration"/> which will | ||
/// write to OpenTelemetry. | ||
/// </summary> | ||
/// <param name="loggerConfiguration"><see cref="LoggerSinkConfiguration"/>.</param> | ||
/// <param name="openTelemetryLoggerProvider"><see cref="OpenTelemetryLoggerProvider"/>.</param> | ||
/// <returns>Supplied <see cref="LoggerConfiguration"/> for chaining calls.</returns> | ||
public static LoggerConfiguration OpenTelemetry( | ||
this LoggerSinkConfiguration loggerConfiguration, | ||
OpenTelemetryLoggerProvider openTelemetryLoggerProvider) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For some of our other extension methods we've put the extension method in the same namespace as the class that's being extended. So, does it make sense to put this method in the |
||
{ | ||
Guard.ThrowIfNull(loggerConfiguration); | ||
Guard.ThrowIfNull(openTelemetryLoggerProvider); | ||
|
||
return loggerConfiguration.Sink(new OpenTelemetrySerilogSink(openTelemetryLoggerProvider)); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// <copyright file="OpenTelemetrySerilogSink.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.Collections.Generic; | ||
using System.Diagnostics; | ||
using Microsoft.Extensions.Logging; | ||
using Serilog.Core; | ||
using Serilog.Events; | ||
|
||
namespace OpenTelemetry.Logs | ||
{ | ||
internal sealed class OpenTelemetrySerilogSink : ILogEventSink | ||
{ | ||
private readonly bool includeFormattedMessage; | ||
private readonly LogEmitter logEmitter; | ||
|
||
public OpenTelemetrySerilogSink(OpenTelemetryLoggerProvider openTelemetryLoggerProvider) | ||
{ | ||
this.includeFormattedMessage = openTelemetryLoggerProvider.IncludeFormattedMessage; | ||
this.logEmitter = openTelemetryLoggerProvider.CreateEmitter(); | ||
} | ||
|
||
public void Emit(LogEvent logEvent) | ||
{ | ||
Debug.Assert(logEvent != null, "LogEvent was null."); | ||
|
||
LogRecordData data = new(Activity.Current) | ||
{ | ||
Timestamp = logEvent!.Timestamp.UtcDateTime, | ||
LogLevel = (LogLevel)(int)logEvent.Level, | ||
Message = this.includeFormattedMessage ? logEvent.RenderMessage() : logEvent.MessageTemplate.Text, | ||
Exception = logEvent.Exception, | ||
}; | ||
|
||
LogRecordAttributeList attributes = default; | ||
foreach (KeyValuePair<string, LogEventPropertyValue> property in logEvent.Properties) | ||
{ | ||
// TODO: Serilog supports complex type logging. This is not yet | ||
// supported in OpenTelemetry. | ||
if (property.Key == Constants.SourceContextPropertyName | ||
&& property.Value is ScalarValue sourceContextValue) | ||
{ | ||
data.CategoryName = sourceContextValue.Value as string; | ||
} | ||
else if (property.Value is ScalarValue scalarValue) | ||
{ | ||
attributes.Add(property.Key, scalarValue.Value); | ||
} | ||
else if (property.Value is SequenceValue sequenceValue) | ||
{ | ||
IReadOnlyList<LogEventPropertyValue> elements = sequenceValue.Elements; | ||
if (elements.Count > 0 && elements[0] is ScalarValue) | ||
{ | ||
object[] values = new object[elements.Count]; | ||
for (int i = 0; i < elements.Count; i++) | ||
{ | ||
if (elements[i] is ScalarValue value) | ||
{ | ||
values[i] = value.Value; | ||
} | ||
} | ||
|
||
attributes.Add(property.Key, values); | ||
} | ||
} | ||
} | ||
|
||
this.logEmitter.Log(in data, in attributes); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# OpenTelemetry.Extensions.Serilog | ||
|
||
This project contains a [Serilog](https://github.com/serilog/) | ||
[sink](https://github.com/serilog/serilog/wiki/Configuration-Basics#sinks) for | ||
writing log messages to OpenTelemetry. | ||
|
||
## Usage Example | ||
|
||
```csharp | ||
// Step 1: Configure OpenTelemetryLoggerProvider... | ||
var resourceBuilder = ResourceBuilder.CreateDefault().AddService("MyService"); | ||
|
||
using var openTelemetryLoggerProvider = new OpenTelemetryLoggerProvider(options => | ||
{ | ||
options | ||
.SetResourceBuilder(resourceBuilder) | ||
.AddConsoleExporter(); | ||
}); | ||
|
||
// Step 2: Register OpenTelemetry sink with Serilog... | ||
Log.Logger = new LoggerConfiguration() | ||
.WriteTo.OpenTelemetry(openTelemetryLoggerProvider) | ||
.CreateLogger(); | ||
``` | ||
|
||
## References | ||
|
||
* [OpenTelemetry Project](https://opentelemetry.io/) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
#nullable enable |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#nullable enable | ||
OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter | ||
OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter.OpenTelemetryEventSourceLogEmitter(OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider, string! name, System.Diagnostics.Tracing.EventLevel logLevel = System.Diagnostics.Tracing.EventLevel.LogAlways) -> void | ||
OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter.OpenTelemetryEventSourceLogEmitter(OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider, System.Func<string!, System.Diagnostics.Tracing.EventLevel?>! shouldListenToFunc) -> void | ||
override OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter.Dispose() -> void |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
#nullable enable |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#nullable enable | ||
OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter | ||
OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter.OpenTelemetryEventSourceLogEmitter(OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider, string! name, System.Diagnostics.Tracing.EventLevel logLevel = System.Diagnostics.Tracing.EventLevel.LogAlways) -> void | ||
OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter.OpenTelemetryEventSourceLogEmitter(OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider, System.Func<string!, System.Diagnostics.Tracing.EventLevel?>! shouldListenToFunc) -> void | ||
override OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter.Dispose() -> void |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tracing is such a commonly used term. I get it comes from System.Diagnostics.Tracing, but is there a better name?
What about
OpenTelemetry.Extensions.EventSource
? Or if that's too narrow of a name maybeOpenTelemetry.Extensions.EventTracing
? Or... ugh maybe evenOpenTelemetry.Extensions.SystemDiagnosticsTracing
🥴?