-
Notifications
You must be signed in to change notification settings - Fork 16
MessageHistory
Message history tracks the lifecycle of every message after it leaves the queue: when it was enqueued, when processing started, whether it succeeded or failed, the exception if any, and how long it took.
Once a message is committed or deleted, it's gone from the queue tables. History gives you an audit trail so you can answer "what happened to message X last Tuesday?"
History has two parts: the history table (created once) and runtime tracking (enabled per consumer).
Step 1: Create the queue with history enabled
Set EnableHistory = true on the transport options when creating the queue:
using (var creator = new QueueCreationContainer<SqLiteMessageQueueInit>())
{
using (var creation = creator.GetQueueCreation<SqLiteMessageQueueCreation>(queueConnection))
{
creation.Options.EnableStatus = true;
creation.Options.EnableStatusTable = true;
creation.Options.EnableHistory = true; // creates the history table
var result = creation.CreateQueue();
}
}If the queue already exists without a history table, you'll need to recreate it or manually create the history table.
Step 2: Enable history on the consumer
Set Enabled = true on the consumer configuration before starting:
using (var consumer = queueContainer.CreateConsumer(queueConnection))
{
consumer.Configuration.History.Enabled = true;
consumer.Configuration.History.RetentionDays = 30;
consumer.Configuration.History.StoreBody = false;
consumer.Configuration.History.MaxExceptionLength = 4000;
consumer.Start<MyMessage>((message, notification) =>
{
// process message
}, notifications);
}The consumer tracks: processing start, complete, error, rollback, delete, and expire events.
Note: The producer-side enqueue tracking also requires IHistoryConfiguration.Enabled = true in the producer's container. If you only enable it on the consumer, enqueue events won't be recorded (but processing/complete/error events will be).
Each message gets a single history record that's updated as it moves through lifecycle states:
| Status | When recorded |
|---|---|
| Enqueued | Message sent by producer |
| Processing | Consumer dequeues the message |
| Complete | Handler finishes and message is committed |
| Error | Handler throws and message moves to error status |
| Deleted | Message is deleted |
| Expired | Message expires before being processed |
Rollbacks increment a retry counter and reset the status back to Enqueued.
| Field | Description |
|---|---|
| QueueId | The message's queue identifier |
| CorrelationId | Cross-system correlation ID from headers |
| Status | Current lifecycle status |
| EnqueuedUtc | When the message was added to the queue |
| StartedUtc | When processing began |
| CompletedUtc | When processing finished (success, error, or delete) |
| DurationMs | Processing time in milliseconds |
| ExceptionText | Truncated exception for error status |
| RetryCount | Number of retries before final status |
| Route | Message route, if routing is enabled |
| MessageType | Body type name from header |
| Body | Serialized body bytes (only when StoreBody = true) |
| Headers | Serialized header bytes (only when StoreBody = true) |
By default, history does not store the message body. It's an audit trail, not a backup. Set StoreBody = true to store the serialized body and headers with each history record. This lets the dashboard display the body for completed messages, but increases storage on high-throughput queues.
Error messages already persist bodies in the error table, so the most common debugging scenario is covered without body storage.
A ClearHistoryMonitor runs periodically (default: once per day) and deletes records older than RetentionDays (default: 30). It works the same way as the error message cleanup monitor, running as part of the maintenance monitors with both MaintenanceMode.Consumer and MaintenanceMode.External.
| Setting | Default | Description |
|---|---|---|
Enabled |
false |
Enable history tracking |
RetentionDays |
30 |
Days to keep history before purging |
MaxExceptionLength |
4000 |
Truncate exception text to this length |
StoreBody |
false |
Store serialized message body and headers |
MonitorTime |
1 day |
How often the purge monitor runs |
TrackEnqueue |
true |
Record when messages are enqueued |
TrackProcessing |
true |
Record when processing starts |
TrackComplete |
true |
Record when processing succeeds |
TrackError |
true |
Record when processing fails |
TrackDelete |
true |
Record when messages are deleted |
TrackExpire |
true |
Record when messages expire |
The Dashboard API exposes history data via these endpoints:
-
GET /api/v1/dashboard/queues/{queueId}/history— paged list with optional status filter -
GET /api/v1/dashboard/queues/{queueId}/history/{messageId}— single message history -
GET /api/v1/dashboard/queues/{queueId}/history/count— count with optional filter -
DELETE /api/v1/dashboard/queues/{queueId}/history— purge by age
The Dashboard UI has a History tab on the queue detail page with status filtering, color-coded status chips, duration formatting, expandable exception text, and a purge button.
History is implemented for all 6 transports: SQL Server, PostgreSQL, SQLite, LiteDB, Redis, and Memory.
For any issues please use the GitHub issues