pinqloq 4.0.3
dotnet add package pinqloq --version 4.0.3
NuGet\Install-Package pinqloq -Version 4.0.3
<PackageReference Include="pinqloq" Version="4.0.3" />
<PackageVersion Include="pinqloq" Version="4.0.3" />
<PackageReference Include="pinqloq" />
paket add pinqloq --version 4.0.3
#r "nuget: pinqloq, 4.0.3"
#:package pinqloq@4.0.3
#addin nuget:?package=pinqloq&version=4.0.3
#tool nuget:?package=pinqloq&version=4.0.3
Pinqloq
Pinqloq is an ASP.NET Core structured logging and log shipping SDK for centralized application logs. It captures HTTP request/response logs through middleware and sends manual application events to the Pinqloq log management platform using in-memory buffering, batching, and HTTPS delivery.
Features
- Automatic ASP.NET Core request logging
- Attribute-based redaction of sensitive fields and endpoints
- Manual structured application events
- Buffered and batched HTTPS delivery
- No customer-managed message broker or token refresh flow
- Graceful shutdown flush
Requirements
- .NET 8.0 or later
- A Pinqloq project and secret key
Installation
dotnet add package pinqloq
Quick Start
Store your secret key in configuration, an environment variable, or a secret manager. Do not hardcode production credentials.
{
"Pinqloq": {
"SecretKey": "your-secret-key",
"ApiLogsCollectionName": "myapp_api_logs",
"DeviceIdentifier": "myapp-instance-1"
}
}
Register Pinqloq and enable automatic request logging:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddPinqloq(options =>
{
options.SecretKey =
builder.Configuration["Pinqloq:SecretKey"]
?? throw new InvalidOperationException("Pinqloq secret key is missing.");
options.ApiLogsCollectionName =
builder.Configuration["Pinqloq:ApiLogsCollectionName"];
options.DeviceIdentifier =
builder.Configuration["Pinqloq:DeviceIdentifier"];
});
var app = builder.Build();
app.UsePinqloqRequestLogging(options =>
{
options.ExcludePaths("/health", "/swagger");
});
app.MapControllers();
app.Run();
The middleware captures the HTTP method, path, and status code as searchable metadata. The request body, response body, request headers, and response headers go to the log detail as InputJson, OutputJson, RequestHeaders, and ResponseHeaders. Bodies are truncated at 32 KB.
Manual Logging
Inject IPinqloqLogger to send structured application events:
public class OrderService(IPinqloqLogger logger)
{
public void CreateOrder(Order order)
{
logger.Enqueue(new PinqloqLogEntry
{
Event = "order.created",
DeviceIdentifier = order.CustomerId.ToString(),
LogLevel = PinqloqLogLevel.Information,
LogSourceType = PinqloqLogSourceType.Backend,
Metadata = new()
{
["orderId"] = order.Id.ToString()
}
});
}
}
Event and DeviceIdentifier are required on every entry. Event is used as the log title; both are created automatically by the request-logging middleware. Leave DeviceIdentifier unset on an entry to inherit the global PinqloqOptions.DeviceIdentifier. Enqueue throws an InvalidOperationException if an entry has no DeviceIdentifier and no global PinqloqOptions.DeviceIdentifier is set — a missing required field fails loudly rather than being silently dropped.
Add Request Metadata
By default the middleware reads the required DeviceIdentifier from the device-identifier request header automatically — no configuration needed as long as callers send that header. Override how it is resolved with SetDeviceIdentifier (a claim, trace id, a different header, …); the override wins, and if it returns null/empty the middleware falls back to the device-identifier header, then to the global PinqloqOptions.DeviceIdentifier. If none of these resolve a value, the middleware rejects the request with HTTP 400 before it runs (rather than silently dropping the log).
To attach searchable request metadata, replace the middleware call from Quick Start with the following configuration. Use AddMetadata for searchable values such as user, tenant, and correlation IDs. Use AddDetail for additional drill-down information. The event key (the panel title) defaults to "{method} {path}" and can be overridden with AddMetadata("event", …).
app.UsePinqloqRequestLogging(options =>
{
options.ExcludePaths("/health", "/swagger");
options.SetDeviceIdentifier(context =>
context.User.FindFirst("sub")?.Value);
options.SetAppVersionName(context =>
context.Request.Headers["X-App-Version"].ToString());
options.AddMetadata("userId", context =>
context.User.FindFirst("sub")?.Value);
options.AddMetadata("correlationId", context =>
context.TraceIdentifier);
});
Redacting Sensitive Values
Request and response bodies and headers may contain credentials, tokens, or personal information. Mask them at the source with two attributes, both MVC-controller-only (they resolve through ControllerActionDescriptor, so minimal API endpoints are not scanned):
[PinqloqRedact]on a DTO property masks just that property's value with*****REDACTED*****wherever it appears inInputJson/OutputJson— the property can belong to the action's parameter type, its return type, or anything nested inside them (nested objects, collections).[PinqloqRedactEndpoint]on an action (or on the controller class, covering every action in it) masks every value inInputJson,OutputJson,RequestHeaders, andResponseHeadersfor that endpoint, keeping the JSON structure and header names intact.
public class AuthResponse
{
[PinqloqRedact]
public string Token { get; set; } = "";
public string UserId { get; set; } = "";
}
[ApiController]
[Route("api/user")]
public class UserController : ControllerBase
{
[HttpPost("Auth")]
public AuthResponse Auth([FromBody] AuthRequest request) => ...;
[PinqloqRedactEndpoint]
[HttpPost("Payment")]
public IActionResult Payment([FromBody] PaymentRequest request) => ...;
}
Logs Auth produces:
{ "token": "*****REDACTED*****", "userId": "64" }
Logs Payment produces (every value masked, including headers):
{ "cardNumber": "*****REDACTED*****", "amount": "*****REDACTED*****" }
[PinqloqRedact] only affects properties reachable from a type the middleware can see through reflection (the action's parameter and return types). It has no effect on serialization — the real value still goes out on the wire; only the log entry is masked. A non-JSON body (form data, plain text) with [PinqloqRedactEndpoint] applied is logged as a single masked string, since there is no JSON structure to preserve.
Using redaction from your own middleware
If you have your own request-logging middleware instead of UsePinqloqRequestLogging, call PinqloqRedaction.Resolve(HttpContext) directly — it runs the same reflection (cached per action) and returns a PinqloqRedactionPlan:
var plan = PinqloqRedaction.Resolve(httpContext);
// RedactAll is true when [PinqloqRedactEndpoint] applies; ShouldRedact covers both
// RedactAll and any [PinqloqRedact]-marked property name (case-insensitive).
bool isSensitive = existingSensitiveKeyCheck(fieldKey) || plan.ShouldRedact(fieldKey);
Resolve it once per request (it's endpoint-dependent, not field-dependent) and reuse the same PinqloqRedactionPlan for every field you check — plan.HasRedactions tells you upfront whether there's anything to mask at all, so you can skip the check entirely when it's false. ShouldRedact only knows about names (JSON property names and, for RedactAll, everything); it does not itself walk JSON — you still own how bodies/headers are traversed.
Security and Reliability
Redaction is opt-in and reflection-based — it protects only fields you've marked. Exclude sensitive endpoints entirely with ExcludePaths if you'd rather not log them at all.
Logs are buffered in memory and sent in batches. Buffered logs may be lost if the process is terminated without a graceful shutdown.
Delivery failures are reported through onFailed callbacks and, even without callbacks, as throttled entries in your application log — never silently discarded, but also never blocking. If your secret key is authorized for more than one collection, set ApiLogsCollectionName (or a per-entry CollectionName); otherwise the batch is rejected.
Documentation
License
MIT
| 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 is compatible. 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 is compatible. 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. |
-
net10.0
- No dependencies.
-
net8.0
- No dependencies.
-
net9.0
- No dependencies.
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 |
|---|---|---|
| 4.0.3 | 142 | 8/6/2026 |
| 4.0.2 | 306 | 7/22/2026 |
| 4.0.1 | 105 | 7/21/2026 |
| 4.0.0 | 105 | 7/20/2026 |
| 3.0.0 | 103 | 7/14/2026 |
| 2.0.8 | 110 | 7/14/2026 |
| 2.0.7 | 263 | 7/6/2026 |
| 2.0.6 | 103 | 7/6/2026 |
| 2.0.5 | 159 | 7/6/2026 |
| 2.0.4 | 108 | 7/6/2026 |
| 2.0.3 | 156 | 7/2/2026 |
| 2.0.2 | 108 | 7/2/2026 |
| 2.0.1 | 118 | 7/1/2026 |
| 2.0.0 | 158 | 6/23/2026 |
| 1.0.2 | 114 | 6/19/2026 |
| 1.0.1 | 186 | 6/12/2026 |
| 1.0.0 | 109 | 6/11/2026 |
4.0.3 BREAKING: Identifier renamed to DeviceIdentifier across PinqloqLogEntry, PinqloqOptions, and the wire contract. SetIdentifier is now SetDeviceIdentifier; the auto-read request header is now "device-identifier" (was "identifier"). Update entry.Identifier to entry.DeviceIdentifier, options.Identifier to options.DeviceIdentifier, and SetIdentifier(...) to SetDeviceIdentifier(...). Also BREAKING: the request-logging middleware now writes a fixed Detail set — InputJson, OutputJson, RequestHeaders, ResponseHeaders. The RequestMethod and ResponseCode detail keys were removed; read metadata["method"] and metadata["statusCode"] instead. Request and response headers are now captured automatically. Added [PinqloqRedact] (property-level) and [PinqloqRedactEndpoint] (action/controller-level) attributes to mask sensitive values with *****REDACTED***** in logs; both resolve via MVC's ControllerActionDescriptor, so minimal API endpoints are not scanned. BREAKING: a missing DeviceIdentifier now fails loudly instead of being silently dropped. The request-logging middleware resolves DeviceIdentifier as SetDeviceIdentifier -> "device-identifier" header -> PinqloqOptions.DeviceIdentifier and, if none yield a value, rejects the request with HTTP 400. Manual Enqueue throws InvalidOperationException when the entry has no DeviceIdentifier and no global fallback is set. Guarantee a value with SetDeviceIdentifier(ctx => ctx.Request.Headers["device-identifier"].FirstOrDefault() ?? Environment.MachineName) or PinqloqOptions.DeviceIdentifier. 4.0.1: AppVersionName sent as null (not "") when unset. 4.0.0 BREAKING: DeviceUid renamed to Identifier and made required — the ingest API rejects logs with an empty Identifier (single log to 400, batch entry skipped). Populate it per log via PinqloqLogEntry.Identifier, globally via PinqloqOptions.Identifier, or per request. The request-logging middleware now reads the "identifier" request header automatically; SetIdentifier overrides it and falls back to the header then to PinqloqOptions.Identifier. Also adds SetAppVersionName to override AppVersionName per request. The package now multi-targets net8.0, net9.0, and net10.0 (previously net9.0 only).