AsGuard 1.1.0
dotnet add package AsGuard --version 1.1.0
NuGet\Install-Package AsGuard -Version 1.1.0
<PackageReference Include="AsGuard" Version="1.1.0" />
<PackageVersion Include="AsGuard" Version="1.1.0" />
<PackageReference Include="AsGuard" />
paket add AsGuard --version 1.1.0
#r "nuget: AsGuard, 1.1.0"
#:package AsGuard@1.1.0
#addin nuget:?package=AsGuard&version=1.1.0
#tool nuget:?package=AsGuard&version=1.1.0
AsGuard
AsGuard is an ASP.NET Core monitoring package for request logging, exception tracking, host ILogger capture, live Server-Sent Events (SSE) updates, and a built-in dashboard.
It is intended for applications that need practical visibility into HTTP traffic and application warnings/errors without wiring a separate observability platform first.
Features
- HTTP request and response logging.
- Request log enrichment (
UserId, custom searchableTags, and structuredMetadataJsonmetadata) via claims, DI-based enrichers, or inline options delegates. - Persistent storage with SQL Server, PostgreSQL, SQLite, or in-memory storage.
- Built-in Razor dashboard.
- Elegant dashboard login page (no browser Basic Auth popup) backed by Basic Auth/session-cookie credentials.
- Basic Authentication for the API and SSE stream.
- Live SSE updates for new request logs and exception/log events.
- Correlation ID propagation with a configurable header.
- Request logging scopes that enrich host logs with correlation and request data.
- Host
ILoggercapture for warnings and errors by default. - Unhandled HTTP exception capture.
- Manual exception logging through
IExceptionLogger. - Optional request body capture.
- Optional response body capture.
- Content-type allow list for body capture.
- Body and header truncation limits.
- Sensitive request/response header redaction.
- Attribute-based request and response JSON body masking.
- Path exclusions for health checks, metrics, Swagger, the dashboard, API, and stream.
- Queue-based background persistence.
- Configurable queue capacity, batch size, and overflow behavior.
- Configurable live broadcast queue pressure behavior.
- Summary-only or full-detail live broadcast payloads.
- Request and exception detail endpoints.
- Exception summary counts by severity.
- Exception trend buckets by hour or day.
- Endpoint performance leaderboard (p50/p95/p99, error rate, volume).
- Per-user leaderboard (volume, error rate, average duration, p95) for authenticated users.
- Slow trace analysis: top slowest DB query and HTTP client spans across all requests in a time range.
- Service health history: periodic health-check sampling with a dashboard trend chart.
- Alert history: fired alerts are retained in-memory and viewable from a dedicated dashboard tab.
- Host metrics and health diagnostics endpoints for dashboard observability.
- CSV and JSON export of the current log view.
- Correlation ID drill-down and "Copy as cURL" from the request details modal.
- Log deletion APIs.
- Retention cleanup worker.
- Queue pressure, exception spike, persistence failure, broadcast failure, and APM span persistence failure alerts.
- Bounded retry with backoff before a persistence failure is counted, to absorb transient database blips.
- Startup validation of connection string, sampling rate, and batch size configuration.
- Custom alert sink support.
- Runtime monitoring stats endpoint.
- Optional Redis management: key browser (
SCAN, neverKEYS), value inspector/editor for string/hash/list/set/zset, TTL editing, and live server metrics over the existing SSE stream — latency, memory pressure, throughput, hit rate, clients, plus persistence and replication health. - Redis connection auto-discovery from the host's service provider, so managed Redis with token auth (Azure Cache for Redis + Microsoft Entra ID) works without a connection string.
Requirements
- .NET 8, .NET 9, or .NET 10.
- ASP.NET Core application using
WebApplicationorIApplicationBuilder.
Installation
Install the package in the host application:
dotnet add package AsGuard
For local development from this repository, pack and install the generated package into your host app:
dotnet pack -c Release
dotnet add package AsGuard --source ./bin/Release
Quick Start
using AsGuard.Domain.RequestLogging;
using AsGuard.Extensions;
using AsGuard.Services;
using Microsoft.Extensions.Logging;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRequestLogging(options =>
{
options.DatabaseProvider = LoggingDatabaseProvider.Sqlite;
options.ConnectionString = "Data Source=AsGuard.db";
options.DashboardRoute = "/request-logs-ui";
options.DashboardUsername = builder.Configuration["AsGuard:DashboardUsername"]!;
options.DashboardPassword = builder.Configuration["AsGuard:DashboardPassword"]!;
options.EnableExceptionLogging = true;
options.CaptureHostLogs = true;
options.HostLogMinimumLevel = LogLevel.Warning;
});
var app = builder.Build();
// If you use ASP.NET Core exception handling, register it before UseRequestLogging().
app.UseExceptionHandler("/error");
app.UseRequestLogging();
app.MapGet("/demo-warning", (ILogger<Program> logger) =>
{
logger.LogWarning("This warning is captured by AsGuard.");
return Results.Ok();
});
app.MapGet("/demo-error", () =>
{
throw new InvalidOperationException("Unhandled sample error.");
});
app.Run();
Open the dashboard:
/request-logs-ui
Visiting the dashboard route while unauthenticated redirects to an elegant built-in login page (no browser Basic Auth popup). The API and SSE stream still use HTTP Basic Authentication with DashboardUsername and DashboardPassword.
Middleware Order
Call app.UseRequestLogging() after building the app and before the endpoints you want monitored.
var app = builder.Build();
app.UseExceptionHandler("/error");
app.UseRequestLogging();
app.MapControllers();
app.Run();
UseRequestLogging() adds:
- Correlation middleware.
- Logging scope middleware.
- AsGuard exception capture middleware.
- Basic Auth middleware for AsGuard surfaces.
- Request/response logging middleware.
- Dashboard, API, and SSE endpoint mapping.
Complete Configuration Example
builder.Services.AddRequestLogging(options =>
{
// Storage
options.DatabaseProvider = LoggingDatabaseProvider.SqlServer;
options.ConnectionString = builder.Configuration.GetConnectionString("AsGuard")!;
// Request queue and persistence
options.QueueCapacity = 10_000;
options.QueueOverflowPolicy = QueueOverflowPolicy.DropNewest;
options.BatchSize = 100;
// Exception and host ILogger capture
options.EnableExceptionLogging = true;
options.EnableApmTracing = true;
options.ExceptionQueueCapacity = 5_000;
options.ExceptionQueueOverflowPolicy = QueueOverflowPolicy.DropNewest;
options.ExceptionBatchSize = 50;
options.CaptureHostLogs = true;
options.HostLogMinimumLevel = LogLevel.Warning;
options.ExcludedHostLogCategoryPrefixes.Add("Microsoft.");
options.ExcludedHostLogCategoryPrefixes.Add("System.");
// In-memory store limit
options.MaxInMemoryEntries = 10_000;
// Live broadcast (SSE delivery)
options.BroadcastQueueCapacity = 2_048;
options.BroadcastOverflowPolicy = BroadcastOverflowPolicy.DropOldest;
options.BroadcastDetailMode = BroadcastDetailMode.SummaryOnly;
// Detail APIs
options.EnableDetailedLogEndpoint = true;
// Correlation
options.CorrelationHeaderName = "X-Correlation-ID";
// Retention
options.RequestRetentionDays = 30;
options.ExceptionRetentionDays = 90;
options.RetentionCleanupInterval = TimeSpan.FromHours(6);
// Alerts
options.QueuePressureAlertThreshold = 0.8;
options.ExceptionSpikeAlertThreshold = 25;
options.ExceptionSpikeAlertWindow = TimeSpan.FromMinutes(5);
options.AlertCooldown = TimeSpan.FromMinutes(5);
// Body capture
options.LogRequestBody = false;
options.LogResponseBody = false;
options.MaxCapturedBodyBytes = 4_096;
options.LoggableContentTypes =
[
"application/json",
"application/xml",
"text/",
"application/x-www-form-urlencoded"
];
// Redaction
options.SensitiveHeaders.Add("X-Internal-Token");
// Path exclusions
options.ExcludedPathPrefixes.Add("/internal");
options.ExcludedExactPaths.Add("/ready");
// Dashboard and protected AsGuard endpoints
options.DashboardRoute = "/request-logs-ui";
options.DashboardUsername = builder.Configuration["AsGuard:DashboardUsername"]!;
options.DashboardPassword = builder.Configuration["AsGuard:DashboardPassword"]!;
});
Storage Providers
Use DatabaseProvider to choose where logs are persisted.
| Provider | Value | Connection string |
|---|---|---|
| SQL Server | LoggingDatabaseProvider.SqlServer |
Required |
| PostgreSQL | LoggingDatabaseProvider.PostgreSql |
Required |
| SQLite | LoggingDatabaseProvider.Sqlite |
Required |
| In-memory | LoggingDatabaseProvider.InMemory |
Not used |
SQL Server
options.DatabaseProvider = LoggingDatabaseProvider.SqlServer;
options.ConnectionString = builder.Configuration.GetConnectionString("AsGuard")!;
PostgreSQL
options.DatabaseProvider = LoggingDatabaseProvider.PostgreSql;
options.ConnectionString = builder.Configuration.GetConnectionString("AsGuard")!;
SQLite
options.DatabaseProvider = LoggingDatabaseProvider.Sqlite;
options.ConnectionString = "Data Source=AsGuard.db";
In-Memory
options.DatabaseProvider = LoggingDatabaseProvider.InMemory;
options.MaxInMemoryEntries = 10_000;
In-memory storage is process-local and is cleared when the application stops.
Dashboard
The dashboard route is controlled by DashboardRoute.
options.DashboardRoute = "/request-logs-ui";
options.DashboardUsername = builder.Configuration["AsGuard:DashboardUsername"]!;
options.DashboardPassword = builder.Configuration["AsGuard:DashboardPassword"]!;
The dashboard supports:
- Request log browsing.
- Request details, including correlation ID drill-down and "Copy as cURL".
- Exception/log event browsing.
- Exception/log event details.
- Severity filtering.
- Method, status, and user ID filtering.
- Search.
- Correlation ID filtering.
- UTC date filtering.
- Summary cards.
- Live updates.
- Clearing logs.
- CSV and JSON export.
- Endpoint leaderboard (p50/p95/p99, error rate, volume).
- Per-user leaderboard.
- Slow traces (top slowest DB query and HTTP client spans).
- Alert history.
- Host metrics charts.
- Service health status and history.
- Live console log terminal.
- Redis key browser, value editor, and live server stats (opt-in).
- Light and dark modes.
Dashboard credentials are required when DashboardRoute is enabled. The legacy default credentials admin / admin are rejected at startup.
Dashboard Login
Unauthenticated browser requests to DashboardRoute are redirected to a built-in login page at /request-logs-login instead of triggering the browser's native Basic Auth dialog. The login form posts to /request-logs-login-api:
- Correct credentials return
200 OKand set anHttpOnlysession cookie, then the browser is redirected to the originally requested page. - Incorrect credentials return
401 Unauthorizedand the form shows an inline error.
The API (/request-logs-api/*) and SSE stream (/request-logs-stream) are unaffected and continue to require HTTP Basic Authentication headers for programmatic/curl access. Query-string credential authentication (?username=&password=) is no longer supported — it leaked credentials into proxy/access logs and was removed in favor of the session cookie.
Host ILogger Capture
AsGuard registers an ILoggerProvider. Host app logs at Warning and above are captured by default and stored with exception/log events.
app.MapGet("/checkout", (ILogger<Program> logger) =>
{
logger.LogWarning("Payment retry required for order {OrderId}", 123);
return Results.Ok();
});
Configure the minimum level:
options.CaptureHostLogs = true;
options.HostLogMinimumLevel = LogLevel.Information;
Exclude noisy categories:
options.ExcludedHostLogCategoryPrefixes.Add("Microsoft.");
options.ExcludedHostLogCategoryPrefixes.Add("System.");
options.ExcludedHostLogCategoryPrefixes.Add("MyApp.NoisyWorker");
AsGuard excludes AsGuard. categories by default to avoid capturing its own internal logs.
If your host app calls builder.Logging.ClearProviders(), call it before builder.Services.AddRequestLogging(...). Clearing providers after AddRequestLogging(...) can remove the AsGuard logger provider.
Unhandled Exception Capture
Unhandled HTTP exceptions are captured when EnableExceptionLogging is enabled.
options.EnableExceptionLogging = true;
app.MapGet("/throw", () =>
{
throw new InvalidOperationException("Captured by AsGuard.");
});
AsGuard rethrows the exception after capturing it, so the host app's normal exception handling still controls the HTTP response.
Manual Exception Logging
Use IExceptionLogger for background jobs, message handlers, scheduled tasks, or exceptions that are handled manually.
app.MapPost("/background-exception", async (IExceptionLogger exceptionLogger) =>
{
try
{
throw new ApplicationException("Manual exception sample.");
}
catch (Exception ex)
{
await exceptionLogger.LogAsync(
ex,
LogLevel.Error,
new ExceptionLogContext(
Path: "/background-exception",
Method: "POST",
UserId: "user-123",
Tags: "manual-api",
MetadataJson: """{"job":"billing-sync"}"""));
}
return Results.Ok();
});
ExceptionLogContext supports:
| Property | Purpose |
|---|---|
CorrelationId |
Existing correlation ID. If omitted, AsGuard uses the current request correlation ID or creates one. |
Path |
Request or operation path. |
Method |
HTTP method or operation verb. |
UserId |
Current user identifier. |
ClientIp |
Client IP address. |
RequestId |
Request identifier. |
Tags |
Searchable labels such as background-job or payment. |
MetadataJson |
Custom JSON metadata. |
OccurredOnUtc |
Override event timestamp. |
Request And Response Body Capture
Body capture is disabled by default.
options.LogRequestBody = true;
options.LogResponseBody = true;
options.MaxCapturedBodyBytes = 8_192;
Only configured content types are captured. If LoggableContentTypes is left empty (the default), all content types will be captured:
// Limit body capture to specific content types
options.LoggableContentTypes =
[
"application/json",
"text/",
"application/x-www-form-urlencoded"
];
Unsupported content types are stored as a skipped marker. Captured bodies are truncated when they exceed MaxCapturedBodyBytes.
Use body capture carefully in production. Request and response bodies can contain credentials, tokens, personal data, or regulated data.
Header Redaction
By default, the Set-Cookie response header is always redacted.
Other sensitive headers (such as Authorization, Cookie, or X-Api-Key) are not redacted by default. You should explicitly add them to the SensitiveHeaders collection:
options.SensitiveHeaders.Add("Authorization");
options.SensitiveHeaders.Add("Cookie");
options.SensitiveHeaders.Add("X-Api-Key");
options.SensitiveHeaders.Add("X-Internal-Token");
options.SensitiveHeaders.Add("X-Session-Id");
Set-Cookie response headers are always redacted.
Body Data Masking
You can automatically redact sensitive data (like passwords, tokens, or PII) from captured request and response JSON bodies.
Simply apply the [AsGuardMasked] attribute to the sensitive properties in your models:
using AsGuard.Domain.RequestLogging;
public class LoginRequest
{
public string Username { get; set; }
[AsGuardMasked]
public string Password { get; set; }
}
AsGuard scans your assemblies at startup to discover these attributes (respecting [JsonPropertyName]). When it captures a JSON body containing these keys, it automatically replaces their values with "[REDACTED]" before saving the log. The actual response returned to the client is completely unaffected.
You can also manually add keys to be masked via options:
options.SensitiveBodyKeys.Add("creditCardNumber");
Request Log Enrichment
AsGuard supports enriching request logs with additional context, enabling you to extract and store:
UserId: The identifier of the logged-in user making the request.Tags: Custom searchable tags (e.g."api,public,v2").MetadataJson: Structured JSON metadata (e.g.{"tenant":"emea", "clientId":"mobile-app"}).
These fields are persistent, fully searchable via the dashboard/API, and indexable in SQL databases.
1. Default Claims-Based UserId
By default, AsGuard automatically populates the UserId property by searching the request user's claims for common claim types:
ClaimTypes.NameIdentifiersub(Subject claim)
If found, these are mapped automatically without any extra configuration.
2. Inline Option-Based Enrichment
For quick and simple enrichment, use the EnrichLog delegate option inside AddRequestLogging:
builder.Services.AddRequestLogging(options =>
{
options.DatabaseProvider = LoggingDatabaseProvider.Sqlite;
options.ConnectionString = "Data Source=AsGuard.db";
options.EnrichLog = (context, log) =>
{
// Custom UserId extraction
if (context.Request.Headers.TryGetValue("X-Client-Id", out var clientId))
{
log.UserId = clientId;
}
// Add custom searchable tags
log.Tags = "custom-tag,v2";
// Add structured custom metadata using the Metadata dictionary
log.Metadata["region"] = "eu-west-1";
log.Metadata["environment"] = "production";
};
});
3. Dependency Injection-Based Enrichment
For more complex enrichment requiring external services (such as looking up database records, caching, or resolving configuration), define an enrichment service by implementing IAsGuardRequestEnricher:
using AsGuard.Domain.RequestLogging;
using AsGuard.Services;
using Microsoft.AspNetCore.Http;
public class TenantRequestEnricher : IAsGuardRequestEnricher
{
private readonly ITenantService _tenantService;
public TenantRequestEnricher(ITenantService tenantService)
{
_tenantService = tenantService;
}
public void Enrich(HttpContext context, RequestLog log)
{
var tenantInfo = _tenantService.GetCurrentTenant();
if (tenantInfo != null)
{
log.Tags = string.IsNullOrEmpty(log.Tags)
? tenantInfo.Code
: $"{log.Tags},{tenantInfo.Code}";
// Enrich with custom structured metadata using the Metadata dictionary
log.Metadata["tenantId"] = tenantInfo.Id;
log.Metadata["tier"] = tenantInfo.SubscriptionTier;
}
}
}
Then, register your enricher in the Dependency Injection container:
builder.Services.AddScoped<IAsGuardRequestEnricher, TenantRequestEnricher>();
Performance Guidance: Log enrichment runs inside the HTTP request middleware pipeline context to ensure all request details (route data, headers, claims) are fully available. Keep all custom enricher operations fast and non-blocking to prevent adding latency to API requests.
Correlation IDs
AsGuard reads and writes the configured correlation header. The default is X-Correlation-ID.
options.CorrelationHeaderName = "X-Correlation-ID";
If the incoming request has a valid correlation ID, AsGuard uses it. Otherwise it creates a new one. The value is assigned to HttpContext.TraceIdentifier and returned on the response header.
Accepted incoming correlation IDs contain only letters, digits, hyphens, and underscores, and must be 128 characters or shorter.
Path Exclusions
AsGuard skips common operational endpoints by default:
- Prefixes:
/health,/swagger,/metrics - Exact paths:
/health,/metrics,/swagger,/favicon.ico
The dashboard route, /request-logs-api, /request-logs-stream, /request-logs-login, and /request-logs-login-api are also skipped automatically.
Add exclusions:
options.ExcludedPathPrefixes.Add("/internal");
options.ExcludedPathPrefixes.Add("/jobs");
options.ExcludedExactPaths.Add("/ready");
Prefix exclusions are case-insensitive and use path-prefix matching. Exact exclusions are case-insensitive exact matches.
Advanced Sampling & Filtering
By default, AsGuard logs 100% of the non-excluded requests. For high-traffic applications, you can reduce storage and queue pressure by configuring sampling and filtering logic.
// 1. Request Sampling (e.g., log only 10% of traffic)
options.SamplingRate = 0.1;
// 2. Threshold-Based Logging (e.g., log requests taking longer than 500ms)
options.LogRequestsSlowerThan = TimeSpan.FromMilliseconds(500);
// 3. Status Code Specific Logging (e.g., log only 400 and 500 responses)
options.LoggableStatusCodes.Add(400);
options.LoggableStatusCodes.Add(500);
Note: Any request that throws an unhandled exception will always be logged, bypassing these sampling and filtering rules.
Queues And Persistence
AsGuard queues logs in memory and persists them from background workers.
options.QueueCapacity = 10_000;
options.BatchSize = 100;
options.QueueOverflowPolicy = QueueOverflowPolicy.DropNewest;
options.ExceptionQueueCapacity = 5_000;
options.ExceptionBatchSize = 50;
options.ExceptionQueueOverflowPolicy = QueueOverflowPolicy.DropNewest;
Overflow policies:
| Policy | Behavior |
|---|---|
DropNewest |
Drop the new item when the queue is full. |
DropOldest |
Remove the oldest queued item and enqueue the new item. |
Use larger queues for bursty systems. Use smaller queues when memory pressure is more important than retaining every monitoring event.
Live SSE Updates
The SSE stream endpoint is:
/request-logs-stream
The stream uses the same Basic Auth credentials as the dashboard.
Optional groups query parameter:
/request-logs-stream?groups=logs,exceptions
If omitted, both groups are streamed.
SSE payloads are JSON envelopes with:
| Field | Meaning |
|---|---|
method |
NewLogs or NewExceptions |
payload |
Array of newly persisted request logs or exception/log events |
Configure broadcast pressure behavior:
options.BroadcastQueueCapacity = 2_048;
options.BroadcastOverflowPolicy = BroadcastOverflowPolicy.DropOldest;
options.BroadcastDetailMode = BroadcastDetailMode.SummaryOnly;
Broadcast detail modes:
| Mode | Behavior |
|---|---|
SummaryOnly |
Sends compact live rows without large details. |
Full |
Includes metadata, stack traces, and inner exception data in live events. |
Retention Cleanup
Configure automatic deletion of old logs:
options.RequestRetentionDays = 30;
options.ExceptionRetentionDays = 90;
options.RetentionCleanupInterval = TimeSpan.FromHours(6);
Set a retention value to null to disable cleanup for that log type.
Alerts
AsGuard publishes alerts for:
- Request queue pressure.
- Exception queue pressure.
- Exception spikes.
- Request persistence failures.
- Exception persistence failures.
- Live broadcast failures.
Configure alert thresholds:
options.QueuePressureAlertThreshold = 0.8;
options.ExceptionSpikeAlertThreshold = 25;
options.ExceptionSpikeAlertWindow = TimeSpan.FromMinutes(5);
options.AlertCooldown = TimeSpan.FromMinutes(5);
Set QueuePressureAlertThreshold or ExceptionSpikeAlertThreshold to null to disable those alerts.
By default, alerts are written through ILogger.
Custom Alert Sink
Register your own IAsGuardAlertSink after AddRequestLogging to send alerts to email, Slack, Teams, webhooks, or another monitoring system.
builder.Services.AddRequestLogging(options =>
{
// options...
});
builder.Services.AddSingleton<IAsGuardAlertSink, WebhookAsGuardAlertSink>();
using AsGuard.Services;
public sealed class WebhookAsGuardAlertSink : IAsGuardAlertSink
{
public async ValueTask PublishAsync(AsGuardAlert alert, CancellationToken cancellationToken)
{
// Send alert.Type, alert.Message, alert.Properties, alert.OccurredOnUtc.
await Task.CompletedTask;
}
}
Redis Management
Opt-in. Adds a Redis tab to the existing dashboard with a key browser, a value inspector/editor, and live server statistics.
options.EnableRedisManagement = true;
In most applications that is the only setting needed. AsGuard discovers your Redis connection rather than asking for a connection string, checking in order:
IAsGuardRedisConnectionProviderregistered in DI.IConnectionMultiplexerin DI (whatAddSingleton<IConnectionMultiplexer>and Aspire'sAddRedisClientproduce).- Keyed
IConnectionMultiplexerregistrations (Aspire'sAddKeyedRedisClient). - The configuration behind
AddStackExchangeRedisCache. options.RedisConnections, for hosts with no Redis client registered at all.
Reusing the host's connection is not just convenient — with Azure Cache for Redis under Microsoft Entra ID there is no password, only a refreshing access token, and that cannot be rebuilt from a connection string. Connections borrowed from the host are never disposed or reconfigured by AsGuard.
Safety:
- Keys are enumerated with
SCAN, neverKEYS. Against a pre-2.8 server, where StackExchange.Redis would silently fall back to the blockingKEYS, AsGuard refuses to enumerate instead. RedisManagementReadOnly = truemakes the tab browse-only, enforced server-side.FLUSHDBneeds four things:AllowRedisFlushDatabase,allowAdmin=trueon the connection, a typed confirmation phrase sent in the request body, and an explicitly named connection.FLUSHALLis never issued, and there is no bulk pattern delete.- A value too large for
MaxRedisValueBytesloads partially and becomes read-only, so a truncated view can never be saved back over the full value. - Slow-log arguments are redacted to the command and its key by default, because
SLOWLOGrecords values verbatim.RedisSlowlogIncludeArguments = trueshows them in full.
Live metrics arrive on the dashboard's existing SSE stream, sampled only while somebody is actually watching the tab. Each tick costs one INFO plus one PING; the heavier INFO commandstats, INFO latencystats and SLOWLOG reads sit behind an on-demand diagnostics panel rather than running against production every few seconds. Counters that Redis reports as lifetime totals — evictions, expirations, new connections, hit rate — are differenced between samples and shown as rates.
- Routes live under
/request-logs-api/redis/..., inheriting dashboard auth and the CSRF header requirement — and staying out of AsGuard's own request logging, so inspected cache values are never persisted to your log store.
Live INFO stats (memory, clients, ops/sec, hit rate) stream over the dashboard's existing SSE connection. Sampling only runs while a dashboard is actually subscribed.
Enabling this module means AsGuard references StackExchange.Redis, floor-pinned at 2.8.58 so a host already on a newer 2.x is never force-upgraded.
See docs/redis-management.md for the full reference.
API Reference
All API routes are protected by the same Basic Auth credentials as the dashboard.
Get Request Logs
GET /request-logs-api
Query parameters:
| Name | Type | Default | Description |
|---|---|---|---|
PageIndex |
int |
1 |
1-based page number. |
PageSize |
int |
20 |
Page size from 1 to 100. |
Search |
string |
Search text. | |
Method |
string |
HTTP method filter. | |
StatusCode |
int |
HTTP status code filter. | |
FromUtc |
DateTime |
Start UTC timestamp filter. | |
ToUtc |
DateTime |
End UTC timestamp filter. | |
UserId |
string |
Filter request logs by user ID. | |
ExceptionsOnly |
bool |
false |
Return only requests with captured exceptions. |
IncludeDetails |
bool |
false |
Include headers, bodies, and stack traces. |
SkipTotalCount |
bool |
false |
Skip total-count calculation for faster paging. |
Example:
GET /request-logs-api?PageIndex=1&PageSize=20&Method=POST&StatusCode=500
Get Request Log Details
GET /request-logs-api/{id}
Returns full request log details. Disabled when EnableDetailedLogEndpoint is false.
Delete Request Logs
DELETE /request-logs-api
Query parameters:
| Name | Type | Description |
|---|---|---|
FromUtc |
DateTime |
Delete logs on or after this UTC timestamp. |
ToUtc |
DateTime |
Delete logs on or before this UTC timestamp. |
Search |
string |
Delete matching logs. |
Method |
string |
Delete by HTTP method. |
StatusCode |
int |
Delete by HTTP status code. |
ExceptionsOnly |
bool |
Delete only requests with exceptions. |
Calling this endpoint without filters clears the request queue and all persisted request logs.
Get Runtime Stats
GET /request-logs-api/stats
Returns:
- Request queue depth, enqueued count, and dropped count.
- Exception queue depth, enqueued count, and dropped count.
- Persisted request and exception/log event counts.
- Persistence failure counts (request, exception, and APM span).
- Broadcast failure count.
- Active SSE connection count (
activeLiveConnections).
Get Endpoint Leaderboard
GET /request-logs-api/leaderboard
Query parameters:
| Name | Type | Default | Description |
|---|---|---|---|
Range |
string |
1h |
15m, 1h, 6h, 24h, or 7d. |
PageIndex |
int |
1 |
1-based page number. |
PageSize |
int |
20 |
Page size from 1 to 100. |
SortBy |
string |
p95 |
p50, p95, p99, errorRate, volume, path, or method. |
SortDir |
string |
desc |
asc or desc. |
Example:
GET /request-logs-api/leaderboard?Range=24h&SortBy=errorRate&SortDir=desc&PageIndex=1&PageSize=20
Get User Leaderboard
GET /request-logs-api/users/leaderboard
Query parameters:
| Name | Type | Default | Description |
|---|---|---|---|
Range |
string |
1h |
15m, 1h, 6h, 24h, or 7d. |
PageIndex |
int |
1 |
1-based page number. |
PageSize |
int |
20 |
Page size from 1 to 100. |
SortBy |
string |
volume |
volume, errorRate, avgDuration, p95, or userId. |
SortDir |
string |
desc |
asc or desc. |
Groups request logs that have a UserId and returns per-user volume, error count/rate, average duration, and p95 for the selected range. Requests without a UserId are excluded.
Get Slow Traces
GET /request-logs-api/apm/slow-traces
Query parameters:
| Name | Type | Default | Description |
|---|---|---|---|
Range |
string |
1h |
15m, 1h, 6h, 24h, or 7d. |
Limit |
int |
20 |
Number of spans to return, from 1 to 100. |
Returns the slowest APM spans (DbQuery or HttpClient) recorded within the selected range, ordered by duration descending. This is a relative top-N ranking, not a fixed "slow" threshold — every span in the window is a candidate. Requires EnableApmTracing.
Get Alert History
GET /request-logs-api/alerts
Returns the in-memory history of fired alerts (queue pressure, exception spike, request/exception/APM span persistence failures, broadcast failures), most-recently-fired last. Capacity is controlled by MaxAlertHistoryStored. This is populated regardless of which IAsGuardAlertSink is registered — history recording happens in AsGuardAlertDispatcher alongside the sink call.
Get Health History
GET /request-logs-api/health/history
Returns sampled overall health-check status and duration history used by the dashboard's Service Health trend chart. Sampling interval and buffer size are controlled by HealthHistorySampleInterval and MaxHealthHistoryStored. Requires EnableHealthChecksVisualizer.
Get Host Metrics
GET /request-logs-api/host-metrics
Returns an array of sampled HostMetricRecord values used by the dashboard metrics charts.
Get Health Status
GET /request-logs-api/health
Executes registered ASP.NET Core health checks and returns overall status, total duration, detailed entries, and whether health checks are configured.
Get Host Environment
GET /request-logs-api/host-environment
Returns runtime host diagnostics such as machine name, OS/framework info, process architecture, startup time, uptime, and safe environment variables.
Get Exception And Log Events
GET /request-logs-api/exceptions
This endpoint returns unhandled exceptions, manually logged exceptions, and captured host ILogger events.
Query parameters:
| Name | Type | Default | Description |
|---|---|---|---|
PageIndex |
int |
1 |
1-based page number. |
PageSize |
int |
20 |
Page size from 1 to 100. |
FromUtc |
DateTime |
Start UTC timestamp. | |
ToUtc |
DateTime |
End UTC timestamp. | |
Level |
LogLevel |
Trace, Debug, Information, Warning, Error, or Critical. |
|
CorrelationId |
string |
Correlation ID filter. | |
Search |
string |
Search text. | |
IncludeDetails |
bool |
false |
Include stack trace, inner exception, and metadata. |
SkipTotalCount |
bool |
false |
Skip total-count calculation for faster paging. |
Example:
GET /request-logs-api/exceptions?Level=Warning&PageSize=50
Get Exception Or Log Event Details
GET /request-logs-api/exceptions/{id}
Returns full exception/log event details. Disabled when EnableDetailedLogEndpoint is false.
Get Exception Summary
GET /request-logs-api/exceptions/summary
Query parameters:
| Name | Type | Description |
|---|---|---|
FromUtc |
DateTime |
Start UTC timestamp. |
ToUtc |
DateTime |
End UTC timestamp. |
Returns total, critical, error, warning, information, debug, and trace counts.
Get Exception Trends
GET /request-logs-api/exceptions/trends
Query parameters:
| Name | Type | Default | Description |
|---|---|---|---|
FromUtc |
DateTime |
Start UTC timestamp. | |
ToUtc |
DateTime |
End UTC timestamp. | |
Interval |
string |
hour |
hour or day. |
Delete Exception And Log Events
DELETE /request-logs-api/exceptions
Query parameters:
| Name | Type | Description |
|---|---|---|
FromUtc |
DateTime |
Delete events on or after this UTC timestamp. |
ToUtc |
DateTime |
Delete events on or before this UTC timestamp. |
Level |
LogLevel |
Delete by severity. |
CorrelationId |
string |
Delete by correlation ID. |
Search |
string |
Delete matching events. |
Calling this endpoint without filters clears the exception queue and all persisted exception/log events.
Dashboard Login
POST /request-logs-login-api
X-AsGuard-Request: 1
Body (JSON):
{ "username": "your-username", "password": "secret" }
Returns 200 OK and sets the AsGuardDashboardAuth session cookie on success, or 401 Unauthorized on invalid credentials. The cookie is an expiring, Data Protection–encrypted session ticket (lifetime configured by DashboardSessionTimeout, sliding renewal) — changing the dashboard credentials invalidates all outstanding sessions. Failed attempts are rate limited per client IP (LoginRateLimitMaxAttempts within LoginRateLimitWindow); once exceeded the endpoint returns 429 Too Many Requests with a Retry-After header.
The X-AsGuard-Request header is required on the login/logout POSTs and on any cookie-authenticated state-changing API call (DELETE), as same-origin CSRF proof. Basic-Auth API clients are exempt.
Dashboard Logout
POST /request-logs-logout-api
X-AsGuard-Request: 1
Clears the session cookie and returns 204 No Content.
Both endpoints and the /request-logs-login login page are excluded from request logging.
Options Reference
| Option | Default | Description |
|---|---|---|
DatabaseProvider |
SqlServer |
Storage provider. |
ConnectionString |
"" |
Connection string for SQL Server, PostgreSQL, or SQLite. |
QueueCapacity |
10_000 |
Request log queue capacity. |
QueueOverflowPolicy |
DropNewest |
Request queue overflow behavior. |
BatchSize |
100 |
Request log persistence batch size. |
EnableExceptionLogging |
true |
Enables exception/log event persistence. |
ExceptionQueueCapacity |
5_000 |
Exception/log event queue capacity. |
ExceptionQueueOverflowPolicy |
DropNewest |
Exception queue overflow behavior. |
ExceptionBatchSize |
50 |
Exception/log event persistence batch size. |
MaxInMemoryEntries |
10_000 |
Maximum retained rows for in-memory stores. |
BroadcastQueueCapacity |
2_048 |
Live broadcast queue capacity. |
BroadcastOverflowPolicy |
DropOldest |
Broadcast queue overflow behavior. |
BroadcastDetailMode |
SummaryOnly |
Live payload detail level for SSE events. |
EnableDetailedLogEndpoint |
true |
Enables detail endpoints. |
CaptureHostLogs |
true |
Captures host ILogger events. |
HostLogMinimumLevel |
Warning |
Minimum host ILogger level captured. |
ExcludedHostLogCategoryPrefixes |
AsGuard. |
Host logger categories excluded from capture. |
CorrelationHeaderName |
X-Correlation-ID |
Incoming/outgoing correlation header. |
InstanceId |
machine name | Instance identity stamped into each entry's metadata (useful when several instances share one database). |
RequestRetentionDays |
7 |
Request log retention period. null keeps request logs forever. |
ExceptionRetentionDays |
30 |
Exception/log event retention period. null keeps them forever. |
ApmSpanRetentionDays |
7 |
APM span retention period. null keeps spans forever. |
DisableRetention |
false |
Explicit opt-out: disables all retention cleanup. |
RetentionCleanupInterval |
6 hours |
Cleanup worker interval. |
RetentionDeleteBatchSize |
5_000 |
Rows removed per DELETE batch during retention cleanup. |
QueuePressureAlertThreshold |
0.8 |
Queue depth ratio for pressure alerts. |
ExceptionSpikeAlertThreshold |
null |
Event count that triggers spike alerts. |
ExceptionSpikeAlertWindow |
5 minutes |
Spike alert rolling window. |
AlertCooldown |
5 minutes |
Minimum time between repeated alerts of the same type. |
AlertWebhookUrl |
null |
Optional webhook (Slack/Teams/generic JSON) that fired alerts are POSTed to. |
AlertWebhookFormat |
Generic |
Webhook payload shape: Generic, Slack, or Teams. |
AlertWebhookTimeout |
10 seconds |
Timeout for alert webhook deliveries. |
MaxAlertHistoryStored |
200 |
Maximum fired alerts retained in the circular alert history buffer. |
MaxCapturedBodyBytes |
65_536 |
Maximum captured body/header characters before truncation. 0 disables truncation (not recommended). |
LogRequestBody |
false |
Captures request bodies. |
LogResponseBody |
false |
Captures response bodies. |
ExcludedPathPrefixes |
/health, /swagger, /metrics |
Case-insensitive path prefixes skipped by request logging. |
ExcludedExactPaths |
/health, /metrics, /swagger, /favicon.ico |
Case-insensitive exact paths skipped by request logging. |
LoggableContentTypes |
empty | Content types eligible for body capture. If empty (default), all content types are captured. |
SensitiveHeaders |
empty | Headers redacted from request/response logs. The Set-Cookie header is always redacted. |
SensitiveBodyKeys |
empty | JSON keys whose values should be redacted in request/response bodies. |
SamplingRate |
null |
Sample rate for request logging (0.0 to 1.0). Null means 100% logged. |
LogRequestsSlowerThan |
null |
Only requests slower than this threshold are logged. |
LoggableStatusCodes |
empty | If populated, only these HTTP status codes will be logged. |
DashboardRoute |
/request-logs-ui |
Dashboard route. Empty disables dashboard route mapping. |
DashboardUsername |
"" |
Basic Auth username. |
DashboardPassword |
"" |
Basic Auth password. |
DashboardSessionTimeout |
8 hours |
Dashboard session lifetime (sliding). Credential changes invalidate outstanding sessions. |
DashboardCookieSecurePolicy |
Always |
Secure attribute policy for the session cookie. Keep Always behind TLS-terminating proxies (with UseForwardedHeaders); use SameAsRequest only for plain-HTTP development. |
LoginRateLimitMaxAttempts |
5 |
Failed credential attempts per client IP before login is rate limited. |
LoginRateLimitWindow |
5 minutes |
Fixed window used for login rate limiting. |
MaskingScanAssemblies |
empty | Additional assemblies scanned for [AsGuardMasked] at startup (the entry assembly is always scanned). |
EnableApmTracing |
true |
Enables automatic APM tracing for database queries and HttpClient requests. |
EnableW3CPropagation |
true |
Enables W3C traceparent header propagation across HTTP boundaries. |
EnableHostMetrics |
true |
Enables process CPU, memory, and ThreadPool resource sampling. |
HostMetricsSampleInterval |
15 seconds |
Sampling interval for host resource metrics. |
MaxHostMetricsStored |
120 |
Maximum samples retained in the circular host metrics buffer. |
EnableLiveLogConsole |
true |
Enables real-time ILogger console streaming to dashboard clients. |
LiveLogConsoleMinLevel |
Information |
Minimum log level forwarded to the live console terminal. |
EnableHealthChecksVisualizer |
true |
Enables Service Health status cards and history sampling. |
HealthHistorySampleInterval |
30 seconds |
Sampling interval for the health check history sampler. |
MaxHealthHistoryStored |
120 |
Maximum samples retained in the circular health check history buffer. |
Production Notes
- Store dashboard credentials in configuration, user secrets, environment variables, or your production secret store.
- Do not use
admin/admin; AsGuard rejects those credentials. - Keep request and response body capture disabled unless you have reviewed privacy and security requirements.
- Add custom sensitive headers before enabling body/header capture in production.
- Use persistent storage for production. In-memory storage is for development, tests, and short-lived diagnostics.
- Size queue capacities based on traffic bursts and available memory.
- Exclude high-volume endpoints such as health checks, metrics, and static assets.
- If the host app clears logging providers, do it before
AddRequestLogging. - Startup now validates
ConnectionString,SamplingRate,BatchSize, andExceptionBatchSize; misconfiguration fails fast with a clearInvalidOperationExceptioninstead of failing silently at runtime. - Persistence workers retry a failed batch with backoff (250ms/750ms/1500ms) before counting it as a persistence failure, to absorb transient database blips without losing logs.
Troubleshooting
ILogger.LogWarning does not show in the dashboard
Check:
CaptureHostLogsistrue.HostLogMinimumLevelisWarningor lower.- The logger category is not excluded by
ExcludedHostLogCategoryPrefixes. - The host app did not call
builder.Logging.ClearProviders()afterAddRequestLogging. EnableExceptionLoggingistrue, because host logs use the exception/log event pipeline.
Dashboard shows a login page instead of loading / API returns 401
Check:
DashboardUsernameandDashboardPasswordare configured.- Browser navigation to
DashboardRouteredirects to/request-logs-loginwhen unauthenticated — sign in there. This is expected and replaces the old browser Basic Auth popup. - For API/curl clients, the request sends HTTP Basic Authentication (
Authorization: Basic <base64_credentials>) — query-string credentials are no longer supported. - The credentials are not
admin/admin.
Request or response bodies are missing
Check:
LogRequestBodyorLogResponseBodyis enabled.- The request/response content type starts with a value in
LoggableContentTypes. - The body was not empty.
- The captured text was not truncated by
MaxCapturedBodyBytes.
Logs are being dropped
Check:
/request-logs-api/statsfor queue depth and dropped counts.- Increase
QueueCapacityorExceptionQueueCapacity. - Increase persistence throughput by tuning
BatchSizeorExceptionBatchSize. - Review storage latency and connection string health.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.Data.SqlClient (>= 7.0.2)
- Microsoft.Data.Sqlite (>= 8.0.28)
- Microsoft.IO.RecyclableMemoryStream (>= 3.0.1)
- Npgsql (>= 8.0.9)
- SQLitePCLRaw.lib.e_sqlite3 (>= 3.53.3)
- StackExchange.Redis (>= 2.8.58)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 1.1.0 | 317 | 9/12/2026 | |
| 1.0.0 | 506 | 8/2/2026 | |
| 0.2.1 | 690 | 7/13/2026 | |
| 0.2.0 | 458 | 7/7/2026 | |
| 0.1.13 | 170 | 6/25/2026 | |
| 0.1.12 | 1,575 | 6/1/2026 | |
| 0.1.11 | 134 | 5/31/2026 | |
| 0.1.10 | 121 | 5/30/2026 | |
| 0.1.9 | 134 | 5/29/2026 | |
| 0.1.8 | 144 | 5/29/2026 | |
| 0.1.7 | 148 | 5/24/2026 | |
| 0.1.6 | 137 | 5/22/2026 | |
| 0.1.5 | 125 | 5/15/2026 | |
| 0.1.4 | 137 | 5/9/2026 | |
| 0.1.3 | 168 | 5/7/2026 | |
| 0.1.2 | 170 | 5/1/2026 | |
| 0.1.1 | 174 | 5/1/2026 | |
| 0.1.0 | 186 | 4/28/2026 |