ISC.Kafka.Logging 1.0.0

dotnet add package ISC.Kafka.Logging --version 1.0.0
                    
NuGet\Install-Package ISC.Kafka.Logging -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="ISC.Kafka.Logging" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ISC.Kafka.Logging" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="ISC.Kafka.Logging" />
                    
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 ISC.Kafka.Logging --version 1.0.0
                    
#r "nuget: ISC.Kafka.Logging, 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 ISC.Kafka.Logging@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=ISC.Kafka.Logging&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=ISC.Kafka.Logging&version=1.0.0
                    
Install as a Cake Tool

ISC.Kafka.Logging

Lightweight, non-blocking, fail-safe Kafka logging client for .NET.

Why This Package?

The legacy IKafkaProducer has critical issues in production:

Legacy ISC.Kafka.Logging
Request blocking await ProduceAsync() blocks the request until Kafka responds or times out (3-5s) void ProduceAsync() returns immediately, Kafka delivery happens in background
Kafka down Requests slow down or hang, risk of ThreadPool starvation under load Zero impact on request latency, messages are silently dropped
Exception handling Logs error but caller must still await All exceptions swallowed internally, never propagates
Timeout defaults 3000-5000ms — too high for logging 500-1000ms — fail fast
Configuration Hardcoded SASL mechanism, values mixed in constructor Fully configurable via IConfiguration, no hardcoded values
Testability Tightly coupled to Kafka Pluggable ILogTransport — swap to Console, Null, or custom
Credentials Read directly from IConfiguration in constructor Config-driven via IOptions<KafkaOptions>, supports Vault / env vars / User Secrets

Migration from Legacy

4 steps — minimal code changes

Step 1. Install package

dotnet add package ISC.Kafka.Logging

Step 2. Replace using

- using legacy.Repositories.Kafka;
+ using ISC.Kafka.Logging;

Step 3. Replace DI registration

- services.AddSingleton<IKafkaProducer, KafkaProducer>();
+ services.AddKafkaProducer(configuration.GetSection("Kafka"));

Step 4. Remove await from all ProduceAsync calls

- await _producer.ProduceAsync(message);
+ _producer.ProduceAsync(message);

That's it. Same interface name IKafkaProducer, same method name ProduceAsync. No other code changes needed.


Installation

dotnet add package ISC.Kafka.Logging

<details> <summary>Other install methods</summary>

Package Manager:

Install-Package ISC.Kafka.Logging

PackageReference:

<PackageReference Include="ISC.Kafka.Logging" Version="1.0.0" />

</details>

Compatibility

Requirement Version
.NET 8.0+
Confluent.Kafka 2.14.0
Microsoft.Extensions.Options 10.0.6
Microsoft.Extensions.DependencyInjection.Abstractions 10.0.6

Quick Start

1. Add configuration

// appsettings.json
{
  "Kafka": {
    "BootstrapServers": "localhost:9092",
    "Topic": "app-logs",
    "EnableLogging": true
  }
}

2. Register services

builder.Services.AddKafkaProducer(builder.Configuration.GetSection("Kafka"));

3. Inject and use

public class OrderService
{
    private readonly IKafkaProducer _producer;

    public OrderService(IKafkaProducer producer)
    {
        _producer = producer;
    }

    public void CreateOrder(int userId)
    {
        // Fire-and-forget — returns immediately, Kafka delivery happens in background
        _producer.ProduceAsync(new { UserId = userId, Action = "CREATE_ORDER", Timestamp = DateTime.UtcNow });
    }
}

Configuration

All options are bound from IConfiguration via KafkaOptions:

Property Type Default Description
BootstrapServers string (required) Kafka broker addresses (e.g. host1:9092,host2:9092)
Topic string (required) Kafka topic to send log messages to
SaslUsername string? null SASL username. When set, enables SASL authentication
SaslPassword string? null SASL password
SaslMechanism string? "Plain" SASL mechanism: Plain, ScramSha256, ScramSha512
SecurityProtocol string? "SaslSsl" Security protocol: SaslSsl, SaslPlaintext, Ssl, Plaintext
ClientSoftwareName string? null Identifies the client application on the Kafka broker (for monitoring)
MessageTimeoutMs int 500 Max time (ms) to wait for message delivery
SocketTimeoutMs int 1000 Broker socket timeout (ms)
RetryBackoffMs int 200 Backoff time (ms) between retries
MessageSendMaxRetries int 1 Max delivery retry attempts
EnableLogging bool true Set to false to disable all log delivery

SASL/SSL Authentication

When SaslUsername is provided, SASL authentication is enabled. Defaults to SaslMechanism = Plain and SecurityProtocol = SaslSsl if not explicitly configured.

