IDOC.Logs.Nlog
1.5.0
dotnet add package IDOC.Logs.Nlog --version 1.5.0
NuGet\Install-Package IDOC.Logs.Nlog -Version 1.5.0
<PackageReference Include="IDOC.Logs.Nlog" Version="1.5.0" />
<PackageVersion Include="IDOC.Logs.Nlog" Version="1.5.0" />
<PackageReference Include="IDOC.Logs.Nlog" />
paket add IDOC.Logs.Nlog --version 1.5.0
#r "nuget: IDOC.Logs.Nlog, 1.5.0"
#:package IDOC.Logs.Nlog@1.5.0
#addin nuget:?package=IDOC.Logs.Nlog&version=1.5.0
#tool nuget:?package=IDOC.Logs.Nlog&version=1.5.0
IDOC.Logs.Nlog
NLog target that sends log entries over UDP encrypted with DTLS 1.2 to a centralizing log server.
Each log entry is serialized as UTF-8 JSON and transmitted via a persistent DTLS session. If the connection fails, the target backs off for 30 seconds before retrying — log events emitted during the cooldown are silently dropped to avoid blocking the application.
Installation
dotnet add package IDOC.Logs.Nlog
Configuration
NLog.config
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<extensions>
<add assembly="IDOC.Logs.Nlog" />
</extensions>
<targets>
<target xsi:type="UdpTls"
name="centralisateur"
address="192.168.1.100"
port="5140"
application="MyApp"
version="1.2.0"
niveauEnvoi="Info" />
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="centralisateur" />
</rules>
</nlog>
Programmatic (NLog fluent API)
var config = new NLog.Config.LoggingConfiguration();
var target = new IDOC.Logs.NLog.UdpTlsTarget
{
Name = "centralisateur",
Address = "192.168.1.100",
Port = 5140,
Application = "MyApp",
// Version is auto-detected from the entry assembly when omitted
};
config.AddTarget(target);
config.AddRuleForAllLevels(target);
NLog.LogManager.Configuration = config;
Target properties
| Property | Type | Default | Description |
|---|---|---|---|
Address |
string |
"127.0.0.1" |
IP address or hostname of the log server |
Port |
int |
5140 |
UDP port of the log server |
Application |
string? |
null |
Application name injected into each log entry |
Version |
string? |
auto | Application version. If omitted, detected automatically from the entry assembly (Assembly.GetEntryAssembly().GetName().Version). |
NiveauEnvoi |
string |
"Info" |
Minimum level actually transmitted to the server. Events below this level only feed the breadcrumb buffer. Route the NLog rule with minlevel="Trace" to get full breadcrumbs. Since 1.5.0 this is the local default: the server may override it per application at runtime (see Server-driven emission level); if it does, the override wins until it expires, then the value here applies again. |
KeepAliveSeconds |
int |
60 |
Heartbeat interval in seconds (0 disables). Keeps the UDP/NAT mapping alive and detects zombie sessions: the server echoes each heartbeat; after 2 heartbeats without echo (server restarted), the session is renewed. See Connection behaviour. |
TailleMaxDatagramme |
int |
1200 |
Max datagram payload (bytes), kept under the ~1400 MTU so IP never fragments (fragmented UDP is often silently dropped). Larger payloads are split into up to 4 chunks reassembled by the server; beyond that the payload is degraded (fewer/truncated breadcrumbs, truncated message) so a log always gets through. |
ScopeProperties |
string |
"Cid,RequestPath" |
Comma-separated whitelist of NLog scope properties to keep. Cid is promoted to a dedicated payload field; the others go into Contexte. Properties outside the list (ASP.NET Core noise: ConnectionId, RequestId, ActionId…) are dropped. "*" keeps everything. |
Connection behaviour and keepalive
The DTLS session is opened lazily on the first log write and reused for subsequent logs.
Behind a NAT with a short UDP idle timeout — typically Azure Container Instances, where an expired outbound UDP mapping is not reliably re-established — a session left idle would be silently torn down, and generating traffic afterwards does not bring it back. To prevent this, a background timer sends a small heartbeat on the open session every KeepAliveSeconds, so the mapping is refreshed regularly and never expires. The heartbeat carries a marker (IDOC-KEEPALIVE) that the server recognises and drops — it creates no log entry. This requires a server that understands the marker (IDOC.Logs.Serveur; older servers would just log it as an invalid payload).
Choose KeepAliveSeconds below the NAT's UDP idle timeout (60–120 s is a safe range for most environments). If a heartbeat or a log send fails, the session is closed and a 30 s cooldown applies before the next attempt; events emitted during the cooldown are dropped without blocking the caller. Certificate validation is intentionally skipped — the target targets internal networks with a self-signed server certificate; DTLS encryption still protects the transport.
Server-driven emission level
Since 1.5.0, the heartbeat carries the application name (IDOC-KEEPALIVE|<application>) and the server replies with the emission level it wants that application to use (IDOC-KEEPALIVE|<level>). When a level is returned, it overrides NiveauEnvoi at runtime — letting an operator temporarily lower an application to Trace for a diagnostic window, then restore it centrally, without redeploying. The change takes effect within one heartbeat interval (KeepAliveSeconds).
The local NiveauEnvoi is always the fallback: an empty reply (no override configured, or the override expired) or an older server that echoes the bare heartbeat both leave the local value in force. A malformed/unknown level is ignored. Set the override and its optional expiry per application in the log server admin (or via PATCH /api/applicationsautorisees/{nom}/niveau).
1.5.0 requires an up-to-date IDOC.Logs.Serveur (heartbeat echo carrying the level). Deploy the server first; a 1.5.0 client against an older server keeps its local level and renews its session more often (the enriched heartbeat isn't echoed) — functional, but update the server to benefit from central control.
Global exception capture
Call once at application startup:
IDOC.Logs.NLog.CaptureGlobale.Activer();
This hooks AppDomain.UnhandledException (logged as Fatal, logs flushed before the process dies) and TaskScheduler.UnobservedTaskException (logged as Error, then marked observed).
For WPF applications, also hook the dispatcher:
// App.xaml.cs
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
IDOC.Logs.NLog.CaptureGlobale.Activer();
DispatcherUnhandledException += (_, args) =>
{
NLog.LogManager.GetCurrentClassLogger().Fatal(args.Exception, "Exception non gérée (dispatcher WPF).");
NLog.LogManager.Flush(TimeSpan.FromSeconds(3));
// args.Handled = true; // uncomment to keep the app alive
};
}
Breadcrumbs and context
The target keeps a circular buffer of the last 30 log events (all levels, including those below NiveauEnvoi). When a Warn, Error or Fatal event is sent, the payload's Contexte field carries a JSON object with:
Breadcrumbs— the buffered events preceding the error (HH:mm:ss.fff [Level] Logger — Message)Utilisateur— Windows user nameOs— OS version stringUptimeProcess— process uptime (d.hh:mm:ss)MemoireProcessMo— process working set in MB
For events below Error, Contexte is null — the wire format is unchanged from 1.0.x for them.
Correlation id / scope properties
Any property pushed onto the NLog scope (ScopeContext.PushProperty) is captured into Contexte on every transmitted log line — automatically propagated to all logs emitted inside the scope (including across async/await). Typical use: a per-request correlation id set once in middleware.
// ASP.NET Core middleware, once per request
using (NLog.ScopeContext.PushProperty("Cid", correlationId))
{
await next(context); // every log emitted downstream carries Cid in Contexte
}
Since 1.4.0, Cid is a dedicated, indexed column on the server (WHERE Cid = '…' ORDER BY Horodatage reconstructs a full user journey); other whitelisted scope properties land in the Contexte JSON. Consumers that never push a scope property see no change.
1.4.0 requires an up-to-date IDOC.Logs.Serveur (chunk reassembly + heartbeat echo). Deploy the server first; clients ≤ 1.3.0 keep working unchanged against the new server.
JSON payload
Each log entry is transmitted as a UTF-8 JSON object:
{
"Horodatage": "2026-06-20T12:34:56.789Z",
"Niveau": "Info",
"Logger": "MyNamespace.MyClass",
"Message": "Operation completed",
"Exception": null,
"Application": "MyApp",
"Machine": "SERVER01",
"Version": "1.2.0",
"Contexte": null
}
Requirements
- .NET 10.0+
- A DTLS-capable UDP log server (e.g. IDOC.Logs.Serveur)
Dependencies
- NLog >= 6.1.2
- BouncyCastle.Cryptography >= 2.5.1
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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
- BouncyCastle.Cryptography (>= 2.6.2)
- NLog (>= 6.1.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.