# Getting Started with OpenTelemetry .NET Logs in 5 Minutes - ASP.NET Core Application

First, download and install the [.NET
SDK](https://dotnet.microsoft.com/download) on your computer.

Create a new web application:

```sh
dotnet new web -o aspnetcoreapp
cd aspnetcoreapp
```

Install the
[OpenTelemetry.Exporter.Console](../../../src/OpenTelemetry.Exporter.Console/README.md)
and
[OpenTelemetry.Extensions.Hosting](../../../src/OpenTelemetry.Extensions.Hosting/README.md)
packages:

```sh
dotnet add package OpenTelemetry.Exporter.Console
dotnet add package OpenTelemetry.Extensions.Hosting
```

Update the `Program.cs` file with the code from [Program.cs](./Program.cs).

Run the application (using `dotnet run`) and then browse to the URL shown in the
console for your application (e.g. `http://localhost:5000`). You should see the
logs output from the console:

```text
LogRecord.Timestamp:               2023-09-06T22:59:17.9787564Z
LogRecord.CategoryName:            getting-started-aspnetcore
LogRecord.Severity:                Info
LogRecord.SeverityText:            Information
LogRecord.Body:                    Starting the app...
LogRecord.Attributes (Key:Value):
    OriginalFormat (a.k.a Body): Starting the app...
LogRecord.EventId:                 225744744
LogRecord.EventName:               StartingApp

...

LogRecord.Timestamp:               2023-09-06T22:59:18.0644378Z
LogRecord.CategoryName:            Microsoft.Hosting.Lifetime
LogRecord.Severity:                Info
LogRecord.SeverityText:            Information
LogRecord.Body:                    Now listening on: {address}
LogRecord.Attributes (Key:Value):
    address: http://localhost:5000
    OriginalFormat (a.k.a Body): Now listening on: {address}
LogRecord.EventId:                 14
LogRecord.EventName:               ListeningOnAddress

...

LogRecord.Timestamp:               2023-09-06T23:00:46.1639248Z
LogRecord.TraceId:                 3507087d60ae4b1d2f10e68f4e40784a
LogRecord.SpanId:                  c51be9f19c598b69
LogRecord.TraceFlags:              None
LogRecord.CategoryName:            Program
LogRecord.Severity:                Info
LogRecord.SeverityText:            Information
LogRecord.Body:                    Food `{name}` price changed to `{price}`.
LogRecord.Attributes (Key:Value):
    name: artichoke
    price: 9.99
    OriginalFormat (a.k.a Body): Food `{name}` price changed to `{price}`.
LogRecord.EventId:                 344095174
LogRecord.EventName:               FoodPriceChanged

...
```

Congratulations! You are now collecting logs using OpenTelemetry.

What does the above program do?

The program has cleared the default [logging
providers](https://learn.microsoft.com/dotnet/core/extensions/logging-providers)
then added OpenTelemetry as a logging provider to the ASP.NET Core logging
pipeline. OpenTelemetry SDK is then configured with a
[ConsoleExporter](../../../src/OpenTelemetry.Exporter.Console/README.md) to
export the logs to the console for demonstration purpose (note: ConsoleExporter
is not intended for production usage, other exporters such as [OTLP
Exporter](../../../src/OpenTelemetry.Exporter.OpenTelemetryProtocol/README.md)
should be used instead). From the console output we can see logs from both our
logger and the ASP.NET Core framework loggers, as indicated by the
`LogRecord.CategoryName`.

The example has demonstrated the best practice from ASP.NET Core by injecting
generic `ILogger<T>`:

```csharp
app.MapGet("/", (ILogger<Program> logger) =>
{
    logger.FoodPriceChanged("artichoke", 9.99);

    return "Hello from OpenTelemetry Logs!";
});
```

Following the .NET logging best practice, [compile-time logging source
generation](https://docs.microsoft.com/dotnet/core/extensions/logger-message-generator)
has been used across the example, which delivers high performance, structured
logging, and type-checked parameters:

```csharp
internal static partial class LoggerExtensions
{
    [LoggerMessage(LogLevel.Information, "Starting the app...")]
    public static partial void StartingApp(this ILogger logger);

    [LoggerMessage(LogLevel.Information, "Food `{name}` price changed to `{price}`.")]
    public static partial void FoodPriceChanged(this ILogger logger, string name, double price);
}
```

For logs that occur between `builder.Build()` and `app.Run()` when injecting a
generic `ILogger<T>` is not an option, `app.Logger` is used instead:

```csharp
app.Logger.StartingApp();
```

> [!NOTE]
> There are cases where logging is needed before the [dependency injection
(DI)](https://learn.microsoft.com/dotnet/core/extensions/dependency-injection)
logging pipeline is available (e.g. before `builder.Build()`) or after the DI
logging pipeline is disposed (e.g. after `app.Run()`). The common practice is to
use a separate logging pipeline by creating a `LoggerFactory` instance.
>
> Refer to the [Getting Started with OpenTelemetry .NET Logs in 5 Minutes -
Console Application](../getting-started-console/README.md) tutorial to learn
more about how to create a `LoggerFactory` instance and configure OpenTelemetry
to work with it.

## Learn more

* [Logging in C# and .NET](https://learn.microsoft.com/dotnet/core/extensions/logging)
* [Logging with Complex Objects](../complex-objects/README.md)
* [Customizing the OpenTelemetry .NET SDK](../customizing-the-sdk/README.md)
* [Extending the OpenTelemetry .NET SDK](../extending-the-sdk/README.md)
