|
| 1 | +# OpenTelemetry .NET Logs |
| 2 | + |
| 3 | +## Best Practices |
| 4 | + |
| 5 | +The following tutorials have demonstrated the best practices for logging with |
| 6 | +OpenTelemetry .NET: |
| 7 | + |
| 8 | +* [Getting Started - Console Application](./getting-started-console/README.md) |
| 9 | +* [Getting Started - ASP.NET Core |
| 10 | + Application](./getting-started-aspnetcore/README.md) |
| 11 | +* [Logging with Complex Objects](./complex-objects/README.md) |
| 12 | + |
| 13 | +## Structured Logging |
| 14 | + |
| 15 | +:heavy_check_mark: You should use structured logging. |
| 16 | + |
| 17 | +* Structured logging is more efficient than unstructured logging. |
| 18 | + * Filtering and redaction can happen on invidual key-value pairs instead of |
| 19 | + the entire log message. |
| 20 | + * Storage and indexing are more efficient. |
| 21 | +* Structured logging makes it easier to manage and consume logs. |
| 22 | + |
| 23 | +:stop_sign: You should avoid string interpolation. |
| 24 | + |
| 25 | +> [!WARNING] |
| 26 | +> The following code has bad performance due to [string |
| 27 | + interpolation](https://learn.microsoft.com/dotnet/csharp/tutorials/string-interpolation): |
| 28 | + |
| 29 | +```csharp |
| 30 | +var food = "tomato"; |
| 31 | +var price = 2.99; |
| 32 | + |
| 33 | +logger.LogInformation($"Hello from {food} {price}."); |
| 34 | +``` |
| 35 | + |
| 36 | +Refer to the [logging performance |
| 37 | +benchmark](../../test/Benchmarks/Logs/LogBenchmarks.cs) for more details. |
| 38 | + |
| 39 | +## Package Version |
| 40 | + |
| 41 | +:heavy_check_mark: You should always use the |
| 42 | +[`ILogger`](https://docs.microsoft.com/dotnet/api/microsoft.extensions.logging.ilogger) |
| 43 | +interface (including |
| 44 | +[`ILogger<TCategoryName>`](https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.ilogger-1)) |
| 45 | +from the latest stable version of |
| 46 | +[Microsoft.Extensions.Logging](https://www.nuget.org/packages/Microsoft.Extensions.Logging/) |
| 47 | +package, regardless of the .NET runtime version being used: |
| 48 | + |
| 49 | +* If you're using the latest stable version of [OpenTelemetry .NET |
| 50 | + SDK](../../src/OpenTelemetry/README.md), you don't have to worry about the |
| 51 | + version of `Microsoft.Extensions.Logging` package because it is already taken |
| 52 | + care of for you via [package dependency](../../Directory.Packages.props). |
| 53 | +* Starting from version `3.1.0`, the .NET runtime team is holding a high bar for |
| 54 | + backward compatibility on `Microsoft.Extensions.Logging` even during major |
| 55 | + version bumps, so compatibility is not a concern here. |
| 56 | + |
| 57 | +## Logging API |
| 58 | + |
| 59 | +:heavy_check_mark: You should use [compile-time logging source |
| 60 | +generation](https://docs.microsoft.com/dotnet/core/extensions/logger-message-generator) |
| 61 | +pattern to achieve the best performance. |
| 62 | + |
| 63 | +```csharp |
| 64 | +public static partial class Food |
| 65 | +{ |
| 66 | + [LoggerMessage(Level = LogLevel.Information, Message = "Hello from {food} {price}.")] |
| 67 | + public static partial void SayHello(ILogger logger, string food, double price); |
| 68 | +} |
| 69 | + |
| 70 | +var food = "tomato"; |
| 71 | +var price = 2.99; |
| 72 | + |
| 73 | +Food.SayHello(logger, food, price); |
| 74 | +``` |
| 75 | + |
| 76 | +> [!NOTE] |
| 77 | +> There is no need to pass in an explicit |
| 78 | + [EventId](https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.eventid) |
| 79 | + while using |
| 80 | + [LoggerMessageAttribute](https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.loggermessageattribute). |
| 81 | + A durable `EventId` will be automatically assigned based on the hash of the |
| 82 | + method name during code generation. |
| 83 | + |
| 84 | +:heavy_check_mark: You can use |
| 85 | +[LogPropertiesAttribute](https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.logpropertiesattribute) |
| 86 | +from |
| 87 | +[Microsoft.Extensions.Telemetry.Abstractions](https://www.nuget.org/packages/Microsoft.Extensions.Telemetry.Abstractions/) |
| 88 | +if you need to log complex objects. Check out the [Logging with Complex |
| 89 | +Objects](./complex-objects/README.md) tutorial for more details. |
| 90 | + |
| 91 | +:stop_sign: You should avoid the extension methods from |
| 92 | +[LoggerExtensions](https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.loggerextensions), |
| 93 | +these methods are not optimized for performance. |
| 94 | + |
| 95 | +> [!WARNING] |
| 96 | +> The following code has bad performance due to |
| 97 | + [boxing](https://learn.microsoft.com/dotnet/csharp/programming-guide/types/boxing-and-unboxing): |
| 98 | + |
| 99 | +```csharp |
| 100 | +var food = "tomato"; |
| 101 | +var price = 2.99; |
| 102 | + |
| 103 | +logger.LogInformation("Hello from {food} {price}.", food, price); |
| 104 | +``` |
| 105 | + |
| 106 | +Refer to the [logging performance |
| 107 | +benchmark](../../test/Benchmarks/Logs/LogBenchmarks.cs) for more details. |
| 108 | + |
| 109 | +## Logger Management |
| 110 | + |
| 111 | +In order to use |
| 112 | +[`ILogger`](https://docs.microsoft.com/dotnet/api/microsoft.extensions.logging.ilogger) |
| 113 | +interface (including |
| 114 | +[`ILogger<TCategoryName>`](https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.ilogger-1)), |
| 115 | +you need to first get a logger. How to get a logger depends on two things: |
| 116 | + |
| 117 | +* The type of application you are building. |
| 118 | +* The place where you want to log. |
| 119 | + |
| 120 | +Here is the rule of thumb: |
| 121 | + |
| 122 | +* If you are building an application with [dependency injection |
| 123 | + (DI)](https://learn.microsoft.com/dotnet/core/extensions/dependency-injection) |
| 124 | + (e.g. [ASP.NET Core](https://learn.microsoft.com/aspnet/core) and [.NET |
| 125 | + Worker](https://learn.microsoft.com/dotnet/core/extensions/workers)), in most |
| 126 | + cases you should use the logger provided by DI, there are special cases when |
| 127 | + you want log before DI logging pipeline is available or after DI logging |
| 128 | + pipeline is disposed. Refer to the [.NET official |
| 129 | + document](https://learn.microsoft.com/dotnet/core/extensions/logging#integration-with-hosts-and-dependency-injection) |
| 130 | + and [Getting Started with OpenTelemetry .NET Logs in 5 Minutes - ASP.NET Core |
| 131 | + Application](./getting-started-aspnetcore/README.md) tutorial to learn more. |
| 132 | +* If you are building an application without DI, create a `LoggerFactory` |
| 133 | + instance and configure OpenTelemetry to work with it. Refer to the [Getting |
| 134 | + Started with OpenTelemetry .NET Logs in 5 Minutes - Console |
| 135 | + Application](./getting-started-console/README.md) tutorial to learn more. |
| 136 | + |
| 137 | +:stop_sign: You should avoid creating `LoggerFactory` instances too frequently, |
| 138 | +`LoggerFactory` is fairly expensive and meant to be reused throughout the |
| 139 | +application. For most applications, one `LoggerFactory` instance per process |
| 140 | +would be sufficient. |
| 141 | + |
| 142 | +:heavy_check_mark: You should properly manage the lifecycle of |
| 143 | +[LoggerFactory](https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.loggerfactory) |
| 144 | +instances if they are created by you. |
| 145 | + |
| 146 | +* If you forget to dispose the `LoggerFactory` instance before the application |
| 147 | + ends, logs might get dropped due to the lack of proper flush. |
| 148 | +* If you dispose the `LoggerFactory` instance too early, any subsequent logging |
| 149 | + API invocation associated with the logger factory could become no-op (i.e. no |
| 150 | + logs will be emitted). |
| 151 | + |
| 152 | +:heavy_check_mark: You should use the fully qualified class name as the log |
| 153 | +category name. Refer to the [.NET official |
| 154 | +document](https://learn.microsoft.com/dotnet/core/extensions/logging#log-category) |
| 155 | +to learn more. |
| 156 | + |
| 157 | +## Log Correlation |
| 158 | + |
| 159 | +In OpenTelemetry, logs are automatically correlated to traces. Check the [Log |
| 160 | +Correlation](./correlation/README.md) tutorial to learn more. |
| 161 | + |
| 162 | +## Log Enrichment |
| 163 | + |
| 164 | +TBD |
| 165 | + |
| 166 | +## Log Filtering |
| 167 | + |
| 168 | +Check the [Customizing OpenTelemetry .NET SDK for |
| 169 | +Logs](./customizing-the-sdk/README.md#log-filtering) document to learn more. |
| 170 | + |
| 171 | +## Log Redaction |
| 172 | + |
| 173 | +Logs might contain sensitive information such as passwords and credit card |
| 174 | +numbers, proper redaction is required to prevent privacy and security incidents. |
| 175 | +Check the [Log Redaction](./redaction/README.md) tutorial to learn more. |
0 commit comments