Do not store credentials in appsettings.json or source code. Use one of the methods below to inject SaslUsername and SaslPassword into IConfiguration at runtime.

appsettings.json only contains non-secret values:

{
  "Kafka": {
    "BootstrapServers": "broker.example.com:9093",
    "Topic": "app-logs",
    "SaslMechanism": "ScramSha512",
    "SecurityProtocol": "SaslPlaintext",
    "ClientSoftwareName": "my-service"
  }
}

Credentials are supplied separately via any of the following methods. All values merge into the same IConfiguration section and are automatically bound to KafkaOptions — no code changes required.

export Kafka__SaslUsername=your-username
export Kafka__SaslPassword=your-password

.NET maps Kafka__SaslUsername (double underscore) to IConfiguration["Kafka:SaslUsername"] automatically.

dotnet user-secrets init
dotnet user-secrets set "Kafka:SaslUsername" "your-username"
dotnet user-secrets set "Kafka:SaslPassword" "your-password"

Requires AddUserSecrets() in the host builder (included by default in ASP.NET Core Development environment).

Install the config provider for your secrets manager and register it at startup. Credentials are pushed into IConfiguration before the app starts.

Provider NuGet Package
HashiCorp Vault VaultSharp.Extensions.Configuration
Azure Key Vault Azure.Extensions.AspNetCore.Configuration.Secrets
AWS Secrets Manager AWSSDK.Extensions.Configuration.SecretsManager

Example (Azure Key Vault):

builder.Configuration.AddAzureKeyVault(
    new Uri("https://your-vault.vault.azure.net/"),
    new DefaultAzureCredential());

Store secrets in the vault with keys Kafka--SaslUsername and Kafka--SaslPassword (double dash -- maps to the : separator).

Development:

{
  "Kafka": {
    "BootstrapServers": "localhost:9092",
    "Topic": "dev-logs",
    "MessageTimeoutMs": 1000,
    "SocketTimeoutMs": 2000,
    "MessageSendMaxRetries": 1
  }
}

Production (high-load):

{
  "Kafka": {
    "BootstrapServers": "broker1:9092,broker2:9092",
    "Topic": "prod-logs",
    "MessageTimeoutMs": 300,
    "SocketTimeoutMs": 500,
    "RetryBackoffMs": 100,
    "MessageSendMaxRetries": 0
  }
}

Transports

The package ships with three built-in transports:

Transport Description Use case
KafkaTransport Sends messages to Kafka via Confluent producer Production (default)
ConsoleTransport Writes messages to Console.WriteLine Local development
NullTransport Discards all messages (no-op) Testing / disable logging

Using a different transport

// Console output for development
builder.Services.AddKafkaProducer<ConsoleTransport>(builder.Configuration.GetSection("Kafka"));

// Disable logging entirely
builder.Services.AddKafkaProducer<NullTransport>(builder.Configuration.GetSection("Kafka"));

Custom transport

Implement the ILogTransport interface:

public class FileTransport : ILogTransport
{
    public async Task SendAsync(string payload)
    {
        await File.AppendAllTextAsync("logs.txt", payload + Environment.NewLine);
    }
}

// Register
builder.Services.AddKafkaProducer<FileTransport>(builder.Configuration.GetSection("Kafka"));

API Reference

IKafkaProducer

public interface IKafkaProducer
{
    void ProduceAsync(object message);
}
  • void return — fire-and-forget by design, cannot be awaited
  • message is serialized to JSON via System.Text.Json with JsonSerializerDefaults.Web (camelCase), using runtime type (not declared type)
  • null messages are silently ignored
  • Kafka delivery happens in the background, never blocks the caller
  • Serialization or delivery errors are silently swallowed

ILogTransport

public interface ILogTransport
{
    Task SendAsync(string payload);
}
  • payload is the JSON-serialized string of the log event
  • Implement this interface to create a custom transport

ServiceCollectionExtensions

// Register with default KafkaTransport
services.AddKafkaProducer(configuration.GetSection("Kafka"));

// Register with custom transport
services.AddKafkaProducer<TTransport>(configuration.GetSection("Kafka"));

Failure Behavior

Scenario Behavior
Kafka is slow Message times out and is dropped
Kafka is down Message is dropped
Serialization error Error is swallowed
Any exception Handled internally, never thrown to caller

The library is designed for best-effort delivery. It should be used for logging, audit trails, and non-critical events — not for transactions that require guaranteed delivery.

Publishing

See PUBLISHING.md for instructions on building and publishing the NuGet package.

License

MIT

Product 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. 
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 100 4/16/2026