Skip to content

Add ASP.NET Core ILogger instrumentation for logs injection#1549

Closed
andrewlock wants to merge 22 commits into
masterfrom
andrew/feature/ilogger-injection
Closed

Add ASP.NET Core ILogger instrumentation for logs injection#1549
andrewlock wants to merge 22 commits into
masterfrom
andrew/feature/ilogger-injection

Conversation

@andrewlock

Copy link
Copy Markdown
Member

The initial implementation was done by @zacharycmontoya - I added tests and extended to cover .NET Core 3+

This instruments the HostingApplication.ProcessRequestAsync method, which wraps execution of the middleware pipeline, _after the DiagnosticSource.BeginRequest has executed (and hence after a TraceId has been generated).

Limitations:

  • The integration does not add SpanId to the logs. Doing so would require a different instrumentation point.
  • The logs injection integration only works for ASP.NET Core requests.
  • TraceId injection is disabled for .NET Framework. The integration is still enabled, but as the diagnostic-source based tracing doesn't work, the trace_id would always be 0, which defeats the point of logs injection. dd_service, dd_env, and dd_version are added though.

As part of this integration, I also updated the 3.1 sample to run and test against .NET 5, and the 2.1 to test and run against .NET Framework (to confirm existing no-tracing/no-trace_id behaviour)

@DataDog/apm-dotnet

