This document describes the API endpoints and programming interfaces provided by AspNetDebugDashboard.
The debug dashboard exposes several REST API endpoints for programmatic access to debug data.
All API endpoints are available under the configured base path (default: /_debug/api).
The API endpoints are currently unauthenticated and should only be used in development environments.
Returns overall statistics about the debug data.
Response:
{
"totalRequests": 150,
"totalSqlQueries": 45,
"totalExceptions": 2,
"totalLogs": 89,
"averageResponseTime": 245.6,
"averageSqlTime": 12.3,
"statusCodeDistribution": {
"200": 120,
"404": 25,
"500": 5
},
"requestMethodDistribution": {
"GET": 100,
"POST": 30,
"PUT": 15,
"DELETE": 5
},
"exceptionTypeDistribution": {
"ArgumentException": 1,
"InvalidOperationException": 1
},
"slowestRequests": [
{
"id": "req-123",
"method": "GET",
"path": "/api/slow-endpoint",
"executionTimeMs": 2500,
"timestamp": "2025-01-07T10:30:00Z"
}
],
"slowestQueries": [
{
"id": "sql-456",
"query": "SELECT * FROM Products WHERE...",
"executionTimeMs": 150,
"timestamp": "2025-01-07T10:25:00Z"
}
],
"lastUpdated": "2025-01-07T10:35:00Z"
}Returns a paginated list of HTTP requests.
Query Parameters:
page(int): Page number (default: 1)pageSize(int): Items per page (default: 50)search(string): Match against path and URL (contains)method(string): Filter by HTTP methodpath(string): Filter by path (contains)statusCode(int): Filter by status codeisSuccessful(bool):falsereturns only 4xx/5xx,trueonly sub-400minExecutionTime(int): Only requests at or above this duration in msrequestId(string): Exact match on the trace identifierdateFrom/dateTo(datetime): Time rangesortBy(string):timestamp,executiontimems,statuscode,method,path(default:timestamp)sortDescending(bool): Sort direction (default: true)
Response:
{
"items": [
{
"id": "req-123",
"type": "Request",
"method": "GET",
"url": "https://localhost:5001/api/products",
"path": "/api/products",
"queryString": "?page=1&size=10",
"statusCode": 200,
"executionTimeMs": 125,
"headers": {
"User-Agent": "Mozilla/5.0...",
"Accept": "application/json"
},
"requestBody": null,
"responseBody": "[{\"id\": 1, \"name\": \"Product 1\"}]",
"contentType": "application/json",
"userAgent": "Mozilla/5.0...",
"ipAddress": "127.0.0.1",
"timestamp": "2025-01-07T10:30:00Z",
"sqlQueries": [],
"logs": [],
"exception": null
}
],
"totalCount": 150,
"page": 1,
"pageSize": 50,
"totalPages": 3,
"hasNextPage": true,
"hasPreviousPage": false
}Returns a specific request by ID.
Response:
{
"id": "req-123",
"type": "Request",
"method": "GET",
"url": "https://localhost:5001/api/products",
"path": "/api/products",
"queryString": "?page=1&size=10",
"statusCode": 200,
"executionTimeMs": 125,
"headers": {
"User-Agent": "Mozilla/5.0...",
"Accept": "application/json"
},
"requestBody": null,
"responseBody": "[{\"id\": 1, \"name\": \"Product 1\"}]",
"contentType": "application/json",
"userAgent": "Mozilla/5.0...",
"ipAddress": "127.0.0.1",
"timestamp": "2025-01-07T10:30:00Z",
"sqlQueries": [
{
"id": "sql-456",
"query": "SELECT * FROM Products",
"executionTimeMs": 25,
"parameters": {},
"isSuccessful": true
}
],
"logs": [
{
"id": "log-789",
"level": "Info",
"message": "Fetching products",
"tag": "ProductService"
}
],
"exception": null
}Returns a paginated list of SQL queries.
Query Parameters:
page(int): Page number (default: 1)pageSize(int): Items per page (default: 50)search(string): Search in query textisSlowQuery(bool): Only queries flagged slow (overSlowQueryThresholdMs)isSuccessful(bool): Filter by success/failurerequestId(string): Exact match on the trace identifierdateFrom/dateTo(datetime): Time rangesortBy(string):timestamporexecutiontimems(default:timestamp)sortDescending(bool): Sort direction (default: true)
Response:
{
"items": [
{
"id": "sql-456",
"type": "SqlQuery",
"query": "SELECT * FROM Products WHERE IsActive = @p0",
"parameters": {
"@p0": true
},
"executionTimeMs": 25,
"rowsAffected": 0,
"requestId": "req-123",
"database": "SampleDb",
"connectionString": "Server=localhost;Database=SampleDb;Trusted_Connection=true",
"isSuccessful": true,
"error": null,
"timestamp": "2025-01-07T10:30:00Z"
}
],
"totalCount": 45,
"page": 1,
"pageSize": 50,
"totalPages": 1,
"hasNextPage": false,
"hasPreviousPage": false
}Returns a specific SQL query by ID.
Returns a paginated list of log entries.
Query Parameters:
page(int): Page number (default: 1)pageSize(int): Items per page (default: 50)level(string): Filter by log leveltag(string): Filter by tagsearch(string): Search in log messagefromDate(datetime): Filter from datetoDate(datetime): Filter to datesortBy(string): Sort field (default: "timestamp")sortDescending(bool): Sort direction (default: true)
Response:
{
"items": [
{
"id": "log-789",
"type": "Log",
"level": "Info",
"message": "Fetching products from database",
"category": null,
"tag": "ProductService",
"requestId": "req-123",
"properties": {
"UserId": 123,
"Action": "GetProducts"
},
"stackTrace": null,
"timestamp": "2025-01-07T10:30:00Z"
}
],
"totalCount": 89,
"page": 1,
"pageSize": 50,
"totalPages": 2,
"hasNextPage": true,
"hasPreviousPage": false
}Returns a specific log entry by ID.
Creates a new log entry.
Request Body:
{
"message": "Custom log message",
"level": "Info",
"tag": "CustomTag",
"category": "CustomCategory",
"properties": {
"CustomProperty": "CustomValue"
}
}Response:
{
"id": "log-new-123"
}Returns a paginated list of exceptions.
Query Parameters:
page(int): Page number (default: 1)pageSize(int): Items per page (default: 50)search(string): Search in exception message or typefromDate(datetime): Filter from datetoDate(datetime): Filter to datesortBy(string): Sort field (default: "timestamp")sortDescending(bool): Sort direction (default: true)
Response:
{
"items": [
{
"id": "exc-123",
"type": "Exception",
"message": "Object reference not set to an instance of an object",
"stackTrace": "at MyApp.Controllers.ProductsController.GetById(Int32 id)...",
"source": "MyApp",
"requestId": "req-456",
"route": "/api/products/{id}",
"method": "GET",
"path": "/api/products/999",
"exceptionType": "NullReferenceException",
"innerException": null,
"data": {},
"timestamp": "2025-01-07T10:35:00Z"
}
],
"totalCount": 2,
"page": 1,
"pageSize": 50,
"totalPages": 1,
"hasNextPage": false,
"hasPreviousPage": false
}Returns a specific exception by ID.
Clears all debug data.
Response:
{
"message": "All debug data cleared"
}Triggers manual cleanup of old entries.
Response:
{
"message": "Cleanup completed"
}Returns current configuration.
Response:
{
"isEnabled": true,
"maxEntries": 1000,
"logRequestBodies": true,
"logResponseBodies": false,
"logSqlQueries": true,
"logExceptions": true,
"enableRealTimeUpdates": true
}Metrics computed over the last 60 minutes of requests.
Response:
{
"totalRequests": 412,
"averageResponseTime": 38.2,
"medianResponseTime": 11.0,
"p95ResponseTime": 240.0,
"p99ResponseTime": 1100.0,
"errorRate": 2.4,
"requestsPerMinute": 6.9,
"slowestEndpoints": [
{ "endpoint": "GET /api/reports", "averageTime": 480.5, "requestCount": 12 }
],
"statusCodeDistribution": [
{ "statusCode": 200, "count": 396, "percentage": 96.1 }
]
}Searches across entry types. types is optional (requests, queries, logs, exceptions); without it all types are searched. Returns up to 50 hits as { "type": "...", "data": { ... } } pairs.
Downloads everything (stats, requests, queries, logs, exceptions) as a single JSON file.
Storage health plus the active configuration. Useful for checking the dashboard is wired up.
public interface IDebugLogger
{
Task LogAsync(string message, string level = "Info", string? tag = null, Dictionary<string, object>? properties = null);
Task LogInfoAsync(string message, string? tag = null, Dictionary<string, object>? properties = null);
Task LogWarningAsync(string message, string? tag = null, Dictionary<string, object>? properties = null);
Task LogErrorAsync(string message, string? tag = null, Dictionary<string, object>? properties = null);
Task LogSuccessAsync(string message, string? tag = null, Dictionary<string, object>? properties = null);
}public static class DebugLogger
{
public static async Task LogAsync(string message, string? tag = null, Dictionary<string, object>? properties = null)
public static async Task LogAsync(string message, string level, string? tag = null, Dictionary<string, object>? properties = null)
public static async Task InfoAsync(string message, string? tag = null, Dictionary<string, object>? properties = null)
public static async Task WarningAsync(string message, string? tag = null, Dictionary<string, object>? properties = null)
public static async Task ErrorAsync(string message, string? tag = null, Dictionary<string, object>? properties = null)
public static async Task SuccessAsync(string message, string? tag = null, Dictionary<string, object>? properties = null)
}To swap LiteDB for your own storage, implement IDebugStorage and register it before AddDebugDashboard(). The full interface (from Core/Services/IDebugStorage.cs):
public interface IDebugStorage : IDisposable
{
Task<string> StoreRequestAsync(RequestEntry request);
Task<string> StoreSqlQueryAsync(SqlQueryEntry query);
Task<string> StoreLogAsync(LogEntry log);
Task<string> StoreExceptionAsync(ExceptionEntry exception);
Task<PagedResult<RequestEntry>> GetRequestsAsync(DebugFilter filter);
Task<PagedResult<SqlQueryEntry>> GetSqlQueriesAsync(DebugFilter filter);
Task<PagedResult<LogEntry>> GetLogsAsync(DebugFilter filter);
Task<PagedResult<ExceptionEntry>> GetExceptionsAsync(DebugFilter filter);
Task<RequestEntry?> GetRequestAsync(string id);
Task<SqlQueryEntry?> GetSqlQueryAsync(string id);
Task<LogEntry?> GetLogAsync(string id);
Task<ExceptionEntry?> GetExceptionAsync(string id);
Task<DebugStats> GetStatsAsync();
Task CleanupAsync(int maxEntries);
Task ClearAllAsync();
Task<object> GetHealthAsync();
Task<object> ExportAllAsync();
Task ImportAsync(object data);
Task<IEnumerable<object>> SearchAsync(string term, string[] types, int maxResults = 50);
Task<object> GetPerformanceMetricsAsync(TimeSpan? timeWindow = null);
Task<int> BulkDeleteAsync(string[] ids, string type);
Task<int> DeleteOlderThanAsync(DateTime cutoff);
Task OptimizeAsync();
Task<long> GetDatabaseSizeAsync();
Task<int> GetTotalEntriesAsync();
}All configuration options and their defaults are documented in CONFIGURATION.md.
// Using static logger
await DebugLogger.InfoAsync("User logged in", "Authentication",
new Dictionary<string, object> { { "UserId", 123 } });
// Using dependency injection
public class UserService
{
private readonly IDebugLogger _logger;
public UserService(IDebugLogger logger)
{
_logger = logger;
}
public async Task<User> GetUserAsync(int id)
{
await _logger.LogInfoAsync($"Fetching user {id}", "UserService");
// Implementation
}
}public class CustomDebugStorage : IDebugStorage
{
public async Task<string> StoreRequestAsync(RequestEntry request)
{
// Custom storage logic
return request.Id;
}
// Implement other methods...
}
// Register custom storage
builder.Services.AddSingleton<IDebugStorage, CustomDebugStorage>();public class DebugDashboardClient
{
private readonly HttpClient _httpClient;
public DebugDashboardClient(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<DebugStats> GetStatsAsync()
{
var response = await _httpClient.GetAsync("/_debug/api/stats");
var json = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<DebugStats>(json);
}
public async Task<PagedResult<RequestEntry>> GetRequestsAsync(DebugFilter filter)
{
var query = BuildQueryString(filter);
var response = await _httpClient.GetAsync($"/_debug/api/requests?{query}");
var json = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<PagedResult<RequestEntry>>(json);
}
}