-
Notifications
You must be signed in to change notification settings - Fork 790
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
[Logs] Serilog extensions project #3438
[Logs] Serilog extensions project #3438
Conversation
Codecov Report
@@ Coverage Diff @@
## main #3438 +/- ##
==========================================
- Coverage 86.37% 86.36% -0.02%
==========================================
Files 263 265 +2
Lines 9550 9598 +48
==========================================
+ Hits 8249 8289 +40
- Misses 1301 1309 +8
|
src/OpenTelemetry.Extensions.Serilog/OpenTelemetrySerilogSink.cs
Outdated
Show resolved
Hide resolved
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.
LGTM. Just a couple thoughts that don't block anything.
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\src\OpenTelemetry.Exporter.Console\OpenTelemetry.Exporter.Console.csproj" /> | ||
<ProjectReference Include="..\..\src\OpenTelemetry.Extensions.Serilog\OpenTelemetry.Extensions.Serilog.csproj" /> |
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.
This name seems fine to me, but just thought I'd share the quick nuget search I did https://www.nuget.org/packages?q=serilog+sink. Appears most Serilog sinks have Sink
in the package name.
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.
nit: Use $(RepoRoot)
.
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.
nit: Use $(RepoRoot).
Updated
Appears most Serilog sinks have Sink in the package name.
Right now the project is just a sink, but I don't know if it might contain other serilog artifacts in the future. For example say users are logging to flat files. They may want a serilog enricher that adds OTel context to those logs. We could also have that in this project. Basically my thinking was this project would just contain stuff to help with different otel/serilog scenarios. We could of course have dedicated packages for each thing. More overhead to do that. Don't feel strongly either way so if you want it renamed just LMK and I'll update.
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.
Don't feel strongly either way so if you want it renamed just LMK and I'll update.
Nah, I don't think Sink
is needed allowing for additional Serilog things in the future.
The other thing I was pondering from a package naming perspective is whether we consider this an Extension
or something else... Java for example considers their support for log4j
"instrumentation" https://github.com/open-telemetry/opentelemetry-java-instrumentation/tree/main/instrumentation/log4j.
I think we have more time to consider these things.
|
||
// Note: It is important that OpenTelemetryLoggerProvider is disposed when the | ||
// app is shutdown. In this example we allow Serilog to do that by calling CloseAndFlush. | ||
var openTelemetryLoggerProvider = new OpenTelemetryLoggerProvider(options => |
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.
Instead of assigning this to a variable openTelemetryLoggerProvider
and explaining the object ownership/lifecycle, I suggest putting the new OpenTelemetryLoggerProvider
expression directly in Ln:35 WriteTo.OpenTelemetry(new OpenTelemetryLoggerProvider(...), disposeProvider: true)
: it increases the locality when folks read the code, making the code more self-contained rather than highly contextual, it avoids accidental misuse (e.g. code holding a reference to disposed object).
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.
Here is the version from the other PR: https://github.com/open-telemetry/opentelemetry-dotnet/pull/3305/files#diff-eebf3c37db933ef5eddad43dc03d2553b800b073573f291b7e0ac5999a1391c8
Where I am heading with this is two different types of extensions sharing a provider.
So the question is, what do we want to demo in the example? Sharing a provider or independent providers? I feel like shared will be the more common case. But LMK your thoughts on this.
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.
Here is my take:
examples/LoggingExtensions/Program.cs
looks an example for the end-user - who is using the OpenTelemetry.Extensions.Serilog package to pipe Serilog via OpenTelemetry plumbing. For these users, normally they will only use Serilog in a single application, it makes more sense for them to have something simple, and not to worry about object lifecycle management except for the top levelCloseAndFlush
.examples/LogEmitter/Program.cs
is more targeting the plumbers, might make sense to expose more complexity / duty.
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.
@reyang Let me merge and then I'll follow-up on this on my next PR where the example project is going to get more interesting?
src/OpenTelemetry.Extensions.Serilog/OpenTelemetry.Extensions.Serilog.csproj
Outdated
Show resolved
Hide resolved
// Note: The goal here is to build a typed array (eg | ||
// int[]) if all the element types match otherwise | ||
// fallback to object[] |
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.
@alanwest It was a bit tricky, but we now build a typed array when we can.
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.
Interesting, yea looks like this does the thing. Going out on a limb here, but I'd suspect more often than not it'll all be of one type. So, most folks won't hit the perf penalty.
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.
I didn't stress the perf too much. For two reasons. The first is I'm assuming logging arrays is kind of a rare thing? The second is Serilog is already destroying perf 🤣 Whatever you log it allocates/wraps/boxes into a spiderweb of reference types. Joined the darkside Serilog has.
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.
🕷️ 🕸️ 🕶️ 🕸️ 🕷️
{ | ||
if (elements[i] is ScalarValue value) | ||
{ | ||
Type currentElementType = value.Value?.GetType() ?? typeof(object); |
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.
If this were of type MyDisposableType
then later when this log record is exported it'd throw an object disposed exception when attempting to ToString the object. The exporters catch and swallow this kind of thing today.
We don't do anything special earlier in the pipeline for attributes on traces or metrics, but is there any reason we'd want to consider coercing things into one of the primitive types earlier for log data?
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.
Not totally following you. But I don't think a complex type would end up as a ScalarValue
.
I have this test at the moment:
opentelemetry-dotnet/test/OpenTelemetry.Extensions.Serilog.Tests/OpenTelemetrySerilogSinkTests.cs
Line 155 in 1643b31
Log.Logger.Information("Hello {greeting} {id} {@complexObj} {$complexStr}", "World", 18, complexType, complexType); |
That is verifying a "destructed" complex type is currently dropped on the floor, because we don't yet have support for that. What happens in that case is instead of a ScalarValue
you get a StructureValue
which itself contains all the properties of the complex type represented as ScalarValue
s or other StructureValue
s, etc. I don't think disposable/mutable things are a concern because Serilog will capture all the data from the complex type and then represent it using these "value" types it has in here: https://github.com/serilog/serilog/tree/dev/src/Serilog/Events
Right now we have basic support. You can use Serilog to do structured log messaging with primitives + arrays of primitives and it will work into OTel. My goal is to get support into OTel somehow for complex types and then come back to this and properly support fully Serilog's destructuring mechanism 😄
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.
Ah thanks I missed that aspect of the test. Definitely seems like a bridge to cross later once complex types are supported. If serilog does all the work then my question is moot. I wasn't sure if a complex type could end up being added to the attributes of a log record.
[Goal is to pare down the amount of changes on #3305]
Changes
Notes
LogEmitter
artifacts areinternal
. Once those gopublic
this should be moved to contrib repo.TODOs
CHANGELOG.md
updated for non-trivial changes