Logging complex objects

Learn how to log complex objects with OpenTelemetry .NET

You are viewing the English version of this page because it has not yet been fully translated. Interested in helping out? See Contributing.

In the Getting Started with OpenTelemetry .NET Logs - Console guide, we learned how to log primitive data types. This guide will show you how to log complex objects.

Complex object logging in .NET

Complex object logging was introduced in .NET 8.0 through the LogPropertiesAttribute. This attribute and the corresponding code generation logic are provided by an extension package called Microsoft.Extensions.Telemetry.Abstractions.

Prerequisites

Implementation steps

1. Install the required package

Install the Microsoft.Extensions.Telemetry.Abstractions package:

dotnet add package Microsoft.Extensions.Telemetry.Abstractions 

2. Define a complex data type

Create a struct to represent your complex object:

public struct FoodRecallNotice {  public string? BrandName { get; set; }  public string? ProductDescription { get; set; }  public string? ProductType { get; set; }  public string? RecallReasonDescription { get; set; }  public string? CompanyName { get; set; } } 

3. Create a logger extension method with LogPropertiesAttribute

Define an extension method for your logger that uses the Define an extension method to ILogger that uses the

using Microsoft.Extensions.Logging;  internal static partial class LoggerExtensions {  [LoggerMessage(LogLevel.Critical)]  public static partial void FoodRecallNotice(  this ILogger logger,  [LogProperties(OmitReferenceName = true)] in FoodRecallNotice foodRecallNotice); } 

The [LogProperties(OmitReferenceName = true)] attribute instructs the source generator to:

  • Include all properties of the FoodRecallNotice as individual log attributes
  • Omit the reference name (the parameter name) from the attribute keys

4. Log the complex object

Create an instance of your complex object and log it:

// Create a complex object var foodRecallNotice = new FoodRecallNotice {  BrandName = "Contoso",  ProductDescription = "Salads",  ProductType = "Food & Beverages",  RecallReasonDescription = "due to a possible health risk from Listeria monocytogenes",  CompanyName = "Contoso Fresh Vegetables, Inc.", };  // Log the complex object logger.FoodRecallNotice(foodRecallNotice); 

5. Run the application

Run the application, for example using dotnet run, and you should see the log output on the console:

LogRecord.Timestamp: 2024-01-12T19:01:16.0604084Z LogRecord.CategoryName: Program LogRecord.Severity: Fatal LogRecord.SeverityText: Critical LogRecord.FormattedMessage: LogRecord.Body: LogRecord.Attributes (Key:Value):  CompanyName: Contoso Fresh Vegetables, Inc.  RecallReasonDescription: due to a possible health risk from Listeria monocytogenes  ProductType: Food & Beverages  ProductDescription: Salads  BrandName: Contoso LogRecord.EventId: 252550133 LogRecord.EventName: FoodRecallNotice 

Notice that each property of the FoodRecallNotice object appears as a separate attribute in the log record.

LogPropertiesAttribute options

The LogPropertiesAttribute provides several options to control how properties are included in logs:

  • OmitReferenceName: When set to true, the parameter name is omitted from attribute keys. In the example above, attribute keys are just the property names (for example, “BrandName”) rather than “foodRecallNotice.BrandName”.

  • IncludeProperties: Used to specify which properties should be included. If not specified, all properties are included.

  • ExcludeProperties: Used to specify which properties should be excluded from logging.

  • IncludeSensitive: When set to true, properties marked with [Sensitive] attribute are included in logs. The default is false.

Complete example

Here’s a complete example that puts everything together:

using System; using Microsoft.Extensions.Logging; using OpenTelemetry; using OpenTelemetry.Logs;  // Complex object definition public struct FoodRecallNotice {  public string? BrandName { get; set; }  public string? ProductDescription { get; set; }  public string? ProductType { get; set; }  public string? RecallReasonDescription { get; set; }  public string? CompanyName { get; set; } }  // Logger extension method internal static partial class LoggerExtensions {  [LoggerMessage(LogLevel.Critical)]  public static partial void FoodRecallNotice(  this ILogger logger,  [LogProperties(OmitReferenceName = true)] in FoodRecallNotice foodRecallNotice); }  // Main program class Program {  static void Main(string[] args)  {  // Create a logger factory with OpenTelemetry  using var loggerFactory = LoggerFactory.Create(builder =>  {  builder.AddOpenTelemetry(options =>  {  options.AddConsoleExporter();  });  });   // Get a logger instance  var logger = loggerFactory.CreateLogger<Program>();   // Create a complex object  var foodRecallNotice = new FoodRecallNotice  {  BrandName = "Contoso",  ProductDescription = "Salads",  ProductType = "Food & Beverages",  RecallReasonDescription = "due to a possible health risk from Listeria monocytogenes",  CompanyName = "Contoso Fresh Vegetables, Inc.",  };   // Log the complex object  logger.FoodRecallNotice(foodRecallNotice);   Console.WriteLine("Press any key to exit");  Console.ReadKey();  } } 

Learn more