The IApiLoggingQueryService interface provides powerful methods for querying logged sessions and actions. Introduced in v1.2.0, this follows the CQRS pattern with separate read and write interfaces for better separation of concerns.
New in v1.2.0: Data retrieval API with advanced filtering, pagination, and multi-database support. For writing operations, see IApiLoggingService.
Injecting the Service
Add IApiLoggingQueryService to your controller or service:
public class AnalyticsController : ControllerBase
{
private readonly IApiLoggingQueryService _queryService;
public AnalyticsController(IApiLoggingQueryService queryService)
{
_queryService = queryService;
}
}Querying Sessions
Get paginated sessions with filtering and sorting:
[HttpGet("sessions")]
public async Task<IActionResult> GetSessions(
[FromQuery] int page = 0,
[FromQuery] int pageSize = 25,
[FromQuery] string? userId = null,
[FromQuery] bool? isActive = null)
{
var parameters = new QueryParameters
{
PageIndex = page,
PageSize = pageSize,
Filters = new FilterState
{
UserId = userId,
IsActive = isActive,
DateRange = new DateRange
{
From = DateTime.UtcNow.AddDays(-7), // Last 7 days
To = DateTime.UtcNow
}
},
Sorting = new List<SortingParameter>
{
new SortingParameter { Id = "LoginTime", Desc = true }
}
};
var result = await _queryService.GetSessionsAsync(parameters);
return Ok(result);
}Querying Actions
Get actions with advanced filtering:
[HttpGet("actions")]
public async Task<IActionResult> GetActions(
[FromQuery] string? sessionId = null,
[FromQuery] string? searchText = null,
[FromQuery] List<int>? statusCodes = null)
{
var parameters = new QueryParameters
{
PageIndex = 0,
PageSize = 50,
Filters = new FilterState
{
SessionId = sessionId,
StatusCodes = statusCodes, // e.g., [200, 404, 500]
SearchText = searchText, // Searches controller, action, route, method, etc.
Methods = new List<string> { "POST", "PUT", "DELETE" }
},
Sorting = new List<SortingParameter>
{
new SortingParameter { Id = "TimeStamp", Desc = true }
}
};
var result = await _queryService.GetActionsAsync(parameters);
return Ok(result);
}Querying Users (v1.2.1+)
Get aggregated user analytics across all their sessions:
[HttpGet("users")]
public async Task<IActionResult> GetUsers(
[FromQuery] int page = 0,
[FromQuery] string? searchText = null)
{
var parameters = new QueryParameters
{
PageIndex = page,
PageSize = 25,
Filters = new FilterState
{
SearchText = searchText
},
Sorting = new List<SortingParameter>
{
new SortingParameter { Id = "LastActivity", Desc = true }
}
};
var result = await _queryService.GetUsersAsync(parameters);
return Ok(result);
}Each UserSummary contains:
| Property | Type | Description |
|---|---|---|
UserId | string | The user's identifier |
TenantId | string | The user's tenant |
TotalSessions | int | Total session count |
ActiveSessions | int | Currently active sessions |
TotalActions | int | Total API calls across all sessions |
LastActivity | DateTime | Most recent activity timestamp |
FirstSeen | DateTime | First recorded session timestamp |
Get Single Item by ID
// Get specific session
var session = await _queryService.GetSessionByIdAsync("session-id-123");
// Get specific action
var action = await _queryService.GetActionByIdAsync("action-id-456");Available Filters
For Sessions
| Filter | Type | Description |
|---|---|---|
UserId | string? | Exact match on user ID |
TenantId | string? | Exact match on tenant ID |
IsActive | bool? | Filter by session active state (null = all) |
SearchText | string? | Multi-field search (UserId, TenantId, Id, EnvironmentName) |
DateRange | DateRange? | Filter by LoginTime |
For Actions
| Filter | Type | Description |
|---|---|---|
UserId | string? | Exact match on user ID |
TenantId | string? | Exact match on tenant ID |
SessionId | string? | Exact match on session ID |
StatusCodes | List<int>? | HTTP status codes (e.g., [200, 404, 500]) |
Methods | List<string>? | HTTP methods (e.g., ["GET", "POST"]) |
SearchText | string? | Multi-field search (UserId, SessionId, Controller, Action, Route, Method, CorrelationId) |
DateRange | DateRange? | Filter by TimeStamp |
Multi-Database Queries
Query from different databases using optional parameters:
// Query from default database var defaultSessions = await _queryService.GetSessionsAsync(parameters); // Query sessions from a tenant-specific database var premiumSessions = await _queryService.GetSessionsAsync( parameters, databaseName: "logs-tenant-premium", collectionName: "premium-sessions"); // Query a specific action from audit database var auditAction = await _queryService.GetActionByIdAsync( "action-id", databaseName: "audit-logs", collectionName: "critical-actions"); // Query users from a tenant-specific database var tenantUsers = await _queryService.GetUsersAsync( parameters, databaseName: "logs-tenant-premium");
Use cases for multi-database queries:
- Multi-tenant applications with separate databases per tenant
- Different retention policies (audit logs vs. debug logs)
- Read replicas for analytics queries
- Compliance requirements (PCI data in separate database)
TanStack Table Integration
The query models are designed to work seamlessly with TanStack Table and the @millerbyte/react-logging package:
// API endpoint that supports TanStack Table state
[HttpPost("actions/query")]
public async Task<IActionResult> QueryActions([FromBody] QueryParameters parameters)
{
// Parameters automatically map from TanStack Table's state
var result = await _queryService.GetActionsAsync(parameters);
return Ok(result);
}Performance Tips
- Pagination: Always use pagination for large datasets. Default page size is 25, max is 1000 to prevent excessive memory usage.
- Indexes: Ensure MongoDB indexes exist on frequently filtered fields (UserId, TenantId, SessionId, TimeStamp, StatusCode).
- Search Text: SearchText performs regex searches which can be slower. Limit to 200 characters and use specific filters when possible.
- Date Ranges: Always specify date ranges for time-based queries to leverage index scans.
- Parallel Queries: Count and data queries run in parallel automatically for better performance (~40-50% faster).
Recommended MongoDB Indexes
// Sessions collection
db.LoginSessions.createIndex({ "UserId": 1, "LoginTime": -1 });
db.LoginSessions.createIndex({ "TenantId": 1, "IsActive": 1 });
db.LoginSessions.createIndex({ "IsActive": 1, "LastActivityTime": -1 });
// Actions collection
db.ApiActions.createIndex({ "UserId": 1, "TimeStamp": -1 });
db.ApiActions.createIndex({ "SessionId": 1, "TimeStamp": -1 });
db.ApiActions.createIndex({ "ResponseInfo.StatusCode": 1 });
db.ApiActions.createIndex({ "TenantId": 1, "TimeStamp": -1 });Next Steps
- Learn about multi-database routing with attributes
- Explore the React package for building analytics dashboards
- Set up multi-database support for multi-tenant scenarios
