# Implementing APM and Logging with Serilog and ELK Stack in .NET Core Minimal API ### 1. Introduction: > This guide outlines the integration of Application Performance Monitoring (APM) and logging into a .NET Core Minimal API application using ELK Stack (Elasticsearch, Logstash, Kibana) and Serilog for structured logging. > ### 2. Prerequisites: A running ELK Stack instance. Basic understanding of .NET Core and C#. ```bash version: '3.9' services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:7.17.1 environment: - discovery.type=single-node - cluster.name=docker-cluster - bootstrap.memory_lock=true ports: - 9200:9200 volumes: - esdata:/usr/share/elasticsearch/data kibana: image: docker.elastic.co/kibana/kibana:7.17.1 depends_on: - elasticsearch ports: - 5601:5601 environment: - ELASTICSEARCH_URL=http://elasticsearch:9200 apm-server: image: docker.elastic.co/apm/apm-server:7.17.1 cap_add: - CHOWN - DAC_OVERRIDE - SETGID - SETUID cap_drop: - ALL depends_on: - elasticsearch ports: - 8200:8200 environment: - setup.kibana.host=kibana:5601 - setup.template.settings.index.number_of_replicas=0 - output.elasticsearch.hosts=["elasticsearch:9200"] - apm-server.rum.enabled=true # Optional: Enable RUM (Real User Monitoring) volumes: esdata: {} # Persistent storage for Elasticsearch data ``` ### 3. Project Setup: #### 1. Create a new .NET Core 7 Minimal API project: ```bash dotnet new webapi -o ElkIntegrationApi ``` Install required NuGet packages: ```bash cd ElkIntegrationApi dotnet add package Serilog dotnet add package Serilog.Sinks.File dotnet add package Serilog.Sinks.Elasticsearch dotnet add package Elastic.Apm.AspNetCore ``` #### 2. Configuration with appsettings.json: Create an appsettings.json file with the following structure, replacing placeholders with your actual values: ```Json { "Logging": { "MinimumLevel": "Information", // Optional: Set the minimum logging level "Serilog": { "WriteTo": [ { "Name": "File", "Args": { "path": "logs/elk_logs.txt", "rollingInterval": "Day" // Optional: Set rolling interval for file logs } }, { "Name": "Elasticsearch", "Args": { "url": "http://localhost:9200", // Replace with your Elasticsearch URL "autoTemplate": "Default", // Optional: Set auto-template name "buffer": { "bufferSize": 100, "flushPeriod": "00:00:05" // Optional: Set flush period in format "hh:mm:ss" }, "indexFormat": "elk-logs-{0:yyyyMMdd}" // Optional: Set dynamic index format } } ] } }, "ElasticApm": { "ServiceName": "MyAwesomeService", // Replace with your service name "ServerUrls": "http://localhost:8200", // Replace with your APM server URL(s) "Environment": "Development" // Optional: Set environment # Additional APM settings (optional) } } ``` #### 3. Program.cs with Combined Configuration: Modify your Program.cs file to read the configuration from appsettings.json and utilize APM settings: ```code using System.Text.Json; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Serilog; using Elastic.Apm.AspNetCore; public class Program { public static void Main(string[] args) { var configuration = new ConfigurationBuilder() .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .Build(); // Read Serilog and APM configuration from appsettings.json Log.Logger = new LoggerConfiguration() .ReadFrom.Configuration(configuration) .CreateLogger(); var app = WebApplication.Create(args); app.UseAllElasticApm(); // Enables auto-instrumentation (optional) app.MapGet("/", () => { using (var transaction = Apm.Current?.StartTransaction("GET /")) // Start APM transaction { Log.Information("Hello from ElkIntegrationApi!"); transaction?.Annotate("http.method", "GET"); // Add APM annotations (optional) transaction?.Annotate("http.url", "/"); // Add APM annotations (optional) return Results.Content("Hello from ElkIntegrationApi!", "text/plain"); } }); app.Run(); } } ``` ### 4. Explanation: * The **appsettings.json** file stores both Serilog and Elastic APM configuration options. * Serilog settings define sinks and options for log writing to file and Elasticsearch. * Elastic APM settings provide the service name, server URLs, and an optional environment. * The Program.cs code reads the configuration and creates the Serilog logger based on Serilog settings. * The **app.UseAllElasticApm()** call enables auto-instrumentation for common scenarios using APM settings. * **Key Auto-instrumented Areas:** * **HTTP Requests and Responses:** Tracks the duration, status codes, request methods, and URLs of HTTP requests. Captures details like request headers, body size, and response headers (configurable). * **ASP.NET Core MVC/Razor Pages Actions:** Measures the execution time of controller actions and Razor Pages handlers. Provides context information like action name, route values, and view rendering time (if applicable). * **Database Calls (ADO.NET, Entity Framework):** Monitors the duration and type of database operations (e.g., SELECT, UPDATE, DELETE). Captures command text and parameters (when available). * **Exceptions:** Records exception type, message, stack trace, and associated HTTP request information (if any). * **ASP.NET Core Middleware:** Tracks the execution time of custom middleware components in your application pipeline. * **Customization and Configuration:** While app.UseAllElasticApm() provides a convenient starting point, you can tailor auto-instrumentation behavior using various configuration options: * **Disabling Auto-instrumentation:** If you need granular control, call app.UseElasticApm(configuration => { configuration.AutoInstrument = false; }); to disable auto-instrumentation completely. * **Enabling/Disabling Specific Areas:** Use options like configuration.CaptureRequestData = false; or configuration.CaptureMvc = false; to control which areas are automatically captured. * **Custom Instrumentation:** For scenarios beyond pre-defined areas, employ the ITracer interface to manually instrument specific code sections using methods like StartTransaction and EndTransaction. * Within the endpoint handler, we create an APM transaction to track the specific request and add annotations for additional context.