# 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.