IDiginsightActivitiesLogOptions Interface
Configuration options for activity logging behavior and rendering control
IDiginsightActivitiesLogOptions Interface
The IDiginsightActivitiesLogOptions provides configuration options for controlling activity logging behavior with fine-grained control over log visibility, rendering, and formatting.
In particular, it defines how activities are logged, which activities should be shown or hidden, at what log level they should be recorded, and how their content should be rendered. This interface is consumed by the ActivityLifecycleLogEmitter to make logging decisions during activity lifecycle events.
IDiginsightActivitiesLogOptions is part of Diginsight.Diagnostics (Diginsight.Diagnostics.dll).
The primary implementation is DiginsightActivitiesOptions, which supports dynamic and volatile configuration patterns for runtime adjustments.
Table of Contents
📋 Overview
The IDiginsightActivitiesLogOptions interface controls activity logging behavior at multiple levels:
- Global log behavior: Default logging mode for all activities
- Activity-specific behavior: Override logging for specific activity names
- Log level assignment: Determine at which severity level activities are logged
- Format control: Configure how activity information is formatted (prefix vs suffix)
- Payload rendering: Control whether activity payloads (parameters, results) are rendered
This interface is primarily consumed by ActivityLifecycleLogEmitter during activity start and stop events to determine: - Whether the activity should be logged (LogBehavior) - At what log level (LogLevel) - How to format the output (WriteActivityActionAsPrefix) - Whether to include payload details (DisablePayloadRendering)
Key Features
- Flexible Log Behavior Control: Configure activities to be shown, hidden, or truncated at both global and individual levels
- Activity Name Dictionary: Map specific activity names to custom log behaviors for fine-grained control
- Log Level Configuration: Specify the severity level for activity logging (Debug, Information, Warning, etc.)
- Format Customization: Control whether activity actions appear as prefix or suffix in log messages
- Payload Rendering Toggle: Enable or disable rendering of activity parameters and return values
- Class-Aware Support: Works with
IClassAwareOptions<T>for class-level configuration granularity - Dynamic Configuration: Supports
IDynamicallyConfigurablefor runtime updates - Volatile Configuration: Supports
IVolatilelyConfigurablefor temporary overrides
Log Behavior Control
The interface provides three primary log behaviors through the LogBehavior enum:
| Behavior | Effect | Use Case |
|---|---|---|
| Show | Activity and all children are logged normally | Default behavior for visible activities |
| Hide | Activity and all children are not logged | Performance-sensitive code, high-frequency operations |
| Truncate | Activity is logged but children are hidden | Show high-level operations without internal details |
The LogBehavior property sets the default behavior, while ActivityNames dictionary allows per-activity overrides.
Activity-Specific Configuration
The ActivityNames dictionary enables precise control over individual activities:
Activities not present in the dictionary inherit the global LogBehavior setting.
🔍 Additional Details
LogBehavior Enum Values
The LogBehavior enum is defined in Diginsight.Diagnostics namespace:
Hierarchical Behavior: - When a parent activity has Truncate, all child activities inherit Truncate regardless of their own settings - This prevents partial visibility of deeply nested call stacks - When a parent activity has Hide, children are also not logged
Example Hierarchy:
Activity A (Show)
├─ Activity B (Truncate) ← Only B is logged
│ ├─ Activity C (Show) ← NOT logged (parent is Truncate)
│ └─ Activity D (Show) ← NOT logged (parent is Truncate)
└─ Activity E (Show) ← Logged normally
└─ Activity F (Show) ← Logged normally
Activity Name Matching
Activity names in the ActivityNames dictionary are matched using exact string comparison:
Matching Rules: - Activity name is taken from Activity.OperationName - Case-sensitive matching - No wildcard or pattern matching support (exact match only) - First match wins (no priority ordering)
Example:
Log Level Control
The LogLevel property determines the severity level at which activities are logged:
Common Log Levels: - LogLevel.Trace - Extremely detailed diagnostic information - LogLevel.Debug - Detailed debugging information (default for activities) - LogLevel.Information - General informational messages - LogLevel.Warning - Warning messages for potentially harmful situations - LogLevel.Error - Error messages for failures
Integration with Logger Configuration:
// Activities logged at Debug level
// Only visible if logger is configured to show Debug or lower
services.Configure<DiginsightActivitiesOptions>(options =>
{
options.ActivityLogLevel = LogLevel.Debug;
});
// Logger must allow Debug level for the namespace
services.AddLogging(builder =>
{
builder.SetMinimumLevel(LogLevel.Debug);
});Payload Rendering Control
The DisablePayloadRendering property controls whether activity parameters and results are included in log output:
When enabled (true): - Activity start logs show only activity name - Activity end logs show only activity name and duration - Parameter values are NOT rendered - Return values are NOT rendered - Reduces log verbosity and potential PII exposure
When disabled (false, default): - Activity start logs include parameter names and values - Activity end logs include return values - Full diagnostic details available - May expose sensitive data
// Example output comparison
// DisablePayloadRendering = false (default)
START MyService.GetUser(userId: 12345, includeDetails: true)
MyService.GetUser STOP (duration: 45ms) => User { Id: 12345, Name: "John Doe" }
// DisablePayloadRendering = true
START MyService.GetUser
MyService.GetUser STOP (duration: 45ms)⚙️ Configuration
Configuration in appsettings.json
{
"Diginsight": {
"DiginsightActivitiesOptions": {
"LogBehavior": "Show",
"ActivityLogLevel": "Debug",
"WriteActivityActionAsPrefix": false,
"DisablePayloadRendering": false,
"LoggedActivityNames": {
"UserService.GetUserById": "Show",
"UserService.GetUserList": "Truncate",
"HighFrequencyPoller": "Hide",
"DatabaseQuery": "Truncate"
}
}
}
}Configuration Properties: - LogBehavior: Global default behavior - "Show", "Hide", or "Truncate" - ActivityLogLevel: Severity level - "Trace", "Debug", "Information", "Warning", "Error", "Critical" - WriteActivityActionAsPrefix: true for "START Activity", false for "Activity START" - DisablePayloadRendering: true to hide parameters/results, false to show them - LoggedActivityNames: Dictionary mapping activity names to specific behaviors
Configuration in the startup sequence
Register Diginsight diagnostics in your service collection:
// In Program.cs
using Diginsight.Diagnostics;
using Microsoft.Extensions.Logging;
var builder = WebApplication.CreateBuilder(args);
// Add Diginsight diagnostics
builder.Services.AddDiginsightDiagnostics();
// Configure activity logging options
builder.Services.Configure<DiginsightActivitiesOptions>(options =>
{
options.LogBehavior = LogBehavior.Show;
options.ActivityLogLevel = LogLevel.Debug;
options.WriteActivityActionAsPrefix = false;
options.DisablePayloadRendering = false;
// Configure specific activities
options.LoggedActivityNames["MyService.CriticalOperation"] = LogBehavior.Show;
options.LoggedActivityNames["HighFrequencyTask"] = LogBehavior.Hide;
});
// Or bind from configuration
builder.Services.Configure<DiginsightActivitiesOptions>(
builder.Configuration.GetSection("Diginsight:DiginsightActivitiesOptions"));
var app = builder.Build();Class-Aware Configuration
Configure different options for specific classes or namespaces:
services.ConfigureClassAware<DiginsightActivitiesOptions>(
"MyApp.Services.*",
options =>
{
// All services: log activities at Information level
options.ActivityLogLevel = LogLevel.Information;
options.LogBehavior = LogBehavior.Show;
});
services.ConfigureClassAware<DiginsightActivitiesOptions>(
"MyApp.Repositories.*",
options =>
{
// Repositories: truncate to hide low-level SQL queries
options.LogBehavior = LogBehavior.Truncate;
options.ActivityLogLevel = LogLevel.Debug;
});
services.ConfigureClassAware<DiginsightActivitiesOptions>(
"MyApp.HighFrequency.*",
options =>
{
// High-frequency operations: hide to reduce noise
options.LogBehavior = LogBehavior.Hide;
});Key Points: - Class-aware configuration uses pattern matching on fully qualified type names - More specific patterns override less specific ones - Supports wildcards (*) for namespace matching - Retrieved via IClassAwareOptions<DiginsightActivitiesOptions> by passing the caller type
Dynamic and Volatile Configuration
DiginsightActivitiesOptions implements both IDynamicallyConfigurable and IVolatilelyConfigurable:
// Dynamic configuration - persists across requests
public class ConfigurationController : ControllerBase
{
private readonly IOptionsMonitor<DiginsightActivitiesOptions> _optionsMonitor;
public IActionResult EnableDebugLogging(string activityName)
{
var options = _optionsMonitor.CurrentValue;
// Update at runtime
options.LoggedActivityNames[activityName] = LogBehavior.Show;
options.ActivityLogLevel = LogLevel.Trace;
return Ok($"Enabled debug logging for {activityName}");
}
}
// Volatile configuration - temporary override for single request
public class MyService
{
private readonly IVolatileConfigurationManager _volatileConfig;
public async Task<T> ExecuteWithDetailedLogging<T>(Func<Task<T>> operation)
{
// Temporarily enable verbose logging
using var scope = _volatileConfig.BeginScope(new DiginsightActivitiesOptions
{
LogBehavior = LogBehavior.Show,
ActivityLogLevel = LogLevel.Trace,
DisablePayloadRendering = false
});
return await operation();
}
}💡 Usage Examples
Basic Usage
using Diginsight.Diagnostics;
using Microsoft.Extensions.Options;
public class MyService
{
private readonly ILogger<MyService> _logger;
private readonly IOptionsMonitor<DiginsightActivitiesOptions> _activitiesOptions;
public MyService(
ILogger<MyService> logger,
IOptionsMonitor<DiginsightActivitiesOptions> activitiesOptions)
{
_logger = logger;
_activitiesOptions = activitiesOptions;
}
public void CheckCurrentConfiguration()
{
var options = (IDiginsightActivitiesLogOptions)_activitiesOptions.CurrentValue;
_logger.LogInformation("Activity logging configuration:");
_logger.LogInformation(" Global behavior: {Behavior}", options.LogBehavior);
_logger.LogInformation(" Log level: {Level}", options.LogLevel);
_logger.LogInformation(" Prefix format: {Prefix}", options.WriteActivityActionAsPrefix);
_logger.LogInformation(" Disable payloads: {Disabled}", options.DisablePayloadRendering);
foreach (var kvp in options.ActivityNames)
{
_logger.LogInformation(" Activity '{Name}': {Behavior}",
kvp.Key, kvp.Value);
}
}
}Explanation: - Access current options via IOptionsMonitor<DiginsightActivitiesOptions> - Cast to IDiginsightActivitiesLogOptions to access the interface properties - All properties are read-only from the interface perspective - Configuration can be updated via the concrete DiginsightActivitiesOptions class
Filtering Specific Activities
// In Startup.cs or Program.cs
services.Configure<DiginsightActivitiesOptions>(options =>
{
// Default: hide all activities
options.LogBehavior = LogBehavior.Hide;
// Explicitly show important business operations
options.LoggedActivityNames["OrderService.CreateOrder"] = LogBehavior.Show;
options.LoggedActivityNames["PaymentService.ProcessPayment"] = LogBehavior.Show;
options.LoggedActivityNames["InventoryService.ReserveItems"] = LogBehavior.Show;
// Show high-level operation but hide internal details
options.LoggedActivityNames["ReportService.GenerateReport"] = LogBehavior.Truncate;
// Keep sensitive operations hidden
options.LoggedActivityNames["AuthenticationService.ValidatePassword"] = LogBehavior.Hide;
});Key behaviors: - Activities not in the dictionary inherit the global LogBehavior (Hide) - Explicit entries override the global setting - Use Truncate to show the operation without exposing internal implementation details - Use Hide for sensitive or high-frequency operations
Example Output:
// OrderService.CreateOrder (Show)
START OrderService.CreateOrder(orderId: "ORD-12345", items: [...])
START ValidationService.ValidateItems ← NOT logged (parent is Show, but child would be logged)
START InventoryService.ReserveItems ← Logged (explicit Show)
START PaymentService.ProcessPayment ← Logged (explicit Show)
START PaymentGateway.AuthorizePayment ← NOT logged if PaymentService is Truncate
OrderService.CreateOrder STOP (duration: 234ms)
// ReportService.GenerateReport (Truncate)
START ReportService.GenerateReport(reportType: "Sales")
START DataService.FetchData ← NOT logged (parent is Truncate)
START ReportBuilder.BuildReport ← NOT logged (parent is Truncate)
ReportService.GenerateReport STOP (duration: 1.2s)
Controlling Payload Rendering
// Scenario 1: Disable payloads globally for production security
services.Configure<DiginsightActivitiesOptions>(options =>
{
options.LogBehavior = LogBehavior.Show;
options.DisablePayloadRendering = true; // No parameters or results in logs
});
// Scenario 2: Selective payload rendering using class-aware config
services.ConfigureClassAware<DiginsightActivitiesOptions>(
"MyApp.PublicApi.*",
options =>
{
// Public API: show activities but hide payloads (may contain PII)
options.LogBehavior = LogBehavior.Show;
options.DisablePayloadRendering = true;
});
services.ConfigureClassAware<DiginsightActivitiesOptions>(
"MyApp.InternalServices.*",
options =>
{
// Internal services: show full details
options.LogBehavior = LogBehavior.Show;
options.DisablePayloadRendering = false;
});Important Notes: - Disabling payload rendering improves security by preventing PII exposure in logs - Reduces log volume significantly for activities with large parameters or results - Still provides timing information (duration) for performance analysis - Cannot be overridden per-activity (global or class-level only)
Using Custom Activity Logging Filter
Implement IActivityLoggingFilter for dynamic behavior decisions:
public class CustomActivityLoggingFilter : IActivityLoggingFilter
{
private readonly IHttpContextAccessor _httpContextAccessor;
public CustomActivityLoggingFilter(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public LogBehavior? GetLogBehavior(Activity activity)
{
// Dynamic decision based on HTTP header
var httpContext = _httpContextAccessor.HttpContext;
if (httpContext?.Request.Headers.TryGetValue("X-Enable-Verbose-Logging", out var value) == true
&& value == "true")
{
return LogBehavior.Show;
}
// Dynamic decision based on activity duration
if (activity.Duration.TotalMilliseconds > 1000)
{
// Always log slow operations
return LogBehavior.Show;
}
// Respect configuration for other cases
return null;
}
}
// Register the filter
services.AddSingleton<IActivityLoggingFilter, CustomActivityLoggingFilter>();🚀 Advanced Usage
Class-Level Configuration
Combine class-aware configuration with namespace patterns for granular control:
public static class ActivityLoggingConfiguration
{
public static IServiceCollection ConfigureActivityLogging(
this IServiceCollection services,
IConfiguration configuration)
{
// Global default
services.Configure<DiginsightActivitiesOptions>(options =>
{
options.LogBehavior = LogBehavior.Hide;
options.ActivityLogLevel = LogLevel.Information;
});
// Controllers: log API entry points
services.ConfigureClassAware<DiginsightActivitiesOptions>(
"*.Controllers.*Controller",
options =>
{
options.LogBehavior = LogBehavior.Show;
options.ActivityLogLevel = LogLevel.Information;
options.WriteActivityActionAsPrefix = true;
options.DisablePayloadRendering = true; // Protect PII in API logs
});
// Business services: detailed logging
services.ConfigureClassAware<DiginsightActivitiesOptions>(
"*.Services.*Service",
options =>
{
options.LogBehavior = LogBehavior.Show;
options.ActivityLogLevel = LogLevel.Debug;
options.DisablePayloadRendering = false;
});
// Repositories: show high-level, hide queries
services.ConfigureClassAware<DiginsightActivitiesOptions>(
"*.Repositories.*Repository",
options =>
{
options.LogBehavior = LogBehavior.Truncate;
options.ActivityLogLevel = LogLevel.Debug;
});
// Background workers: minimal logging
services.ConfigureClassAware<DiginsightActivitiesOptions>(
"*.Workers.*Worker",
options =>
{
options.LogBehavior = LogBehavior.Hide;
});
return services;
}
}Advanced Capabilities: - Layer-based configuration (controllers, services, repositories, workers) - Different log levels and behaviors per architectural layer - Selective payload rendering based on security requirements - Wildcard patterns for flexible namespace matching
Runtime Configuration Updates
public class DiagnosticsController : ControllerBase
{
private readonly IOptionsMonitor<DiginsightActivitiesOptions> _optionsMonitor;
private readonly ILogger<DiagnosticsController> _logger;
public DiagnosticsController(
IOptionsMonitor<DiginsightActivitiesOptions> optionsMonitor,
ILogger<DiagnosticsController> logger)
{
_optionsMonitor = optionsMonitor;
_logger = logger;
}
[HttpPost("enable-verbose/{activityName}")]
public IActionResult EnableVerboseLogging(string activityName)
{
try
{
var options = _optionsMonitor.CurrentValue;
// Runtime update - persists until next configuration reload
options.LoggedActivityNames[activityName] = LogBehavior.Show;
_logger.LogInformation(
"Enabled verbose logging for activity: {ActivityName}",
activityName);
return Ok(new {
message = $"Verbose logging enabled for {activityName}",
currentBehavior = LogBehavior.Show
});
}
catch (InvalidOperationException ex)
{
// Options instance might be frozen
_logger.LogError(ex, "Failed to update activity logging configuration");
return StatusCode(500, "Configuration update failed");
}
}
[HttpPost("disable-payloads")]
public IActionResult DisablePayloadRendering()
{
var options = _optionsMonitor.CurrentValue;
options.DisablePayloadRendering = true;
_logger.LogWarning("Payload rendering disabled globally");
return Ok("Payload rendering disabled");
}
[HttpGet("current-config")]
public IActionResult GetCurrentConfiguration()
{
var options = (IDiginsightActivitiesLogOptions)_optionsMonitor.CurrentValue;
return Ok(new
{
logBehavior = options.LogBehavior.ToString(),
logLevel = options.LogLevel.ToString(),
writeActivityActionAsPrefix = options.WriteActivityActionAsPrefix,
disablePayloadRendering = options.DisablePayloadRendering,
activityOverrides = options.ActivityNames.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value.ToString())
});
}
}Hierarchical Log Behavior
Understanding and leveraging behavior inheritance:
// Configure parent activity to truncate
services.Configure<DiginsightActivitiesOptions>(options =>
{
options.LogBehavior = LogBehavior.Show; // Global default
// Parent operation: show but don't show internals
options.LoggedActivityNames["ComplexOperation"] = LogBehavior.Truncate;
// Child operations: these settings are IGNORED if parent is Truncate
options.LoggedActivityNames["ComplexOperation.Step1"] = LogBehavior.Show;
options.LoggedActivityNames["ComplexOperation.Step2"] = LogBehavior.Show;
});
// Example activities
public class ComplexService
{
[Activity("ComplexOperation")] // Will be logged (Truncate)
public async Task ComplexOperation()
{
await Step1(); // Will NOT be logged (parent is Truncate)
await Step2(); // Will NOT be logged (parent is Truncate)
}
[Activity("ComplexOperation.Step1")]
private async Task Step1()
{
// Internal implementation
}
[Activity("ComplexOperation.Step2")]
private async Task Step2()
{
// Internal implementation
}
}Pattern Options:
| Parent Behavior | Child Behavior | Effective Child Behavior | Use Case |
|---|---|---|---|
| Show | Show | Show | Full visibility of operation hierarchy |
| Show | Hide | Hide | Selectively hide specific children |
| Show | Truncate | Truncate | Hide grandchildren but show direct children |
| Truncate | Show | Truncate (inherited) | Hide all descendants regardless of settings |
| Truncate | Hide | Truncate (inherited) | Same as above |
| Hide | Show | Hide (not logged) | Entire subtree is hidden |
🔧 Troubleshooting
Common Issues
1. Activities Not Being Logged
Activities may be hidden due to multiple configuration layers.
// Problem: Activity not appearing in logs
// Possible causes:
// 1. Global LogBehavior is Hide
// 2. Parent activity is Truncate or Hide
// 3. Logger minimum level too high
// 4. Activity name doesn't match configuration
// Solution: Enable diagnostic logging
services.Configure<DiginsightActivitiesOptions>(options =>
{
options.LogBehavior = LogBehavior.Show; // Ensure default is Show
options.ActivityLogLevel = LogLevel.Debug;
// Explicitly enable specific activity
options.LoggedActivityNames["MyService.MyMethod"] = LogBehavior.Show;
});
// Ensure logger level is appropriate
services.AddLogging(builder =>
{
builder.SetMinimumLevel(LogLevel.Debug);
builder.AddFilter("Diginsight.Diagnostics", LogLevel.Trace); // Verbose Diginsight logs
});2. Activity Names Not Matching
Activity name in configuration must match Activity.OperationName exactly.
Ensure that: - Activity name is case-sensitive exact match - No extra spaces or special characters - Activity source is registered and started - Activity.Current is not null
Debugging:
public class MyService
{
public void MyMethod()
{
var activity = Activity.Current;
if (activity != null)
{
_logger.LogInformation("Current activity name: '{Name}'", activity.OperationName);
// Check this name matches configuration exactly
}
else
{
_logger.LogWarning("No current activity - check ActivitySource registration");
}
}
}3. Payload Still Being Rendered Despite DisablePayloadRendering = true
If payloads are still appearing in logs:
Check that configuration is loaded correctly:
Verify no class-aware override:
Check for frozen options - updates to frozen instances throw exceptions
4. Configuration Changes Not Taking Effect
Configuration updates may not reflect immediately.
- Symptom: Updated
appsettings.jsonbut behavior unchanged - Cause: Options snapshot cached or instance frozen
- Solution: Use
IOptionsMonitor<T>and accessCurrentValuefor live updates
// ✓ Recommended: IOptionsMonitor for dynamic updates
public class MyService
{
private readonly IOptionsMonitor<DiginsightActivitiesOptions> _optionsMonitor;
public MyService(IOptionsMonitor<DiginsightActivitiesOptions> optionsMonitor)
{
_optionsMonitor = optionsMonitor;
}
public void DoWork()
{
// Always gets current configuration
var options = _optionsMonitor.CurrentValue;
}
}
// ✗ Avoid: IOptions<T> caches at construction time
public class MyService
{
private readonly DiginsightActivitiesOptions _options;
public MyService(IOptions<DiginsightActivitiesOptions> options)
{
_options = options.Value; // Cached - won't see updates
}
}Debugging
Enable detailed logging to troubleshoot activity logging issues:
// In appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Diginsight.Diagnostics": "Trace", // Verbose Diginsight internal logs
"Diginsight.Diagnostics.ActivityLifecycleLogEmitter": "Trace" // Activity logging decisions
}
}
}
// Or in code
services.Configure<LoggerFilterOptions>(options =>
{
options.AddFilter("Diginsight.Diagnostics", LogLevel.Trace);
});Debugging Techniques: - Enable Trace level logging for Diginsight.Diagnostics namespace - Log current Activity.Current.OperationName to verify activity names - Check IOptionsMonitor<DiginsightActivitiesOptions>.CurrentValue to inspect configuration - Verify ActivitySource is registered and activities are started - Use IActivityLoggingFilter with breakpoints to debug behavior decisions
Telemetry and Monitoring:
// Create diagnostic middleware to monitor configuration
public class ActivityLoggingDiagnosticMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ActivityLoggingDiagnosticMiddleware> _logger;
public ActivityLoggingDiagnosticMiddleware(
RequestDelegate next,
ILogger<ActivityLoggingDiagnosticMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(
HttpContext context,
IOptionsMonitor<DiginsightActivitiesOptions> optionsMonitor)
{
if (context.Request.Path.StartsWithSegments("/_diagnostics/activity-config"))
{
var options = (IDiginsightActivitiesLogOptions)optionsMonitor.CurrentValue;
context.Response.ContentType = "application/json";
await context.Response.WriteAsJsonAsync(new
{
logBehavior = options.LogBehavior.ToString(),
logLevel = options.LogLevel.ToString(),
writeActivityActionAsPrefix = options.WriteActivityActionAsPrefix,
disablePayloadRendering = options.DisablePayloadRendering,
activityOverrides = options.ActivityNames
});
return;
}
await _next(context);
}
}
// Register middleware
app.UseMiddleware<ActivityLoggingDiagnosticMiddleware>();
// Access: https://yourapp/_diagnostics/activity-configPerformance Considerations
Activity Logging Overhead: - Activity creation: ~10-50 microseconds per activity - Log output: depends on logger backend and payload size - Payload rendering: can add significant overhead for large objects
Optimization Strategies:
// Strategy 1: Use Hide for high-frequency operations
services.Configure<DiginsightActivitiesOptions>(options =>
{
options.LoggedActivityNames["PollingService.Poll"] = LogBehavior.Hide;
options.LoggedActivityNames["CacheCheck"] = LogBehavior.Hide;
options.LoggedActivityNames["HealthCheck"] = LogBehavior.Hide;
});
// Strategy 2: Disable payload rendering in production
services.Configure<DiginsightActivitiesOptions>(options =>
{
options.DisablePayloadRendering = true; // Significant performance improvement
});
// Strategy 3: Use Truncate for deep call stacks
services.Configure<DiginsightActivitiesOptions>(options =>
{
options.LoggedActivityNames["HttpClientCall"] = LogBehavior.Truncate;
// Prevents logging of all HTTP pipeline activities
});
// Strategy 4: Increase log level to reduce noise
services.Configure<DiginsightActivitiesOptions>(options =>
{
options.ActivityLogLevel = LogLevel.Information; // From Debug
// Only logs if logger allows Information level
});Monitoring Recommendations: - Monitor log volume (MB/hour) to detect excessive logging - Track activity creation rate (activities/second) - Measure P95/P99 latency with and without activity logging - Alert on sudden increases in log volume - Use sampling for high-frequency operations
📚 Reference
Interface Definition
Namespace: Diginsight.Diagnostics
Assembly: Diginsight.Diagnostics.dll
Properties
| Property | Type | Description | Default |
|---|---|---|---|
ActivityNames |
IReadOnlyDictionary<string, LogBehavior> |
Dictionary mapping activity names to specific log behaviors. Enables per-activity override of global LogBehavior. |
Empty dictionary |
LogBehavior |
LogBehavior |
Global default log behavior for activities not specified in ActivityNames dictionary. |
LogBehavior.Hide |
LogLevel |
LogLevel |
The severity level at which activities are logged (Trace, Debug, Information, Warning, Error, Critical). | LogLevel.Debug |
WriteActivityActionAsPrefix |
bool |
When true, formats activity logs as "START Activity" and "STOP Activity". When false, formats as "Activity START" and "Activity STOP". |
false |
DisablePayloadRendering |
bool |
When true, suppresses rendering of activity parameters and return values in log output. When false, includes full payload details. |
false |
Implementing Classes
DiginsightActivitiesOptions: Primary concrete implementation- Implements
IDiginsightActivitiesLogOptions - Also implements
IDiginsightActivitiesOptions,IMetricRecordingOptions - Supports
IDynamicallyConfigurableandIVolatilelyConfigurable - Mutable properties with frozen state support
- Located in
Diginsight.Diagnosticsnamespace
- Implements
Consumption Points: - ActivityLifecycleLogEmitter: Consumes this interface to determine logging behavior during activity lifecycle events (start/stop) - OptionsBasedActivityLoggingFilter: Uses this interface to implement IActivityLoggingFilter - Class-Aware Options System: Retrieved via IClassAwareOptions<DiginsightActivitiesOptions>.Get(Type callerType)
💡 Best Practices
Configuration Organization
Organize by Architectural Layer:
public static class ActivityLoggingSetup
{
public static IServiceCollection AddLayeredActivityLogging(
this IServiceCollection services)
{
// Layer 1: Presentation (hide payloads for security)
services.ConfigureClassAware<DiginsightActivitiesOptions>(
"*.Controllers.*",
options =>
{
options.LogBehavior = LogBehavior.Show;
options.ActivityLogLevel = LogLevel.Information;
options.DisablePayloadRendering = true; // PII protection
});
// Layer 2: Application/Services (full details)
services.ConfigureClassAware<DiginsightActivitiesOptions>(
"*.Services.*",
options =>
{
options.LogBehavior = LogBehavior.Show;
options.ActivityLogLevel = LogLevel.Debug;
options.DisablePayloadRendering = false;
});
// Layer 3: Infrastructure (truncate to hide low-level details)
services.ConfigureClassAware<DiginsightActivitiesOptions>(
"*.Repositories.*",
options =>
{
options.LogBehavior = LogBehavior.Truncate;
});
return services;
}
}Guidelines: - Use environment-specific configuration (appsettings.Production.json) - Apply least-privilege logging (hide by default, show explicitly) - Document activity naming conventions in team standards - Version control configuration files with code - Review logging configuration during security audits
Performance Optimization
Minimize Logging Overhead:
// ✓ Recommended: Hide high-frequency operations
services.Configure<DiginsightActivitiesOptions>(options =>
{
options.LoggedActivityNames["PollingWorker.Poll"] = LogBehavior.Hide;
options.LoggedActivityNames["CacheService.Get"] = LogBehavior.Hide;
options.LoggedActivityNames["MetricsCollector.Collect"] = LogBehavior.Hide;
});
// ✓ Recommended: Disable payloads when not needed
services.Configure<DiginsightActivitiesOptions>(options =>
{
options.DisablePayloadRendering = true; // Production default
});
// ✓ Recommended: Use Truncate for deep hierarchies
services.Configure<DiginsightActivitiesOptions>(options =>
{
options.LoggedActivityNames["ExternalApiClient.Call"] = LogBehavior.Truncate;
// Prevents logging of entire HTTP pipeline
});
// ✗ Avoid: Logging everything at Trace level
services.Configure<DiginsightActivitiesOptions>(options =>
{
options.LogBehavior = LogBehavior.Show;
options.ActivityLogLevel = LogLevel.Trace;
options.DisablePayloadRendering = false;
// Massive log volume and performance impact
});When to Use Each Behavior: - Show: Business operations, API endpoints, critical paths - Hide: High-frequency operations, background tasks, health checks - Truncate: External service calls, database operations, deep hierarchies
Key Recommendations: 1. Start with LogBehavior.Hide as default, enable explicitly 2. Always disable payload rendering in production for APIs 3. Use Truncate for operations with deep call stacks 4. Monitor log volume and adjust configuration accordingly 5. Profile performance impact before deploying verbose logging
Security Considerations
Protect Sensitive Data:
// Security pattern: Environment-based configuration
public static IServiceCollection ConfigureSecureActivityLogging(
this IServiceCollection services,
IWebHostEnvironment environment)
{
services.Configure<DiginsightActivitiesOptions>(options =>
{
if (environment.IsProduction())
{
// Production: hide payloads to prevent PII exposure
options.DisablePayloadRendering = true;
// Hide authentication/authorization activities
options.LoggedActivityNames["AuthenticationService.Authenticate"] = LogBehavior.Hide;
options.LoggedActivityNames["AuthorizationService.Authorize"] = LogBehavior.Hide;
options.LoggedActivityNames["TokenService.GenerateToken"] = LogBehavior.Hide;
}
else
{
// Development: full details for debugging
options.DisablePayloadRendering = false;
options.LogBehavior = LogBehavior.Show;
}
});
return services;
}
// Custom filter for dynamic PII protection
public class PiiProtectionFilter : IActivityLoggingFilter
{
public LogBehavior? GetLogBehavior(Activity activity)
{
// Hide activities that may contain PII
if (activity.OperationName.Contains("Password", StringComparison.OrdinalIgnoreCase) ||
activity.OperationName.Contains("CreditCard", StringComparison.OrdinalIgnoreCase) ||
activity.OperationName.Contains("SSN", StringComparison.OrdinalIgnoreCase))
{
return LogBehavior.Hide;
}
return null; // Respect configuration
}
}When to Hide Activities: - Authentication and authorization operations - Payment processing - PII handling (email, phone, address) - Cryptographic operations - Token generation and validation
When NOT to Show Payloads: - User credentials (passwords, tokens) - Financial data (credit cards, account numbers) - Personal identifiable information - API keys and secrets - Health/medical data
📖 Appendices
Appendix A: LogBehavior Decision Flow
The ActivityLifecycleLogEmitter determines logging behavior using the following algorithm:
flowchart TD
Start([Activity Started]) --> GetCallerType[Get Caller Type from Activity]
GetCallerType --> GetOptions[Get IDiginsightActivitiesLogOptions<br/>for Caller Type]
GetOptions --> CheckFilter{IActivityLoggingFilter<br/>available?}
CheckFilter -->|Yes| CallFilter[Call Filter.GetLogBehavior]
CallFilter --> FilterReturns{Filter returns<br/>value?}
FilterReturns -->|Yes| UseFilterBehavior[Use Filter Behavior]
FilterReturns -->|No| CheckActivityNames
CheckFilter -->|No| CheckActivityNames
CheckActivityNames{Activity name in<br/>ActivityNames?}
CheckActivityNames -->|Yes| UseActivityBehavior[Use ActivityNames[name]]
CheckActivityNames -->|No| UseDefaultBehavior[Use LogBehavior property]
UseFilterBehavior --> CheckParent
UseActivityBehavior --> CheckParent
UseDefaultBehavior --> CheckParent
CheckParent{Parent activity<br/>is Truncate?}
CheckParent -->|Yes| ForceTruncate[Force Behavior = Truncate]
CheckParent -->|No| KeepBehavior[Keep Computed Behavior]
ForceTruncate --> SetBehavior[Set Activity.LogBehavior]
KeepBehavior --> SetBehavior
SetBehavior --> CheckShow{Behavior == Show?}
CheckShow -->|Yes| LogActivity[Log Activity Start/Stop]
CheckShow -->|No| SkipActivity[Skip Logging]
LogActivity --> End([Activity Logged])
SkipActivity --> EndKey Decision Points: 1. Caller Type Extraction: Activity’s CallerType custom property determines which class-aware configuration to use 2. Filter Priority: IActivityLoggingFilter has highest priority if it returns a non-null value 3. Activity-Specific Override: ActivityNames dictionary overrides global LogBehavior 4. Parent Truncate Inheritance: If parent is Truncate, child inherits Truncate regardless of other settings 5. Final Behavior Check: Only Show results in logging; Hide and Truncate skip logging
Appendix B: Integration with ActivityLifecycleLogEmitter
The ActivityLifecycleLogEmitter consumes IDiginsightActivitiesLogOptions at two key points:
1. Activity Start Event:
// Simplified pseudo-code from ActivityLifecycleLogEmitter.cs
void ActivityStarted(Activity activity)
{
// Step 1: Get options for caller type
Type callerType = activity.GetCallerType();
IDiginsightActivitiesLogOptions options = activitiesOptionsMonitor.Get(callerType);
// Step 2: Determine log behavior
LogBehavior behavior = activityLoggingFilter?.GetLogBehavior(activity)
?? options.ActivityNames.GetValueOrDefault(activity.OperationName, options.LogBehavior);
// Step 3: Check parent truncate inheritance
if (activity.Parent?.GetLogBehavior() == LogBehavior.Truncate)
behavior = LogBehavior.Truncate;
// Step 4: Store behavior on activity
activity.SetLogBehavior(behavior);
// Step 5: Log if behavior is Show
if (behavior == LogBehavior.Show)
{
LogLevel logLevel = options.LogLevel;
bool writeAsPrefix = options.WriteActivityActionAsPrefix;
bool disablePayload = options.DisablePayloadRendering;
string format = writeAsPrefix ? "START {ActivityName}" : "{ActivityName} START";
if (!disablePayload)
{
// Render parameters
LogActivityStartWithPayload(activity, format, logLevel);
}
else
{
// Log without parameters
LogActivityStart(activity, format, logLevel);
}
}
}2. Activity Stop Event:
// Simplified pseudo-code from ActivityLifecycleLogEmitter.cs
void ActivityStopped(Activity activity)
{
// Step 1: Retrieve stored log behavior
LogBehavior behavior = activity.GetLogBehavior();
if (behavior != LogBehavior.Show)
return; // Skip logging
// Step 2: Get options again (may have changed)
Type callerType = activity.GetCallerType();
IDiginsightActivitiesLogOptions options = activitiesOptionsMonitor.Get(callerType);
// Step 3: Log activity stop
LogLevel logLevel = options.LogLevel;
bool writeAsPrefix = options.WriteActivityActionAsPrefix;
bool disablePayload = options.DisablePayloadRendering;
string format = writeAsPrefix ? "STOP {ActivityName} (duration: {Duration}ms)"
: "{ActivityName} STOP (duration: {Duration}ms)";
if (!disablePayload && activity.GetCustomProperty("Result") is object result)
{
// Render return value
LogActivityStopWithResult(activity, format, logLevel, result);
}
else
{
// Log without result
LogActivityStop(activity, format, logLevel, activity.Duration.TotalMilliseconds);
}
}Integration Points: - activitiesOptionsMonitor: IClassAwareOptions<DiginsightActivitiesOptions> injected into ActivityLifecycleLogEmitter - activitiesOptionsMonitor.Get(Type): Retrieves class-aware configuration for specific caller type - activityLoggingFilter: Optional IActivityLoggingFilter for dynamic behavior decisions - Activity custom properties: Stores LogBehavior between start and stop events
Configuration Flow:
appsettings.json
↓
DiginsightActivitiesOptions (IOptions)
↓
IClassAwareOptions<DiginsightActivitiesOptions>
↓
ActivityLifecycleLogEmitter.activitiesOptionsMonitor.Get(callerType)
↓
IDiginsightActivitiesLogOptions (interface cast)
↓
Extract properties (LogBehavior, LogLevel, etc.)
↓
Make logging decision
This integration ensures that all activity logging decisions respect the configured options while supporting dynamic updates and class-aware granularity.
End of Reference Documentation