MH.UserActivityLogger
8.0.1
dotnet add package MH.UserActivityLogger --version 8.0.1
NuGet\Install-Package MH.UserActivityLogger -Version 8.0.1
<PackageReference Include="MH.UserActivityLogger" Version="8.0.1" />
<PackageVersion Include="MH.UserActivityLogger" Version="8.0.1" />
<PackageReference Include="MH.UserActivityLogger" />
paket add MH.UserActivityLogger --version 8.0.1
#r "nuget: MH.UserActivityLogger, 8.0.1"
#:package MH.UserActivityLogger@8.0.1
#addin nuget:?package=MH.UserActivityLogger&version=8.0.1
#tool nuget:?package=MH.UserActivityLogger&version=8.0.1
MH.UserActivityLogger
Plug-and-play HTTP user activity logging for ASP.NET Core.
Capture request/response details automatically, buffer them locally for zero-latency impact, and securely flush them to PostgreSQL with blockchain-like tamper detection.
✨ Key Features
- 🚀 Zero Latency Impact: Requests are buffered to a local SQLite file immediately; database writes happen in the background.
- 🛡️ Tamper-Proof: Uses SHA-256 hash chaining (blockchain style) to detect if logs in the database have been altered or deleted.
- 📦 Bulk Processing: Efficiently bulk-inserts logs to PostgreSQL using
COPYfor high throughput. - 🔌 Plug-and-Play: Simple setup with standard ASP.NET Core Dependency Injection and Middleware.
- 🔍 Granular Control: Log specific endpoints using the
[UserActivityLog]attribute. - 👤 Extensible Identity: Built-in support for standard claims (sub, name, role) and extensible for custom user models.
🚀 Getting Started
1. Install the Package
via .NET CLI:
dotnet add package MH.UserActivityLogger
via Package Manager:
Install-Package MH.UserActivityLogger
2. Register Services
In your Program.cs:
using MH.UserActivityLogger.Extensions;
var builder = WebApplication.CreateBuilder(args);
// Add the logger services
builder.Services.AddUserActivityLogging(options =>
{
options.PostgresConnectionString = builder.Configuration.GetConnectionString("DefaultConnection");
// options.SchemaName = "public"; // Default
// options.TableName = "user_activity_logs"; // Default
});
4. Enable Middleware
Add the middleware after authentication/authorization but before your controllers.
var app = builder.Build();
// ... UseAuthentication();
// ... UseAuthorization();
await app.UseUserActivityLoggingAsync(); // Initialize the logger
app.MapControllers();
app.Run();
🛠 Usage
Basic Logging
Decorate any controller action you want to track using the [UserActivityLog] attribute. This attribute allows you to categorize and describe the activity for reporting purposes.
using MH.UserActivityLogger.Filters;
using MH.UserActivityLogger.Models;
[HttpPost("create")]
[UserActivityLog(action: "InvoiceCreation", actionType: "Write", criticality: CriticalityLevel.High, description: "User requested to create a new invoice.")]
public IActionResult CreateInvoice([FromBody] InvoiceDto dto)
{
return Ok();
}
| Parameter | Type | Description |
|---|---|---|
action |
string |
Name of the activity. Grouping key for reports (e.g., "Login", "ExportReport"). |
actionType |
string |
Category of the operation. (e.g., "Read", "Write", "Delete", "System"). |
criticality |
CriticalityLevel |
Importance level (enum). Allowed values: High, Medium, Low. Defaults to Low. |
description |
string |
Human-readable detail. A static description of what this endpoint does. |
Advanced: Custom User Details
By default, the logger captures standard user info (User ID, Name, etc.). If you want to log custom fields (like TenantId, Department, or SubscriptionLevel) into the user_details JSON column, follow these steps:
Step 1: Define Your Custom Model
Create a class that represents the extra data you want to log.
public class UserDetailsModel
{
public string TenantId { get; set; }
public string Department { get; set; }
public string SubscriptionLevel { get; set; }
}
Step 2: Implement the Data Provider
Create a class that implements IUserDetailsProvider<UserDetailsModel>. This logic extracts the data from the current user (ClaimsPrincipal).
using System.Security.Claims;
using MH.UserActivityLogger.Abstractions;
public class UserDetailsModelProvider : IUserDetailsProvider<UserDetailsModel>
{
// This method is called for every request to populate your custom model
public UserDetailsModel ExtractUserDetails(ClaimsPrincipal user)
{
return new UserDetailsModel
{
// Extract values from claims (or any other source)
TenantId = user.FindFirst("tenant_id")?.Value ?? "Default-Tenant",
Department = user.FindFirst("department")?.Value ?? "General",
SubscriptionLevel = user.FindFirst("subscription")?.Value ?? "Free"
};
}
// Required: How to get the unique User ID
public string? GetUserId(ClaimsPrincipal user) => user.FindFirst(ClaimTypes.NameIdentifier)?.Value;
// Optional: customized Session ID logic (default is null)
public string? GetSessionId(ClaimsPrincipal user) => user.FindFirst("session_id")?.Value;
}
Step 3: Register in Program.cs
Use the generic AddUserActivityLogging method to register your custom provider.
// 1. Add your custom context and provider as generic arguments
builder.Services.AddUserActivityLogging<UserDetailsModel, UserDetailsModelProvider>(options =>
{
options.PostgresConnectionString = builder.Configuration.GetConnectionString("DefaultConnection");
});
Now, every log entry in the database will have your UserDetailsModel serialized inside the user_details JSON column!
⚙️ Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
PostgresConnectionString |
string |
Required | Connection string for the target PostgreSQL database. |
SqlitePath |
string |
"activity_logs.db" |
Path to the local SQLite buffer file. High-throughput local storage. |
GroupSize |
int |
2000 |
Number of logs to accumulate in buffer before pushing to PostgreSQL. |
FallbackFlushMinutes |
int |
5 |
Maximum time to wait before forcing a flush of the buffer, even if GroupSize isn't met. |
FlushBatchSize |
int |
20 |
Number of records to insert per SQL command during bulk flush (optimizes network roundtrips). |
TableName |
string |
"user_activity_logs" |
Name of the table in PostgreSQL. |
SchemaName |
string |
"public" |
Schema name in PostgreSQL. |
EnableHashChaining |
bool |
true |
Enables SHA-256 blockchain-style linking of log entries for tamper detection. |
MaxRequestBodySize |
int |
4096 |
Max characters to capture from the request body. Larger bodies are truncated. |
MaxResponseSize |
int |
4096 |
Max characters to capture from the response body. Larger responses are truncated. |
EnableCompression |
bool |
false |
Compresses request_data and response JSON using GZip (Base64 encoded) before storing. |
CompressionThreshold |
int |
1024 |
Minimum string length to trigger compression (if enabled). |
🏗 Architecture
- Incoming Request: Filter captures data.
- Local Buffer: Data is written to SQLite (fast, reliable).
- Background Flush: Service wakes up when buffer is full or time elapses.
- Secure Push: Logs are hashed (chained to previous log) and bulk-inserted into PostgreSQL.
| 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
- EFCore.BulkExtensions (>= 8.0.0)
- Microsoft.AspNetCore.Mvc.Abstractions (>= 2.2.0)
- Microsoft.EntityFrameworkCore.Sqlite (>= 8.0.0)
- Microsoft.Extensions.Configuration.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 8.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.0)
- Npgsql.EntityFrameworkCore.PostgreSQL (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.