Enterprise-grade logging SDK for .NET with file-based logging, distributed tracing, ASP.NET Core middleware, Format methods (log4net-compatible), Logger API (Node.js-style), and ILogger integration. Zero external dependencies except Microsoft.Extensions.Logging.Abstractions. Supports .NET Framework 4.7.2+, .NET Standard 2.0, .NET Core 2.1+, .NET 6+, and .NET 8+.
$ dotnet add package LogSystem.ClientEnterprise-grade logging SDK for .NET Framework 4.7.2+, .NET Core 2.0+, and .NET 6+, with file-based logging, distributed tracing, and seamless integration with ILogger and Log4Net.
# Core client library
dotnet add package LogSystem.Client
# Log4Net integration (optional)
dotnet add package LogSystem.Log4Net
FormatExample/ for basic usage and Format methodsWebFormatExample/ for REST API integration with dependency injectionQuick test:
# Test console example
cd sdks/dotnet/FormatExample
dotnet run
# Test web API example
cd sdks/dotnet/WebFormatExample
dotnet run
# Visit http://localhost:5289/swagger
using LogSystem.Client;
var logger = new LogClient(new LogClientOptions
{
ApiKey = "your-api-key",
Service = "my-service",
LogDirectory = "C:\\logs\\myapp", // Windows
// LogDirectory = "/var/logs/myapp", // Linux
Logger = "MyApp.MainClass", // Optional: adds logger field
EnableTracing = true
});
// Use logger API methods
logger.Info("Application started", new Dictionary<string, object>
{
["version"] = "1.0.0",
["environment"] = "production"
});
logger.Debug("Configuration loaded", new Dictionary<string, object>
{
["config_file"] = "app.json"
});
logger.Warn("High memory usage", new Dictionary<string, object>
{
["memory_mb"] = 950,
["threshold_mb"] = 1024
});
// Error with exception
try {
ProcessOrder();
} catch (Exception ex) {
logger.Error("Order processing failed", ex, new Dictionary<string, object>
{
["order_id"] = "12345"
});
}
// Dispose to ensure all logs are written
logger.Dispose();
// Available log levels (matching Node.js SDK)
logger.Debug(message, properties); // Detailed diagnostic info
logger.Info(message, properties); // General informational messages
logger.Warn(message, properties); // Warning messages
logger.Error(message, properties); // Error messages
logger.Fatal(message, properties); // Critical errors
// Exception handling (auto-extracts error message and stack trace)
logger.Error(message, exception, properties);
logger.Fatal(message, exception, properties);
// log4net-style Format methods
logger.DebugFormat(format, ...args); // Debug with string.Format
logger.InfoFormat(format, ...args); // Info with string.Format
logger.WarnFormat(format, ...args); // Warn with string.Format
logger.ErrorFormat(format, ...args); // Error with string.Format
logger.FatalFormat(format, ...args); // Fatal with string.Format
Format Method Examples:
var logger = new LogClient(new LogClientOptions
{
ApiKey = "your-api-key",
Service = "order-service",
LogDirectory = "C:\\logs",
Logger = "OrderService"
});
// Like log4net's DebugFormat
logger.DebugFormat("CreateMtpData:companyOrderId={0},sellid={1},qty={2}",
companyOrderId, sellid, qty);
// InfoFormat with currency formatting
logger.InfoFormat("Order {0} created by user {1} with total amount {2:C}",
orderId, userId, 299.99m);
// WarnFormat with threshold checking
logger.WarnFormat("Product {0} stock is low: {1} units remaining (threshold: {2})",
productId, currentStock, threshold);
// ErrorFormat with error code
logger.ErrorFormat("Payment gateway {0} failed for order {1} with error code {2}",
gatewayName, orderId, errorCode);
Each log entry is written as a single-line JSON:
{
"@timestamp": "2025-12-06T00:58:35Z",
"api_key": "your-api-key",
"service": "my-service",
"host": "DESKTOP-PC",
"logger": "MyApp.MainClass",
"trace_id": "trace_abc123...",
"span_id": "span_def456...",
"level": "INFO",
"message": "Application started",
"version": "1.0.0",
"environment": "production"
}
// Program.cs (.NET 6+)
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddProvider(new LogSystemLoggerProvider(new LogClientOptions
{
ApiKey = "your-api-key",
Service = "my-api",
LogDirectory = "C:\\logs\\myapi",
Logger = "API"
}));
var app = builder.Build();
// In controller
public class OrderController : ControllerBase
{
private readonly ILogger<OrderController> _logger;
public OrderController(ILogger<OrderController> logger)
{
_logger = logger;
}
[HttpPost]
public IActionResult CreateOrder([FromBody] OrderRequest request)
{
_logger.LogInformation("Order created: {OrderId} by {UserId}",
request.OrderId, request.UserId);
return Ok();
}
}
<!-- log4net.config -->
<log4net>
<appender name="LogSystemAppender" type="LogSystem.Log4Net.LogSystemAppender, LogSystem.Log4Net">
<ApiKey value="your-api-key" />
<ServiceName value="my-app" />
<LogDirectory value="C:\logs\myapp" />
<Logger value="Log4NetApp" />
<BatchInterval value="5000" />
</appender>
<root>
<level value="INFO" />
<appender-ref ref="LogSystemAppender" />
</root>
</log4net>
using log4net;
var logger = LogManager.GetLogger(typeof(Program));
logger.Info("Application started");
logger.Error("Something went wrong", new Exception("Test exception"));
// Generate trace and span IDs
var traceId = LogClient.GenerateTraceId(); // trace_abc123...
var spanId = LogClient.GenerateSpanId(); // span_xyz789...
await client.SendAsync(new Dictionary<string, object>
{
["trace_id"] = traceId,
["span_id"] = spanId,
["level"] = "INFO",
["message"] = "Request started"
});
// In ASP.NET Core middleware
public async Task InvokeAsync(HttpContext context)
{
var headers = context.Request.Headers.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value.ToString()
);
var (traceId, parentId) = LogClient.ExtractTraceContext(headers);
var currentSpanId = LogClient.GenerateSpanId();
// Store in HttpContext for use in controllers
context.Items["trace_id"] = traceId ?? LogClient.GenerateTraceId();
context.Items["span_id"] = currentSpanId;
context.Items["parent_id"] = parentId;
await _next(context);
}
var traceId = context.Items["trace_id"].ToString();
var spanId = context.Items["span_id"].ToString();
var httpClient = new HttpClient();
var traceHeaders = LogClient.CreateTraceHeaders(traceId, spanId);
foreach (var header in traceHeaders)
{
httpClient.DefaultRequestHeaders.Add(header.Key, header.Value);
}
var response = await httpClient.PostAsync("http://downstream-service/api", content);
// API Gateway
var rootTraceId = LogClient.GenerateTraceId();
var gatewaySpanId = LogClient.GenerateSpanId();
await logger.SendAsync(new Dictionary<string, object>
{
["trace_id"] = rootTraceId,
["span_id"] = gatewaySpanId,
["parent_id"] = null, // root span
["level"] = "INFO",
["message"] = "Gateway received request"
});
// Call Order Service
var orderHeaders = LogClient.CreateTraceHeaders(rootTraceId, gatewaySpanId);
// ... add headers to HttpClient request
// Order Service receives request
var (orderTraceId, orderParentId) = LogClient.ExtractTraceContext(receivedHeaders);
var orderSpanId = LogClient.GenerateSpanId();
await logger.SendAsync(new Dictionary<string, object>
{
["trace_id"] = orderTraceId, // inherited from Gateway
["span_id"] = orderSpanId,
["parent_id"] = orderParentId, // gatewaySpanId
["level"] = "INFO",
["message"] = "Order service processing"
});
| Property | Type | Default | Description |
|---|---|---|---|
ApiKey | string | (required) | API key for authentication |
Service | string | (required) | Service name identifier |
Endpoint | string | http://localhost:8888 | Log server HTTP endpoint |
Host | string | Environment.MachineName | Host identifier |
BatchSize | int | 100 | Maximum logs in batch queue |
BatchInterval | int | 5000 | Batch send interval (ms) |
EnableBatch | bool | true | Enable batch sending |
EnableTracing | bool | true | Auto-generate trace_id |
{
"LogSystem": {
"ApiKey": "your-api-key",
"Service": "order-service",
"Endpoint": "http://localhost:8888",
"BatchSize": 100,
"BatchInterval": 5000
}
}
// Load from configuration
var options = new LogClientOptions();
builder.Configuration.GetSection("LogSystem").Bind(options);
builder.Logging.AddProvider(new LogSystemLoggerProvider(options));
public LogClient(LogClientOptions options)
Send a log entry asynchronously. Returns immediately after queuing.
await client.SendAsync(new Dictionary<string, object>
{
["level"] = "INFO",
["message"] = "User logged in",
["user_id"] = 12345
});
Send a log entry synchronously (blocking). Use SendAsync for better performance.
client.Send(new Dictionary<string, object>
{
["level"] = "ERROR",
["message"] = "Critical error"
});
Flush queued logs immediately.
await client.FlushAsync();
Flush synchronously (blocking).
client.Flush();
Generate a unique trace ID.
var traceId = LogClient.GenerateTraceId(); // "trace_abc123..."
Generate a unique span ID.
var spanId = LogClient.GenerateSpanId(); // "span_xyz789..."
Extract trace context from HTTP headers.
var (traceId, parentId) = LogClient.ExtractTraceContext(requestHeaders);
Create HTTP headers for downstream calls.
var headers = LogClient.CreateTraceHeaders(traceId, spanId);
// { "X-Trace-Id": "trace_...", "X-Span-Id": "span_..." }
ILoggerProvider implementation for Microsoft.Extensions.Logging.
// With existing LogClient
public LogSystemLoggerProvider(LogClient client, bool disposeClient = false)
// With options
public LogSystemLoggerProvider(LogClientOptions options)
Log4Net appender with HTTP support.
Configuration Properties:
ApiKey - API key (required)ServiceName - Service name (required)Endpoint - HTTP endpointBatchSize - Batch sizeBatchInterval - Batch interval (ms)EnableBatch - Enable batchingEnableTracing - Auto trace_id// ✅ Good: Structured fields
await client.SendAsync(new Dictionary<string, object>
{
["level"] = "INFO",
["message"] = "Order created",
["order_id"] = "ORD-12345",
["user_id"] = "user_789",
["amount"] = 99.99
});
// ❌ Bad: All in message
await client.SendAsync(new Dictionary<string, object>
{
["level"] = "INFO",
["message"] = "Order ORD-12345 created by user_789 for $99.99"
});
// Extract from incoming request
var (traceId, parentId) = LogClient.ExtractTraceContext(request.Headers);
// Use in current service
var currentSpanId = LogClient.GenerateSpanId();
// Pass to downstream services
var downstreamHeaders = LogClient.CreateTraceHeaders(
traceId ?? LogClient.GenerateTraceId(),
currentSpanId
);
// Leverage ILogger's structured logging
_logger.LogInformation(
"Order {OrderId} created by {UserId} for {Amount} {Currency}",
"ORD-123", "user_789", 99.99, "USD"
);
// Automatically becomes:
// {
// "message": "Order ORD-123 created by user_789 for 99.99 USD",
// "OrderId": "ORD-123",
// "UserId": "user_789",
// "Amount": 99.99,
// "Currency": "USD"
// }
// Use using statement
using var client = new LogClient(options);
await client.SendAsync(logData);
// Auto-flush on dispose
// Or manual flush
await client.FlushAsync();
client.Dispose();
try
{
await RiskyOperation();
}
catch (Exception ex)
{
await client.SendAsync(new Dictionary<string, object>
{
["trace_id"] = traceId,
["span_id"] = spanId,
["level"] = "ERROR",
["message"] = "Operation failed",
["error_message"] = ex.Message,
["error_type"] = ex.GetType().Name,
["error_stack"] = ex.StackTrace
});
}
Run the included examples:
cd examples
dotnet run # All examples
dotnet run --project BasicExample # Basic usage
dotnet run --project ILoggerExample # ILogger integration
dotnet run --project TraceExample # Distributed tracing
| v1.x (File-based) | v2.x (HTTP-based) |
|---|---|
new LogClient(apiKey, service, logDirectory) | new LogClient(new LogClientOptions { ApiKey, Service, Endpoint }) |
client.Write(logData) | await client.SendAsync(logData) |
| No batching | Built-in batch queue |
| Vector Agent required | Direct HTTP (Vector optional) |
# Build all projects
dotnet build
# Run examples
cd examples
dotnet run
# Run with custom endpoint
$env:LOG_ENDPOINT="http://production-server:8888"
dotnet run
| Target Framework | .NET Version | Status |
|---|---|---|
net472 | .NET Framework 4.7.2+ | ✅ Supported |
netstandard2.0 | .NET Core 2.0+ | ✅ Supported |
net6.0 | .NET 6 | ✅ Supported |
net8.0 | .NET 8 | ✅ Supported |
MIT
Version: 2.0.0
Last Updated: 2025-12-05
Status: ✅ Production Ready