| name | Logging |
| description | UTF-8 file logging with automatic date-based filenames and thread-safe operations for RocsMiddleware services |
Logging
Instructions
When helping users implement logging in RocsMiddleware services, follow these guidelines:
Command-Line Option: Always include
--log-dirparameter for specifying log directory pathUTF-8 Encoding: All log files must use UTF-8 encoding without BOM
Automatic Filename Pattern: Use
{ProgramName}_YYYY_MM_DD.logformat with automatic date rotationDirectory Path Handling: Smart detection - if path has no extension or ends with separator, treat as directory and use directory name as base filename
Error Handling: Include console output before logger initialization since logger may fail. Always check if
_loggeris null in exception handlers.Log Levels: Use
LogLevel.Debugwith--debugflag, default toLogLevel.InformationThread Safety: The UTF8Writer uses locking for concurrent writes
Utf8LoggingExtensions.cs: Copy the reference implementation to project's
Services/directory - provides UTF8Writer, UTF8LoggerProvider, UTF8Logger, and helper methods
Examples
Example 1: Basic Logger Setup
User: Help me add logging to my RocsMiddleware service
Claude: I'll set up the standard UTF-8 file logging pattern:
- Add --log-dir command-line option
- Copy Utf8LoggingExtensions.cs to Services/
- Initialize logger with automatic date-based filename
- Add console output before logger initialization for debugging
[Provides implementation using the Logger Initialization pattern from reference material below]
Example 2: Directory Path Handling
User: Why isn't my log file being created when I specify --log-dir?
Claude: Let me check the directory path handling. The logger should:
- Detect if path is a directory (no extension or ends with separator)
- Use directory name as base filename
- Add date string and .log extension automatically
Common issue: Old Utf8LoggingExtensions.cs without directory detection logic.
Solution: Copy the updated version from skills folder.
[Provides directory detection implementation from reference material below]
Example 3: Error Handling Before Logger Init
User: My program just prints "Error" with no details
Claude: This happens when an exception occurs before logger initialization. I'll add:
- Console.WriteLine at key points (startup, before logger init, after logger init)
- Null check on _logger in exception handler
- Fallback to console output if logger is null
[Provides error handling pattern from reference material below]
Reference Implementation Details
The sections below contain proven working code from RocsMiddleware services that the examples above reference.
Reference Files in This Folder:
Utf8LoggingExtensions.cs- Complete UTF-8 logger implementation (copy to Services/)
Standard Logging Pattern
All RocsMiddleware services use a consistent logging approach:
- Command-line option:
--log-dirparameter specifies the directory path - Automatic filename generation:
{ProgramName}_YYYY_MM_DD.log - UTF-8 encoding: All log files use UTF-8 encoding
- Date rotation: Log files automatically rotate daily based on the date
Implementation
Command-Line Option
[Option("log-dir", Required = false, HelpText = "Directory for log files")]
public string? LogDir { get; set; }
Logger Initialization
// Initialize logger with optional log directory
var logLevel = options.Debug ? LogLevel.Debug : LogLevel.Information;
_logger = {Namespace}.Services.Utf8LoggingExtensions.CreateUtf8Logger(
"{ProgramName}",
logLevel,
options.LogDir);
Example Usage
dotnet run -- --log-dir "C:\RI Services\Logs\MyProgram" --other-options
This creates: C:\RI Services\Logs\MyProgram\MyProgram_2025_10_16.log
Directory Path Handling
The UTF8Writer.Init() method automatically detects directory paths and creates properly named log files:
- If path has no extension and ends with a path separator (or exists as a directory), it's treated as a directory
- The directory name becomes the base name for the log file
- Default
.logextension is added automatically - Date string in format
YYYY_MM_DDis inserted before the extension
Examples
Input --log-dir |
Output Log File |
|---|---|
C:\Logs\MyProgram |
C:\Logs\MyProgram\MyProgram_2025_10_16.log |
C:\Logs\MyProgram\ |
C:\Logs\MyProgram\MyProgram_2025_10_16.log |
C:\Logs\MyProgram\custom.log |
C:\Logs\MyProgram\custom_2025_10_16.log |
C:\Logs\MyProgram\custom |
C:\Logs\MyProgram\custom_2025_10_16.log (adds .log extension) |
UTF8LoggingExtensions.cs
All services include a Services/Utf8LoggingExtensions.cs file that provides:
UTF8Writerclass: Low-level UTF-8 file writer with thread-safe lockingUTF8LoggerProvider: Custom logger provider for Microsoft.Extensions.LoggingUTF8Logger: ILogger implementation that writes to UTF-8 filesCreateUtf8Logger(): Helper method to create a configured logger instanceConfigureUtf8Logging(): Extension method for ILoggingBuilder
Error Handling
Before Logger Initialization
Since the logger may fail to initialize, always include console output for early errors:
public static async Task Main(string[] args)
{
try
{
Console.WriteLine("{ProgramName} starting...");
// Parse command line
CommandLineOptions? options = null;
Parser.Default.ParseArguments<CommandLineOptions>(args)
.WithParsed(opts => options = opts)
.WithNotParsed(errors =>
{
Console.WriteLine("Failed to parse command line arguments");
Environment.Exit(1);
});
if (options == null)
{
Console.WriteLine("Failed to parse command line options");
Environment.Exit(1);
return;
}
Console.WriteLine($"Initializing logger with log-dir: {options.LogDir ?? "(null)"}");
// Initialize logger
_logger = {Namespace}.Services.Utf8LoggingExtensions.CreateUtf8Logger(
"{ProgramName}",
options.Debug ? LogLevel.Debug : LogLevel.Information,
options.LogDir);
Console.WriteLine("Logger initialized");
_logger.LogInformation("{ProgramName} starting");
// ... rest of program
}
catch (Exception ex)
{
if (_logger != null)
{
_logger.LogError(ex, "{ProgramName} failed: {Message}", ex.Message);
}
else
{
Console.WriteLine($"{ProgramName} failed: {ex.Message}");
Console.WriteLine(ex.StackTrace);
}
Environment.Exit(1);
}
}
Log Levels
Standard log levels used across all services:
LogLevel.Debug: Verbose diagnostic information (use--debugflag)LogLevel.Information: General informational messages (default)LogLevel.Warning: Warning messages for non-critical issuesLogLevel.Error: Error messages for failures
Real-World Examples
PriceExtractor
dotnet run -- --pqdir "R:\Outputs\Parquets\poller" --pg "R:\JsonParams\x3rocs_db.json" --log-dir "C:\RI Services\Logs\PriceExtractor" --full
Creates: C:\RI Services\Logs\PriceExtractor\PriceExtractor_2025_10_16.log
PriceDiscountUploader
dotnet run -- --pg "R:/JsonParams/x3rocs_db.json" --log-dir "R:/Logs/PriceDiscounter" --elastic https://rocs-stage-es.ramsden-international.com/ --debug --sphkey1 425073
Creates: R:/Logs/PriceDiscounter/PriceDiscounter_2025_10_16.log
Benefits
- Consistent naming: All services follow the same
{ProgramName}_YYYY_MM_DD.logpattern - Automatic rotation: Daily log files without manual intervention
- UTF-8 encoding: Proper support for international characters
- Directory-aware: Smart handling of directory vs. file paths
- Optional logging: Works without
--log-dir(console only) - Thread-safe: Multiple threads can log simultaneously
Common Issues
No Log File Created
Symptom: Program runs but no log file appears in the specified directory.
Causes:
- Old version of
Utf8LoggingExtensions.cswithout directory path handling - Logger initialization failing silently
Solution:
- Copy
Utf8LoggingExtensions.csfrom this skills folder to your project'sServices/directory - Add console debug output before logger initialization (see Error Handling section above)
- Check that the
UTF8Writer.Init()method includes directory detection logic
Program Exits with "Error" and No Details
Symptom: Program outputs just "Error" with no stack trace or details.
Causes:
- Exception thrown before logger is initialized
- Exception handler trying to use null logger
Solution:
- Add console output at key points (see Error Handling section)
- Wrap exception handler to check if
_loggeris null before using it - Check log file was actually created and contains error details
Related Files
Utf8LoggingExtensions.cs: Reference implementation included in this folder - copy to your project'sServices/directory- Command-line parsing: Uses
CommandLineParserlibrary - All services in RocsMiddleware follow this pattern
Notes
- Log files are created on-demand when the first log message is written
- If
--log-diris not specified, logging goes to console only (if enabled) - Parent directories are created automatically if they don't exist
- Log files are UTF-8 encoded without BOM
- The logger uses Microsoft.Extensions.Logging interfaces for compatibility