.NET Core / C# Integration Guide
Overview
This guide explains how to integrate the AmpliServ Backend SDK with your .NET Core / C# application. The integration enables JSON logging, correlation ID propagation, and automatic log collection for the AmpliServ SDK.
Prerequisites
- .NET Core 3.1 or higher (.NET 6/7/8 recommended)
- Visual Studio 2022 or VS Code
- AmpliServ SDK binary
Step 1: Create a New .NET Project
dotnet new webapi -n YourErpBackend
cd YourErpBackend
Step 2: Add Required NuGet Packages
# Serilog for structured JSON logging
dotnet add package Serilog
dotnet add package Serilog.AspNetCore
dotnet add package Serilog.Sinks.File
dotnet add package Serilog.Formatting.Compact
# For correlation ID
dotnet add package CorrelationId
Or add to your .csproj file:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Serilog.Formatting.Compact" Version="2.0.0" />
<PackageReference Include="CorrelationId" Version="3.0.1" />
</ItemGroup>
</Project>
Step 3: Configure Serilog JSON Logging
Create appsettings.json:
{
"Logging": {
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
}
}
},
"AllowedHosts": "*",
"Application": {
"Name": "YourErpBackend",
"Environment": "development"
}
}
Step 4: Configure Program.cs
using Serilog;
using Serilog.Formatting.Compact;
using CorrelationId;
var builder = WebApplication.CreateBuilder(args);
// Configure Serilog for JSON logging
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(builder.Configuration)
.Enrich.FromLogContext()
.Enrich.WithProperty("Application", builder.Configuration["Application:Name"])
.Enrich.WithProperty("Environment", builder.Configuration["Application:Environment"])
.WriteTo.Console(new CompactJsonFormatter())
.WriteTo.File(
new CompactJsonFormatter(),
"app.log", // This is the file AmpliServ SDK will read
rollingInterval: RollingInterval.Day,
retainedFileCountLimit: 7
)
.CreateLogger();
builder.Host.UseSerilog();
// Add services
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Add Correlation ID support
builder.Services.AddCorrelationId(options =>
{
options.Header = "X-Correlation-ID";
options.IncludeInResponse = true;
options.UpdateTraceIdentifier = true;
});
var app = builder.Build();
// Configure pipeline
app.UseCorrelationId(); // Must be early in pipeline
// Custom correlation ID middleware to add to logging context
app.Use(async (context, next) =>
{
var correlationId = context.TraceIdentifier;
using (LogContext.PushProperty("CorrelationId", correlationId))
{
var logger = context.RequestServices.GetRequiredService<ILogger<Program>>();
logger.LogInformation(
"Incoming {Method} {Path} - CorrelationId: {CorrelationId}",
context.Request.Method,
context.Request.Path,
correlationId
);
await next();
}
});
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Step 5: Create Global Exception Handler (Captures Full Stack Traces)
Create Middlewares/GlobalExceptionMiddleware.cs:
using System.Net;
using System.Text.Json;
using Serilog.Context;
namespace YourErpBackend.Middlewares;
public class GlobalExceptionMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<GlobalExceptionMiddleware> _logger;
public GlobalExceptionMiddleware(RequestDelegate next, ILogger<GlobalExceptionMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private async Task HandleExceptionAsync(HttpContext context, Exception exception)
{
// This captures the FULL stack trace
_logger.LogError(exception, "An unhandled exception occurred: {Message}", exception.Message);
var response = new
{
message = exception.Message,
correlationId = context.TraceIdentifier,
timestamp = DateTime.UtcNow,
statusCode = (int)HttpStatusCode.InternalServerError
};
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
await context.Response.WriteAsync(JsonSerializer.Serialize(response));
}
}
Register the middleware in Program.cs:
app.UseMiddleware<GlobalExceptionMiddleware>();
Step 6: Create Test Controller
Create Controllers/TestController.cs:
using Microsoft.AspNetCore.Mvc;
using Serilog.Context;
namespace YourErpBackend.Controllers;
[ApiController]
[Route("api/[controller]")]
public class TestController : ControllerBase
{
private readonly ILogger<TestController> _logger;
public TestController(ILogger<TestController> logger)
{
_logger = logger;
}
[HttpGet("info")]
public IActionResult Info()
{
var correlationId = HttpContext.TraceIdentifier;
using (LogContext.PushProperty("CorrelationId", correlationId))
{
_logger.LogInformation("Info endpoint called - This is a normal log");
return Ok(new { message = "Info logged successfully", correlationId });
}
}
[HttpGet("warn")]
public IActionResult Warn()
{
var correlationId = HttpContext.TraceIdentifier;
using (LogContext.PushProperty("CorrelationId", correlationId))
{
_logger.LogWarning("Warning endpoint called - This is a warning log");
return Ok(new { message = "Warning logged", correlationId });
}
}
[HttpGet("error")]
public IActionResult Error()
{
var correlationId = HttpContext.TraceIdentifier;
using (LogContext.PushProperty("CorrelationId", correlationId))
{
_logger.LogInformation("Test error endpoint called");
try
{
SimulateDeepError();
}
catch (Exception ex)
{
// Full stack trace captured here
_logger.LogError(ex, "Test error occurred: {Message}", ex.Message);
return StatusCode(500, new
{
message = "Error triggered",
correlationId,
error = ex.Message
});
}
return Ok(new { message = "No error", correlationId });
}
}
[HttpGet("dashboard")]
public IActionResult Dashboard()
{
var correlationId = HttpContext.TraceIdentifier;
using (LogContext.PushProperty("CorrelationId", correlationId))
{
_logger.LogInformation("Dashboard endpoint called");
var dashboard = new
{
totalProducts = 100,
totalOrders = 50,
totalCustomers = 75,
totalRevenue = 250000,
correlationId
};
_logger.LogDebug("Dashboard data: {@Dashboard}", dashboard);
return Ok(dashboard);
}
}
private void SimulateDeepError()
{
Level1();
}
private void Level1()
{
Level2();
}
private void Level2()
{
Level3();
}
private void Level3()
{
throw new Exception("This is a test error with deep stack trace from .NET!");
}
}
Step 7: Create Business Service with Error Simulation
Create Services/BusinessService.cs:
using Serilog.Context;
namespace YourErpBackend.Services;
public interface IBusinessService
{
Task<decimal> CalculateOrderTotal(int orderId);
}
public class BusinessService : IBusinessService
{
private readonly ILogger<BusinessService> _logger;
public BusinessService(ILogger<BusinessService> logger)
{
_logger = logger;
}
public async Task<decimal> CalculateOrderTotal(int orderId)
{
var correlationId = CorrelationContext.GetCorrelationId();
using (LogContext.PushProperty("CorrelationId", correlationId))
{
_logger.LogInformation("Calculating total for order {OrderId}", orderId);
// Simulate business logic error
if (orderId <= 0)
{
var ex = new ArgumentException($"Invalid order ID: {orderId}");
_logger.LogError(ex, "Business validation failed");
throw ex;
}
await Task.Delay(100); // Simulate async operation
return 250.50m;
}
}
}
// Helper class for correlation context
public static class CorrelationContext
{
private static readonly AsyncLocal<string> _currentCorrelationId = new();
public static string GetCorrelationId()
{
return _currentCorrelationId.Value ?? "unknown";
}
public static void SetCorrelationId(string correlationId)
{
_currentCorrelationId.Value = correlationId;
}
}
Register the service in Program.cs:
builder.Services.AddScoped<IBusinessService, BusinessService>();
Step 8: Create Auth Controller (JWT Example)
Create Controllers/AuthController.cs:
using Microsoft.AspNetCore.Mvc;
using Serilog.Context;
using System.Security.Claims;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using System.Text;
namespace YourErpBackend.Controllers;
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
private readonly ILogger<AuthController> _logger;
private readonly IConfiguration _configuration;
public AuthController(ILogger<AuthController> logger, IConfiguration configuration)
{
_logger = logger;
_configuration = configuration;
}
[HttpPost("login")]
public IActionResult Login([FromBody] LoginRequest request)
{
var correlationId = HttpContext.TraceIdentifier;
using (LogContext.PushProperty("CorrelationId", correlationId))
{
_logger.LogInformation("Login attempt for user: {Username}", request.Username);
// Simple validation
if (request.Username == "admin" && request.Password == "admin123")
{
var token = GenerateJwtToken(request.Username);
_logger.LogInformation("User logged in successfully: {Username}", request.Username);
return Ok(new
{
token,
user = new { username = request.Username },
correlationId
});
}
_logger.LogWarning("Authentication failed: Invalid credentials for user: {Username}", request.Username);
return Unauthorized(new { message = "Invalid username or password", correlationId });
}
}
private string GenerateJwtToken(string username)
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your-super-secret-key-here-minimum-32-chars-long"));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var claims = new[]
{
new Claim(ClaimTypes.Name, username),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
var token = new JwtSecurityToken(
issuer: "YourErpBackend",
audience: "YourErpBackend",
claims: claims,
expires: DateTime.UtcNow.AddHours(24),
signingCredentials: credentials
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
public class LoginRequest
{
public string Username { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
}
Step 9: Configure AmpliServ SDK
Create run-agent.bat (Windows):
@echo off
set AMPLISERV_API_KEY=your-api-key-here
set AMPLISERV_COLLECTOR_MODE=file
set AMPLISERV_ENDPOINT=http://dev-backend.ampliserv.com/api/ingestion/v1/ingest
set AMPLISERV_SERVICE_NAME=ingestion-service
set AMPLISERV_ENV=development
set AMPLISERV_LOG_PATH=C:\path\to\your\project\app.log
set AMPLISERV_BATCH_SIZE=50
set AMPLISERV_FLUSH_INTERVAL_SEC=5
set AMPLISERV_INCIDENT_ENABLED=true
echo Starting AmpliServ Agent...
ampliserv-agent.exe
Create run-agent.sh (Linux/Mac):
#!/bin/bash
export AMPLISERV_API_KEY="your-api-key-here"
export AMPLISERV_COLLECTOR_MODE="file"
export AMPLISERV_ENDPOINT="http://dev-backend.ampliserv.com/api/ingestion/v1/ingest"
export AMPLISERV_SERVICE_NAME="ingestion-service"
export AMPLISERV_ENV="development"
export AMPLISERV_LOG_PATH="/path/to/your/project/app.log"
export AMPLISERV_BATCH_SIZE="50"
export AMPLISERV_FLUSH_INTERVAL_SEC="5"
export AMPLISERV_INCIDENT_ENABLED="true"
echo "Starting AmpliServ Agent..."
./ampliserv-agent
Step 10: Build and Run
Build the application:
dotnet build
Run the application:
dotnet run
In a separate terminal, start the AmpliServ SDK:
# Windows
run-agent.bat
# Linux/Mac
chmod +x run-agent.sh
./run-agent.sh
Step 11: Test the Integration
# Test info endpoint
curl http://localhost:5000/api/Test/info
# Test warning endpoint
curl http://localhost:5000/api/Test/warn
# Test error endpoint (generates stack trace)
curl http://localhost:5000/api/Test/error
# Test dashboard
curl http://localhost:5000/api/Test/dashboard
# Test login
curl -X POST http://localhost:5000/api/Auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123"}'
Sample JSON Log Output
{
"@t": "2026-03-23T10:30:45.1234567Z",
"@mt": "Test error occurred: {Message}",
"@l": "Error",
"@x": "System.Exception: This is a test error with deep stack trace from .NET!\n at YourErpBackend.Controllers.TestController.Level3() in /app/Controllers/TestController.cs:line 85\n at YourErpBackend.Controllers.TestController.Level2() in /app/Controllers/TestController.cs:line 79\n at YourErpBackend.Controllers.TestController.Level1() in /app/Controllers/TestController.cs:line 73\n at YourErpBackend.Controllers.TestController.SimulateDeepError() in /app/Controllers/TestController.cs:line 67\n at YourErpBackend.Controllers.TestController.Error() in /app/Controllers/TestController.cs:line 48",
"Message": "This is a test error with deep stack trace from .NET!",
"CorrelationId": "a1270278-7ef9-4e59-803e-e9862c257d73",
"Application": "YourErpBackend",
"Environment": "development"
}
Docker Deployment
Create Dockerfile:
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["YourErpBackend.csproj", "."]
RUN dotnet restore
COPY . .
RUN dotnet build -c Release -o /app/build
FROM build AS publish
RUN dotnet publish -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "YourErpBackend.dll"]
Docker Compose:
version: '3.8'
services:
app:
build: .
ports:
- "5000:80"
volumes:
- ./app.log:/app/app.log
environment:
- ASPNETCORE_ENVIRONMENT=Production
ampliserv-agent:
image: ampliserv/agent:latest
volumes:
- ./app.log:/app/app.log:ro
environment:
- AMPLISERV_API_KEY=${AMPLISERV_API_KEY}
- AMPLISERV_COLLECTOR_MODE=file
- AMPLISERV_LOG_PATH=/app/app.log
- AMPLISERV_SERVICE_NAME=dotnet-erp-backend
- AMPLISERV_ENV=production
Verification Checklist
- .NET application starts without errors
-
app.logfile is created with JSON content - Logs use CompactJsonFormatter format
- AmpliServ SDK shows "Starting AmpliServ Agent"
- SDK displays "Mode: file"
- Test endpoints produce logs
- Full stack traces are visible in logs
- Correlation ID is present in all logs
Common Issues
| Issue | Solution |
|---|---|
| Log file not created | Check write permissions in application directory |
| JSON parsing errors | Ensure CompactJsonFormatter is used |
| SDK can't read file | Run SDK with same user as .NET app |
| Correlation ID missing | Verify UseCorrelationId() is called early |
| Stack traces truncated | Serilog automatically includes full stack traces |
Performance Optimization
For production, configure rolling file sizes:
.WriteTo.File(
new CompactJsonFormatter(),
"app.log",
rollingInterval: RollingInterval.Day,
fileSizeLimitBytes: 10485760, // 10MB
retainedFileCountLimit: 10,
rollOnFileSizeLimit: true
)