@andrewlock andrewlock requested a review from a team as a code owner June 17, 2021 14:54
namespace Datadog.Trace.AspNetCore
{
// Implementation based on AspNetCore Hosting implementation: https://github.com/dotnet/aspnetcore/blob/1a2b3260c6161ae9b7f639de228a6eb0488a1075/src/Hosting/Hosting/src/Internal/HostingLoggerExtensions.cs#L114
internal class DatadogLoggingScope : IReadOnlyList<KeyValuePair<string, object>>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder if we should make this public, so customers can use it with manual instrumentation?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, is this for non-aspnetcore use cases? I was thinking we would start by only exposing an ApplicationBuilder extension method that the user can call, and this could be an implementation detail.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was thinking non-aspnetcore use cases. The ApplicationBuilder makes sense, but then we would have to take dependencies on the aspnetcore packages, which I wasn't sure if we'd want to? We can always make this public later if we want to, so probably makes sense to keep it internal for now.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but then we would have to take dependencies on the aspnetcore packages

In a new Datadog.Trace.AspNetCore package! 😅

{
if (index == 0)
{
return new KeyValuePair<string, object>("dd_service", _service);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These identifiers are "serilog" style. If users are using ILogger injection and NLog/Log4NET, then they will have two sets of identifiers I think. Not sure what the best way to tackle that is

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah they'll have two sets of identifiers...perhaps we'll have a better idea of how to tackle it once we see it in use?

@lucaspimentel lucaspimentel added area:automatic-instrumentation Automatic instrumentation managed C# code (Datadog.Trace.ClrProfiler.Managed) type:new-feature labels Jun 17, 2021
@andrewlock andrewlock force-pushed the andrew/feature/ilogger-injection branch 5 times, most recently from 2c1597d to bf5b530 Compare June 22, 2021 09:46
"Samples.AspNetCoreMvc21" => Framework == TargetFramework.NETCOREAPP2_1,
"Samples.AspNetCoreMvc30" => Framework == TargetFramework.NETCOREAPP3_0,
"Samples.AspNetCoreMvc31" => Framework == TargetFramework.NETCOREAPP3_1,
"Samples.AspNetCoreMvc31" => Framework == TargetFramework.NETCOREAPP3_1 || Framework == TargetFramework.NET5_0,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No Samples.AspNetCoreMvc50? 😝

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a reasonable question, tbh, and given we have separate projects for 3.0 and 3.1, maybe we should?🤔

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not in this PR of course. To the backlog!

Comment thread src/Datadog.Trace/DuckTyping/DuckTypeExceptions.cs Outdated
@andrewlock andrewlock force-pushed the andrew/feature/ilogger-injection branch 3 times, most recently from 1dde0da to eca92e8 Compare June 30, 2021 10:02
@andrewlock

Copy link
Copy Markdown
Member Author

I wonder if we should provide a way to disable this log injection integration. It's written as part of the aspnetcore integration, but given it isn't totally complete (i.e. it only works for aspnetcore requests, not all ILogger, not manual instrumentation etc), it would add unnecessary duplication if users are also using serilog/nlog/log4net integration.

Those other integrations can be "disabled" by just not wiring them up, but we don't have a way of disabling this ILogger integration, while keeping aspnetcore and Serilog/nlog/log4net log injection enabled. A simple "fix" would be to use a different IntegrationName for this, instead of bundling as part of AspNetCore?

@andrewlock andrewlock force-pushed the andrew/feature/ilogger-injection branch 2 times, most recently from 4e1fd51 to 98d0e8c Compare July 13, 2021 08:11
/// <summary>
/// Gets the diagnostics field
/// </summary>
[Duck(Name = "_diagnostics", Kind = DuckKind.Field)]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If #1601 is merged soon, then we can refactor this to

[DuckField(Name = "_diagnostics")]

@zacharycmontoya

Copy link
Copy Markdown
Contributor

I wonder if we should provide a way to disable this log injection integration. It's written as part of the aspnetcore integration, but given it isn't totally complete (i.e. it only works for aspnetcore requests, not all ILogger, not manual instrumentation etc), it would add unnecessary duplication if users are also using serilog/nlog/log4net integration.

Those other integrations can be "disabled" by just not wiring them up, but we don't have a way of disabling this ILogger integration, while keeping aspnetcore and Serilog/nlog/log4net log injection enabled. A simple "fix" would be to use a different IntegrationName for this, instead of bundling as part of AspNetCore?

How about we change the IntegrationName to ILogger, since all of our automatic logs injection for ILogger will have to be done by automatic instrumentation?

where TTarget : IHostingApplication
{
IDisposable disposable = null;
if (Tracer.Instance.Settings.LogsInjectionEnabled)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'll need to modify the check to also check the integration:

if (Tracer.Instance.Settings.LogsInjectionEnabled && Tracer.Instance.Settings.IsIntegrationEnabled(IntegrationId))

paired with

private static readonly IntegrationInfo IntegrationId = IntegrationRegistry.GetIntegrationInfo(IntegrationName);

andrewlock and others added 16 commits July 15, 2021 15:07
- Fix content root
- Configure loggers
- log using middleware (so we confirm 404s get the trace id etc too)
- Add Startup log that will not be traced
- Exclude other logs by default
Could/Should make these separate tests, but keeping them the same for now for speed/simplicity
Don't support tracing yet, so won't get any TraceIds, but can inject service/env/version into logs
This wasn't possible before, because the .NET Core 2.1 implementation of ILogger, Logger, used explicit interface implementation. This is now supported out of the box thanks to #1555
Co-authored-by: Lucas Pimentel-Ordyna <lucas.pimentel@datadoghq.com>
@andrewlock andrewlock force-pushed the andrew/feature/ilogger-injection branch from 98d0e8c to 9ff60d4 Compare July 15, 2021 14:23

@zacharycmontoya zacharycmontoya left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, and since I hadn't looked at this in a while I think I acquired an objective perspective to review it properly 😆

@andrewlock

Copy link
Copy Markdown
Member Author

Superseded by #1663. I'll pull the additional tests (for .NET Framework etc) out in a separate PR

@andrewlock andrewlock closed this Aug 23, 2021
@andrewlock andrewlock deleted the andrew/feature/ilogger-injection branch August 23, 2021 14:51
@andrewlock andrewlock restored the andrew/feature/ilogger-injection branch August 23, 2021 14:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:automatic-instrumentation Automatic instrumentation managed C# code (Datadog.Trace.ClrProfiler.Managed) type:new-feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants