DCTekSolutions.ExternalCallGuard.NLogTelemetrySink 1.0.0

dotnet add package DCTekSolutions.ExternalCallGuard.NLogTelemetrySink --version 1.0.0
                    
NuGet\Install-Package DCTekSolutions.ExternalCallGuard.NLogTelemetrySink -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="DCTekSolutions.ExternalCallGuard.NLogTelemetrySink" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DCTekSolutions.ExternalCallGuard.NLogTelemetrySink" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="DCTekSolutions.ExternalCallGuard.NLogTelemetrySink" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add DCTekSolutions.ExternalCallGuard.NLogTelemetrySink --version 1.0.0
                    
#r "nuget: DCTekSolutions.ExternalCallGuard.NLogTelemetrySink, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package DCTekSolutions.ExternalCallGuard.NLogTelemetrySink@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=DCTekSolutions.ExternalCallGuard.NLogTelemetrySink&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=DCTekSolutions.ExternalCallGuard.NLogTelemetrySink&version=1.0.0
                    
Install as a Cake Tool

ExternalCallGuard.NLogTelemetrySink

NuGet NuGet Downloads Target

ExternalCallGuard.NLogTelemetrySink by DC Tek Solutions turns ExternalCallGuard lifecycle events into structured NLog records. It keeps logging out of the core resilience package while giving operations teams consistent fields for starts, retries, successes, failures, cancellations, and timeouts.

Free to use in personal and commercial projects. This package is closed-source proprietary software; see LICENSE.md.

Install

Applications normally install both the core package and this adapter:

dotnet add package DCTekSolutions.ExternalCallGuard --version 1.0.0
dotnet add package DCTekSolutions.ExternalCallGuard.NLogTelemetrySink --version 1.0.0

The sink itself depends only on DCTekSolutions.ExternalCallGuard.Abstractions. This keeps the adapter reusable and prevents it from reaching into resilience implementation details.

Quick start

using DCTekSolutions.ExternalCallGuard.DependencyInjectionSetups;
using DCTekSolutions.ExternalCallGuard.NLogTelemetrySink.DependencyInjectionSetups;

services.AddExternalCallGuard();
services.AddNLogTelemetrySink();

That is enough to log retries, terminal failures, caller cancellations, and guard timeouts. Started and successful events are intentionally quiet by default.

Enable the optional lower-severity events when you need a complete call timeline:

services.AddNLogTelemetrySink(options =>
{
    options.LogStartedCalls = true;
    options.LogSuccessfulCalls = true;
});

Calling AddNLogTelemetrySink more than once does not duplicate this sink. Other IExternalCallTelemetrySink registrations are preserved.

If registration is repeated with more than one options function, every function is retained and applied in registration order even though only one sink is added.

Configuration reference

AddNLogTelemetrySink accepts an optional Action<NLogTelemetrySinkOptions>. In simple terms, this is a function that receives the options object so the application can change it during service registration:

services.AddNLogTelemetrySink(options =>
{
    options.LogStartedCalls = true;
    options.LogSuccessfulCalls = true;
});
Option Default Effect when enabled
LogStartedCalls false Writes each started event at Debug. The NLog rules must allow Debug records for these to reach a target.
LogSuccessfulCalls false Writes each successful event at Info. Leave this off when only exceptional activity is needed.

Retry, terminal failure, caller cancellation, and timeout records do not have switches and are always sent to NLog. Whether an emitted record reaches a file, database, console, or other destination is controlled by the application's NLog rules and targets.

Registration details

AddNLogTelemetrySink(IServiceCollection, Action<NLogTelemetrySinkOptions>?) returns the same service collection, so calls can be chained. It throws ArgumentNullException when the service collection is null.

The extension registers NLogTelemetrySink as an IExternalCallTelemetrySink without replacing other sink implementations. It also preserves an existing NLog.ILogger. When no logger has been registered, it adds one named ExternalCallGuard.

ServiceRegistry is the public helper behind the extension method. Most applications do not need it, but explicit registration is equivalent:

var registry = new ServiceRegistry(options =>
{
    options.LogStartedCalls = true;
    options.LogSuccessfulCalls = true;
});

registry.RegisterServices(services);

RegisterServices also returns the same service collection and throws ArgumentNullException for a null collection.

Calling the sink directly

Dependency injection normally creates and calls the sink. A test or focused integration can construct it directly with an NLog logger and IOptions<NLogTelemetrySinkOptions>:

using System;
using System.Collections.Generic;
using System.Threading;
using DCTekSolutions.ExternalCallGuard.Abstractions.Models;
using DCTekSolutions.ExternalCallGuard.NLogTelemetrySink.Models;
using DCTekSolutions.ExternalCallGuard.NLogTelemetrySink.Sinks;
using Microsoft.Extensions.Options;

NLog.ILogger logger = NLog.LogManager.GetLogger("ExternalCallGuard");
var sinkOptions = Options.Create(new NLogTelemetrySinkOptions
{
    LogSuccessfulCalls = true
});
var sink = new NLogTelemetrySink(logger, sinkOptions);

var call = new ExternalCallDescriptor(
    "CatalogApi",
    "GetProduct",
    new ExternalCallPolicyKey("Standard"),
    new Dictionary<string, object?> { ["region"] = "east" });

var telemetry = new ExternalCallSucceededEvent(
    call,
    DateTimeOffset.UtcNow,
    TimeSpan.FromMilliseconds(42),
    attemptCount: 1);

await sink.WriteAsync(telemetry, CancellationToken.None);

The constructor throws ArgumentNullException for a missing logger, options wrapper, or options value. WriteAsync throws ArgumentNullException for a missing event and OperationCanceledException when its token is already canceled. It completes synchronously after writing or ignoring the event. The token is checked before logging starts; it does not cancel an NLog target after logging has begun.

