Logging complex objects

Learn how to log complex objects with OpenTelemetry .NET

Estás viendo la versión en inglés de está página porque aún no ha sido traducida. ¿Te interesa ayudar? Mira en Contribuir.

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