diff --git a/OpenTelemetry.sln b/OpenTelemetry.sln index e21bab6ad63..7e2a7912713 100644 --- a/OpenTelemetry.sln +++ b/OpenTelemetry.sln @@ -313,6 +313,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Metrics", "Metrics", "{1C45 src\Shared\Metrics\Base2ExponentialBucketHistogramHelper.cs = src\Shared\Metrics\Base2ExponentialBucketHistogramHelper.cs EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "getting-started-aspnetcore", "docs\logs\getting-started-aspnetcore\getting-started-aspnetcore.csproj", "{99B4D965-8782-4694-8DFA-B7A3630CEF60}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -583,6 +585,10 @@ Global {777C04B8-1BD5-43D7-B3CD-D2189DFABCF3}.Debug|Any CPU.Build.0 = Debug|Any CPU {777C04B8-1BD5-43D7-B3CD-D2189DFABCF3}.Release|Any CPU.ActiveCfg = Release|Any CPU {777C04B8-1BD5-43D7-B3CD-D2189DFABCF3}.Release|Any CPU.Build.0 = Release|Any CPU + {99B4D965-8782-4694-8DFA-B7A3630CEF60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {99B4D965-8782-4694-8DFA-B7A3630CEF60}.Debug|Any CPU.Build.0 = Debug|Any CPU + {99B4D965-8782-4694-8DFA-B7A3630CEF60}.Release|Any CPU.ActiveCfg = Release|Any CPU + {99B4D965-8782-4694-8DFA-B7A3630CEF60}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -630,6 +636,7 @@ Global {494902DD-C63F-48E0-BED3-B58EFB4051C8} = {A49299FB-C5CD-4E0E-B7E1-B7867BBD67CC} {A0CB9A10-F22D-4E66-A449-74B3D0361A9C} = {A49299FB-C5CD-4E0E-B7E1-B7867BBD67CC} {1C459B5B-C702-46FF-BF1A-EE795E420FFA} = {A49299FB-C5CD-4E0E-B7E1-B7867BBD67CC} + {99B4D965-8782-4694-8DFA-B7A3630CEF60} = {3862190B-E2C5-418E-AFDC-DB281FB5C705} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {55639B5C-0770-4A22-AB56-859604650521} diff --git a/README.md b/README.md index b4fd74a11ac..e0bcc7c7cc7 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ repo. If you are new here, please read the getting started docs: -* Logs: [Console](./docs/logs/getting-started-console/README.md) +* Logs: [ASP.NET Core](./docs/logs/getting-started-aspnetcore/README.md) | [Console](./docs/logs/getting-started-console/README.md) * Metrics: [ASP.NET Core](./docs/metrics/getting-started-aspnetcore/README.md) | [Console](./docs/metrics/getting-started-console/README.md) * Traces: [ASP.NET Core](./docs/trace/getting-started-aspnetcore/README.md) | diff --git a/docs/logs/getting-started-aspnetcore/Program.cs b/docs/logs/getting-started-aspnetcore/Program.cs new file mode 100644 index 00000000000..e5e150b162d --- /dev/null +++ b/docs/logs/getting-started-aspnetcore/Program.cs @@ -0,0 +1,64 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using OpenTelemetry.Logs; +using OpenTelemetry.Resources; + +var builder = WebApplication.CreateBuilder(args); + +// Remove default providers and add OpenTelemetry logging provider +// For instructional purposes only, disable the default .NET console logging provider to +// use the verbose OpenTelemetry console exporter instead. For most development +// and production scenarios the default console provider works well and there is no need to +// clear these providers. +builder.Logging.ClearProviders(); + +builder.Logging.AddOpenTelemetry(logging => +{ + logging.IncludeScopes = true; + + var resourceBuilder = ResourceBuilder + .CreateDefault() + .AddService(builder.Environment.ApplicationName); + + logging.SetResourceBuilder(resourceBuilder) + + // ConsoleExporter is used for demo purpose only. + // In production environment, ConsoleExporter should be replaced with other exporters (e.g. OTLP Exporter). + .AddConsoleExporter(); +}); + +var app = builder.Build(); + +app.MapGet("/", (ILogger logger) => +{ + logger.FoodPriceChanged("artichoke", 9.99); + + return "Hello from OpenTelemetry Logs!"; +}); + +app.Logger.StartingApp(); + +app.Run(); + +public static partial class ApplicationLogs +{ + [LoggerMessage(EventId = 1, Level = LogLevel.Information, Message = "Starting the app...")] + public static partial void StartingApp(this ILogger logger); + + [LoggerMessage(EventId = 2, Level = LogLevel.Information, Message = "Food `{name}` price changed to `{price}`.")] + public static partial void FoodPriceChanged(this ILogger logger, string name, double price); +} diff --git a/docs/logs/getting-started-aspnetcore/README.md b/docs/logs/getting-started-aspnetcore/README.md new file mode 100644 index 00000000000..81b5407eee6 --- /dev/null +++ b/docs/logs/getting-started-aspnetcore/README.md @@ -0,0 +1,133 @@ +# 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: 1 +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: 2 +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). In addition, `OpenTelemetryLoggerOptions.IncludeScopes` +is enabled so the logs will include the [log +scopes](https://learn.microsoft.com/dotnet/core/extensions/logging#log-scopes). +From the console output we can see the log scopes that are coming from the +ASP.NET Core framework, and 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`: + +```csharp +app.MapGet("/", (ILogger 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 +public static partial class ApplicationLogs +{ + [LoggerMessage(EventId = 1, Level = LogLevel.Information, Message = "Starting the app...")] + public static partial void StartingApp(this ILogger logger); + + [LoggerMessage(EventId = 2, Level = LogLevel.Information, Message = "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` is not an option, `app.Logger` is used instead: + +```csharp +app.Logger.StartingApp(); +``` + +## Learn more + +* [Compile-time logging source + generation](https://docs.microsoft.com/dotnet/core/extensions/logger-message-generator) +* [Customizing the OpenTelemetry .NET SDK](../customizing-the-sdk/README.md) +* [Extending the OpenTelemetry .NET SDK](../extending-the-sdk/README.md) diff --git a/docs/logs/getting-started-aspnetcore/appsettings.Development.json b/docs/logs/getting-started-aspnetcore/appsettings.Development.json new file mode 100644 index 00000000000..770d3e93146 --- /dev/null +++ b/docs/logs/getting-started-aspnetcore/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "DetailedErrors": true, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/docs/logs/getting-started-aspnetcore/appsettings.json b/docs/logs/getting-started-aspnetcore/appsettings.json new file mode 100644 index 00000000000..10f68b8c8b4 --- /dev/null +++ b/docs/logs/getting-started-aspnetcore/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/docs/logs/getting-started-aspnetcore/getting-started-aspnetcore.csproj b/docs/logs/getting-started-aspnetcore/getting-started-aspnetcore.csproj new file mode 100644 index 00000000000..826f2e1f489 --- /dev/null +++ b/docs/logs/getting-started-aspnetcore/getting-started-aspnetcore.csproj @@ -0,0 +1,12 @@ + + + + net6.0;net7.0 + + + + + + + + diff --git a/docs/metrics/getting-started-aspnetcore/getting-started-aspnetcore.csproj b/docs/metrics/getting-started-aspnetcore/getting-started-aspnetcore.csproj index 1956f427004..78c5ab06672 100644 --- a/docs/metrics/getting-started-aspnetcore/getting-started-aspnetcore.csproj +++ b/docs/metrics/getting-started-aspnetcore/getting-started-aspnetcore.csproj @@ -2,8 +2,6 @@ net6.0;net7.0 - enable - enable diff --git a/docs/trace/getting-started-aspnetcore/getting-started-aspnetcore.csproj b/docs/trace/getting-started-aspnetcore/getting-started-aspnetcore.csproj index 1956f427004..78c5ab06672 100644 --- a/docs/trace/getting-started-aspnetcore/getting-started-aspnetcore.csproj +++ b/docs/trace/getting-started-aspnetcore/getting-started-aspnetcore.csproj @@ -2,8 +2,6 @@ net6.0;net7.0 - enable - enable