When called directly, an NLog target exception can surface if NLog is configured to throw. When the sink is registered with ExternalCallGuard, the guard isolates telemetry-sink failures from the guarded operation's result, exception, cancellation, and timing.

Event levels

Guard event NLog level Written by default?
Started Debug No; enable LogStartedCalls.
Retry scheduled Warn Yes.
Succeeded Info No; enable LogSuccessfulCalls.
Failed Error Yes.
Cancelled by caller Info Yes.
Timed out Error Yes.

Caller cancellation is logged separately from a timeout so dashboards do not mistake abandoned work for dependency failure.

Structured properties

Every emitted record includes these searchable NLog event properties:

Property Meaning
Dependency Stable external-system name from the call descriptor.
Operation Stable operation name from the call descriptor.
Policy Policy key selected for the call.
DurationMilliseconds Elapsed call duration when the event was emitted.
Outcome Started, Retry, Succeeded, Failed, Cancelled, or TimedOut.
Metadata The complete caller-supplied read-only metadata dictionary.
Metadata.<key> Each metadata item flattened for structured-log queries.

Event-specific properties are:

Event Additional properties
Started None.
Retry Attempt, RetryDelayMilliseconds, ExceptionType, and the original exception.
Succeeded AttemptCount.
Failed AttemptCount, ExceptionType, and the original exception.
Cancelled AttemptCount, ExceptionType, and the original OperationCanceledException.
Timed out AttemptCount, ExceptionType, and the original timeout exception.

WriteAsync safely ignores an unknown ExternalCallTelemetryEvent subtype. It does not turn an unknown event into a misleading log record.

Example NLog configuration

The sink uses the logger name ExternalCallGuard unless an application supplies its own NLog.ILogger registration. JSON output makes the structured properties easy to ingest:

<?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">
  <targets>
    <target xsi:type="File"
            name="externalCalls"
            fileName="${basedir}/logs/external-calls.json">
      <layout xsi:type="JsonLayout" includeAllProperties="true">
        <attribute name="timestamp" layout="${longdate}" />
        <attribute name="level" layout="${level}" />
        <attribute name="logger" layout="${logger}" />
        <attribute name="message" layout="${message}" />
        <attribute name="exception" layout="${exception:format=tostring}" />
      </layout>
    </target>
  </targets>

  <rules>
    <logger name="ExternalCallGuard" minlevel="Debug" writeTo="externalCalls" />
  </rules>
</nlog>

If you leave started and successful events disabled, minlevel="Info" is normally sufficient. Keep Debug when started events are enabled.

Typical log queries

Because the outcome is semantic rather than inferred from message text, common operational questions are straightforward:

Outcome = "Retry" and Dependency = "CatalogApi"
Outcome = "TimedOut" and Operation = "GetProduct"
Outcome = "Failed" and ExceptionType = "System.Net.Http.HttpRequestException"
Metadata.region = "east"

The exact query syntax depends on the NLog target and log-analysis platform, but the property names stay consistent.

Use case: see retries without logging every success

The default settings are useful for normal production traffic:

services.AddExternalCallGuard();
services.AddNLogTelemetrySink();

Successful calls add no NLog noise. A transient failure produces a Warn record for each scheduled retry, and a final exhausted failure produces one Error record. This makes intermittent dependency trouble visible without a log entry for every healthy request.

Use case: capture a full diagnostic timeline

Temporarily enable started and successful events while diagnosing latency or policy selection:

services.AddNLogTelemetrySink(options =>
{
    options.LogStartedCalls = true;
    options.LogSuccessfulCalls = true;
});

Filter the records using stable Dependency, Operation, and metadata values to reconstruct a call's progression through retry and completion.

Use case: provide a custom NLog logger

Register an NLog.ILogger before adding the sink when you need a different logger name or factory strategy:

using NLog;

services.AddSingleton<ILogger>(_ =>
    LogManager.GetLogger("Application.ExternalDependencies"));

services.AddNLogTelemetrySink();

The adapter respects an existing NLog.ILogger registration and does not replace it.

Combine with other telemetry sinks

The registration uses the enumerable dependency-injection pattern, so NLog can coexist with metrics, tracing, auditing, or test sinks:

services.AddSingleton<IExternalCallTelemetrySink, MetricsTelemetrySink>();
services.AddNLogTelemetrySink();

The core guard dispatches to the sinks in registration order. A sink exception or telemetry timeout does not change the business operation's result or replace its exception.

Security guidance

Descriptor metadata is flattened into NLog properties. Never put passwords, access tokens, authorization headers, cookies, connection strings, raw payloads, personal information, or other secrets in dependency names, operation names, or metadata. Prefer stable, low-cardinality labels such as service name, region, or request category.

Compatibility

  • Single-targets .NET Standard 2.1 and can be consumed by runtimes that implement that standard. .NET Framework does not implement .NET Standard 2.1.
  • Depends on DCTekSolutions.ExternalCallGuard.Abstractions, NLog, and Microsoft dependency-injection/options abstractions.
  • Does not require a project reference to the core implementation.
  • Implements only IExternalCallTelemetrySink; it does not alter retry or timeout behavior.

License

Free for personal and commercial use under the included DC Tek Solutions license. Package license acceptance is required during installation.

Support / Donate

ExternalCallGuard is developed by DC Tek Solutions. For documentation and support, visit the ExternalCallGuard support wiki.

If this package saves you time, please consider supporting continued development:

Buy Me A Coffee

Thank you for your support!

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  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. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
1.0.0 84 9/21/2026

Initial standalone release of the structured NLog adapter for ExternalCallGuard telemetry.