Skip to content
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

Adding Microsoft.Extensions.Telemetry.Abstractions README #4670

Merged
merged 3 commits into from
Nov 6, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 100 additions & 1 deletion src/Libraries/Microsoft.Extensions.Telemetry.Abstractions/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Microsoft.Extensions.Telemetry.Abstractions

Common abstractions for high-level telemetry primitives.
This package contains common abstractions for high-level telemetry primitives. Here are the main features it provides:

- Enhanced Logging Capabilities
- Log Enrichment
- Latency Measurement
- HTTP Request Metadata Handling

## Install the package

Expand All @@ -18,6 +23,100 @@ Or directly in the C# project file:
</ItemGroup>
```

## Usage

### Enhanced Logging Capabilities

The package includes a custom logging generator that enhances the default .NET logging capabilities by replacing the default generator. This generator automatically logs the contents of collections and offers advanced logging features, significantly improving the debugging and monitoring process.

```csharp
[LoggerMessage(1, LogLevel.Information, "These are the contents of my dictionary: {dictionary}")]
joperezr marked this conversation as resolved.
Show resolved Hide resolved
internal static partial void LogMyDictionary(ILogger<Program> logger, Dictionary<int, string> temperature);
```

It also adds the `LogProperties` attribute which can be applied to a an object parameter of a `LoggerMessage` method. It introspects the passed-in object and automatically adds tags for all its properties. This leads to more informative logs without the need for manual tagging of each property.
joperezr marked this conversation as resolved.
Show resolved Hide resolved

```csharp
[LoggerMessage(1, LogLevel.Information, "Detected a new temperature: {temperature}")]
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
[LoggerMessage(1, LogLevel.Information, "Detected a new temperature: {temperature}")]
[LoggerMessage(1, LogLevel.Information, "Detected a new temperature: {Temperature}")]

Copy link
Member Author

Choose a reason for hiding this comment

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

This should match the parameter name, which is lower case.

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

Keep in mind that this is using a different generator which adds additional constraints and diagnostics (this is using Microsoft.Extensions.Telemetry.Abstractions logging generator)

I believe in the past I've seen warnings when parameter name doesn't exactly match the template, @geeknoid can you confirm?

Copy link
Member Author

Choose a reason for hiding this comment

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

I checked back and it does seem like this is not case-sensitive as you suggested, which also seems to be called out explicitly https://learn.microsoft.com/en-us/dotnet/core/extensions/logger-message-generator#case-insensitive-template-name-support . Anyway, since it's not a convention that we should upper case those, I've left it lowercase for now (I also hope it helps describing that what should match is paramter name as opposed to the parameter type, since in my example both are the same).

Copy link
Contributor

Choose a reason for hiding this comment

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

First letter can be either lowercased or uppercased, you can check Iliar's PR: #4584

internal static partial void LogNewTemperature(ILogger<Program> logger, [LogProperties] Temperature temperature);

internal record Temperature(double value, TemperatureUnit unit);
```

### Log Enrichment

To enrich your logging data, you can add custom log enrichers to your service collection. This can be done using specific implementations or generic types.
joperezr marked this conversation as resolved.
Show resolved Hide resolved

```csharp
// Using a specific implementation
Copy link
Contributor

Choose a reason for hiding this comment

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

implementation -> instance?

builder.services.AddLogEnricher(new CustomLogEnricher());

// Using a generic type
builder.services.AddLogEnricher<AnotherLogEnricher>();
```

Create custom log enrichers by implementing the `ILogEnricher` interface.
Copy link
Contributor

Choose a reason for hiding this comment

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

Worth mentioning IStaticLogEnricher too

Copy link
Contributor

Choose a reason for hiding this comment

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


```csharp
public class CustomLogEnricher : ILogEnricher
{
public void Enrich(IEnrichmentTagCollector collector)
{
// Add custom logic to enrich log data
collector.Add("CustomTag", "CustomValue");
}
}
```

### Latency Measurement

To track latency in your application, register checkpoint, measure, and tag names using the provided methods.
joperezr marked this conversation as resolved.
Show resolved Hide resolved

```csharp
builder.services.RegisterCheckpointNames("databaseQuery", "externalApiCall");
builder.services.RegisterMeasureNames("responseTime", "processingTime");
builder.services.RegisterTagNames("userId", "transactionId");
```

Implement the `ILatencyDataExporter` to export latency data. This can be integrated with external systems or logging frameworks.

```csharp
public class CustomLatencyDataExporter : ILatencyDataExporter
{
public async Task ExportAsync(LatencyData data, CancellationToken cancellationToken)
{
// Export logic here
}
}
```

Use the latency context to track performance metrics in your application.

```csharp
public void YourMethod(ILatencyContextProvider contextProvider)
{
var context = contextProvider.CreateContext();
var checkpointToken = context.GetCheckpointToken("databaseQuery");

// Start measuring
context.AddCheckpoint(checkpointToken);

// Perform operations...

// End measuring
context.AddCheckpoint(checkpointToken);

// Optionally, record measures and tags
context.RecordMeasure(context.GetMeasureToken("responseTime"), measureValue);
context.SetTag(context.GetTagToken("userId"), "User123");
}
```

### Http Request Metadata Handling

The `IDownstreamDependencyMetadata` interface is designed to capture and store metadata about the downstream dependencies of an HTTP request. This is particularly useful for understanding external service dependencies and their impact on your application's performance and reliability.

The `IOutgoingRequestContext` interface provides a mechanism for associating metadata with outgoing HTTP requests. This allows you to enrich outbound requests with additional information that can be used for logging, telemetry, and analysis.

## Feedback & Contributing

Expand Down