@@ -25,33 +25,17 @@ OpenTelemetry .NET:
2525* [ Getting Started - Console Application] ( ./getting-started-console/README.md )
2626* [ Logging with Complex Objects] ( ./complex-objects/README.md )
2727
28- ## Structured Logging
29-
30- :heavy_check_mark : You should use structured logging.
31-
32- * Structured logging is more efficient than unstructured logging.
33- * Filtering and redaction can happen on individual key-value pairs instead of
34- the entire log message.
35- * Storage and indexing are more efficient.
36- * Structured logging makes it easier to manage and consume logs.
37-
38- :stop_sign : You should avoid string interpolation.
39-
40- > [ !WARNING]
41- > The following code has bad performance due to [ string
42- interpolation] ( https://learn.microsoft.com/dotnet/csharp/tutorials/string-interpolation ) :
43-
44- ``` csharp
45- var food = " tomato" ;
46- var price = 2 . 99 ;
28+ ## Logging API
4729
48- logger .LogInformation ($" Hello from {food } {price }." );
49- ```
30+ ### ILogger
5031
51- Refer to the [ logging performance
52- benchmark] ( ../../test/Benchmarks/Logs/LogBenchmarks.cs ) for more details.
32+ .NET supports high performance, structured logging via the
33+ [ ` Microsoft.Extensions.Logging.ILogger ` ] ( https://docs.microsoft.com/dotnet/api/microsoft.extensions.logging.ilogger )
34+ interface (including
35+ [ ` ILogger<TCategoryName> ` ] ( https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.ilogger-1 ) )
36+ to help monitor application behavior and diagnose issues.
5337
54- ## Package Version
38+ #### Package Version
5539
5640:heavy_check_mark : You should always use the
5741[ ` ILogger ` ] ( https://docs.microsoft.com/dotnet/api/microsoft.extensions.logging.ilogger )
@@ -69,16 +53,6 @@ package, regardless of the .NET runtime version being used:
6953 backward compatibility on ` Microsoft.Extensions.Logging ` even during major
7054 version bumps, so compatibility is not a concern here.
7155
72- ## Logging API
73-
74- ### ILogger
75-
76- .NET supports high performance, structured logging via the
77- [ ` Microsoft.Extensions.Logging.ILogger ` ] ( https://docs.microsoft.com/dotnet/api/microsoft.extensions.logging.ilogger )
78- interface (including
79- [ ` ILogger<TCategoryName> ` ] ( https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.ilogger-1 ) )
80- to help monitor application behavior and diagnose issues.
81-
8256#### Get Logger
8357
8458In order to use the ` ILogger ` interface, you need to first get a logger. How to
@@ -125,7 +99,38 @@ are not super expensive, they still come with CPU and memory cost, and are meant
12599to be reused throughout the application. Refer to the [ logging performance
126100benchmark] ( ../../test/Benchmarks/Logs/LogBenchmarks.cs ) for more details.
127101
128- #### Use Logger
102+ #### Write log messages
103+
104+ :heavy_check_mark : You should use structured logging.
105+
106+ * Structured logging is more efficient than unstructured logging.
107+ * Filtering and redaction can happen on individual key-value pairs instead of
108+ the entire log message.
109+ * Storage and indexing are more efficient.
110+ * Structured logging makes it easier to manage and consume logs.
111+
112+ ``` csharp
113+ var food = " tomato" ;
114+ var price = 2 . 99 ;
115+
116+ logger .LogInformation (" Hello from {food} {price}." , food , price );
117+ ```
118+
119+ :stop_sign : You should avoid string interpolation.
120+
121+ > [ !WARNING]
122+ > The following code has bad performance due to [ string
123+ interpolation] ( https://learn.microsoft.com/dotnet/csharp/tutorials/string-interpolation ) :
124+
125+ ``` csharp
126+ var food = " tomato" ;
127+ var price = 2 . 99 ;
128+
129+ logger .LogInformation ($" Hello from {food } {price }." );
130+ ```
131+
132+ Refer to the [ logging performance
133+ benchmark] ( ../../test/Benchmarks/Logs/LogBenchmarks.cs ) for more details.
129134
130135:heavy_check_mark : You should use [ compile-time logging source
131136generation] ( https://docs.microsoft.com/dotnet/core/extensions/logger-message-generator )
@@ -222,6 +227,111 @@ code is now depending on which logger is being enabled, not to mention the
222227argument evaluation might have significant side effects that are now depending
223228on the logging configuration.
224229
230+ :heavy_check_mark : You should use a dedicated parameter to log exceptions when
231+ using the compile-time source generator.
232+
233+ ``` csharp
234+ var food = " tomato" ;
235+ var price = 2 . 99 ;
236+
237+ try
238+ {
239+ // Execute some logic
240+
241+ logger .SayHello (food , price );
242+ }
243+ catch (Exception ex )
244+ {
245+ logger .SayHelloFailure (ex , food , price );
246+ }
247+
248+ internal static partial class LoggerExtensions
249+ {
250+ [LoggerMessage (Level = LogLevel .Information , Message = " Hello from {food} {price}." )]
251+ public static partial void SayHello (this ILogger logger , string food , double price );
252+
253+ [LoggerMessage (Level = LogLevel .Error , Message = " Could not say hello from {food} {price}." )]
254+ public static partial void SayHelloFailure (this ILogger logger , Exception exception , string food , double price );
255+ }
256+ ```
257+
258+ > [ !NOTE]
259+ > When using the compile-time source generator the first ` Exception ` parameter
260+ > detected is automatically given special handling. It ** SHOULD NOT** be part of
261+ > the message template. For details see: [ Log method
262+ > anatomy] ( https://learn.microsoft.com/dotnet/core/extensions/logger-message-generator#log-method-anatomy ) .
263+
264+ :heavy_check_mark : You should use the dedicated overloads to log exceptions when
265+ using the logging extensions methods.
266+
267+ ``` csharp
268+ var food = " tomato" ;
269+ var price = 2 . 99 ;
270+
271+ try
272+ {
273+ // Execute some logic
274+
275+ logger .LogInformation (" Hello from {food} {price}." , food , price );
276+ }
277+ catch (Exception ex )
278+ {
279+ logger .LogError (ex , " Could not say hello from {food} {price}." , food , price );
280+ }
281+ ```
282+
283+ :stop_sign : You should avoid adding exception details into the message template.
284+
285+ You want to use the correct ` Exception ` APIs because the OpenTelemetry
286+ Specification [ defines dedicated
287+ attributes] ( https://github.com/open-telemetry/semantic-conventions/blob/main/docs/exceptions/exceptions-logs.md )
288+ for ` Exception ` details. The following examples show what ** NOT** to do. In
289+ these cases the details won't be lost, but the dedicated attributes also won't
290+ be added.
291+
292+ ``` csharp
293+ var food = " tomato" ;
294+ var price = 2 . 99 ;
295+
296+ try
297+ {
298+ // Execute some logic
299+
300+ logger .SayHello (food , price );
301+ }
302+ catch (Exception ex )
303+ {
304+ logger .SayHelloFailure (food , price , ex .Message );
305+ }
306+
307+ internal static partial class LoggerExtensions
308+ {
309+ [LoggerMessage (Level = LogLevel .Information , Message = " Hello from {food} {price}." )]
310+ public static partial void SayHello (this ILogger logger , string food , double price );
311+
312+ // BAD - Exception should not be part of the message template. Use the dedicated parameter.
313+ [LoggerMessage (Level = LogLevel .Error , Message = " Could not say hello from {food} {price} {message}." )]
314+ public static partial void SayHelloFailure (this ILogger logger , string food , double price , string message );
315+ }
316+ ```
317+
318+ ``` csharp
319+ var food = " tomato" ;
320+ var price = 2 . 99 ;
321+
322+ try
323+ {
324+ // Execute some logic
325+
326+ logger .LogInformation (" Hello from {food} {price}." , food , price );
327+ }
328+ catch (Exception ex )
329+ {
330+ // BAD - Exception should not be part of the message template. Use the dedicated parameter.
331+ logger .LogError (" Could not say hello from {food} {price} {message}." , food , price , ex .Message );
332+ }
333+ ```
334+
225335## LoggerFactory
226336
227337In many cases, you can use [ ILogger] ( #ilogger ) without having to interact with
0 commit comments