- Fix pull consumer fetch: send original stream subject in HMSG (not inbox) so NATS client distinguishes data messages from control messages - Fix MaxAge expiry: add background timer in StreamManager for periodic pruning - Fix JetStream wire format: Go-compatible anonymous objects with string enums, proper offset-based pagination for stream/consumer list APIs - Add 42 E2E black-box tests (core messaging, auth, TLS, accounts, JetStream) - Add ~1000 parity tests across all subsystems (gaps closure) - Update gap inventory docs to reflect implementation status
218 lines
16 KiB
Markdown
218 lines
16 KiB
Markdown
# Logging — Gap Analysis
|
|
|
|
> This file tracks what has and hasn't been ported from Go to .NET for the **Logging** module.
|
|
> See [stillmissing.md](stillmissing.md) for the full LOC comparison across all modules.
|
|
|
|
## LLM Instructions: How to Analyze This Category
|
|
|
|
### Step 1: Read the Go Reference Files
|
|
|
|
Read each Go source file listed below. For every file:
|
|
|
|
1. Extract all **exported types** (structs, interfaces, type aliases)
|
|
2. Extract all **exported methods** on those types (receiver functions)
|
|
3. Extract all **exported standalone functions**
|
|
4. Note **key constants, enums, and protocol states**
|
|
5. Note **important unexported helpers** that implement core logic (functions >20 lines)
|
|
6. Pay attention to **concurrency patterns** (goroutines, mutexes, channels) — these map to different .NET patterns
|
|
|
|
### Step 2: Read the .NET Implementation Files
|
|
|
|
Read all `.cs` files in the .NET directories listed below. For each Go symbol found in Step 1:
|
|
|
|
1. Search for a matching type, method, or function in .NET
|
|
2. If found, compare the behavior: does it handle the same edge cases? Same error paths?
|
|
3. If partially implemented, note what's missing
|
|
4. If not found, note it as MISSING
|
|
|
|
### Step 3: Cross-Reference Tests
|
|
|
|
Compare Go test functions against .NET test methods:
|
|
|
|
1. For each Go `Test*` function, check if a corresponding .NET `[Fact]` or `[Theory]` exists
|
|
2. Note which test scenarios are covered and which are missing
|
|
3. Check the parity DB (`docs/test_parity.db`) for existing mappings:
|
|
```bash
|
|
sqlite3 docs/test_parity.db "SELECT go_test, dotnet_test, confidence FROM test_mappings tm JOIN go_tests gt ON tm.go_test_id=gt.rowid JOIN dotnet_tests dt ON tm.dotnet_test_id=dt.rowid WHERE gt.go_file LIKE '%PATTERN%'"
|
|
```
|
|
|
|
### Step 4: Classify Each Item
|
|
|
|
Use these status values:
|
|
|
|
| Status | Meaning |
|
|
|--------|---------|
|
|
| **PORTED** | Equivalent exists in .NET with matching behavior |
|
|
| **PARTIAL** | .NET implementation exists but is incomplete (missing edge cases, error handling, or features) |
|
|
| **MISSING** | No .NET equivalent found — needs to be ported |
|
|
| **NOT_APPLICABLE** | Go-specific pattern that doesn't apply to .NET (build tags, platform-specific goroutine tricks, etc.) |
|
|
| **DEFERRED** | Intentionally skipped for now (document why) |
|
|
|
|
### Step 5: Fill In the Gap Inventory
|
|
|
|
Add rows to the Gap Inventory table below. Group by Go source file. Include the Go file and line number so a porting LLM can jump directly to the reference implementation.
|
|
|
|
### Key Porting Notes for Logging
|
|
|
|
- .NET has **0 LOC** for logging because it uses `Microsoft.Extensions.Logging` (`ILogger<T>`) with Serilog as the provider.
|
|
- This is an intentional architectural difference, not a gap.
|
|
- Analysis should verify:
|
|
1. All log levels from Go (`Debug`, `Trace`, `Notice`, `Warn`, `Error`, `Fatal`) are mapped to .NET equivalents
|
|
2. Log output format includes the same fields (timestamps, client IDs, subjects)
|
|
3. Syslog output capability exists if needed (Serilog has syslog sinks)
|
|
- Most items in this category will be **NOT_APPLICABLE** or **PORTED** (via framework).
|
|
|
|
---
|
|
|
|
## Go Reference Files (Source)
|
|
|
|
- `golang/nats-server/server/log.go` — Server logging facade (~287 lines)
|
|
- `golang/nats-server/logger/log.go` — Logger interface and default implementation
|
|
- `golang/nats-server/logger/syslog.go` — Syslog backend
|
|
- `golang/nats-server/logger/syslog_windows.go` — Windows-specific syslog
|
|
|
|
## Go Reference Files (Tests)
|
|
|
|
- `golang/nats-server/server/log_test.go`
|
|
- `golang/nats-server/logger/log_test.go`
|
|
- `golang/nats-server/logger/syslog_test.go`
|
|
- `golang/nats-server/logger/syslog_windows_test.go`
|
|
|
|
## .NET Implementation Files (Source)
|
|
|
|
- (none — .NET uses `Microsoft.Extensions.Logging` + `Serilog` NuGet packages)
|
|
|
|
## .NET Implementation Files (Tests)
|
|
|
|
- (none — logging tests are integrated into other test areas)
|
|
|
|
---
|
|
|
|
## Gap Inventory
|
|
|
|
<!-- After analysis, fill in this table. Group rows by Go source file. -->
|
|
|
|
### golang/nats-server/server/log.go
|
|
|
|
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|
|
|-----------|:-------------|--------|:----------------|-------|
|
|
| `Logger` interface | log.go:27-46 | PORTED | ILogger via Microsoft.Extensions.Logging | .NET uses standard Microsoft.Extensions.Logging framework instead of custom Logger interface |
|
|
| `Logger.Noticef()` | log.go:30 | PORTED | ILogger.LogInformation() | Maps to LogInformation (notice level) |
|
|
| `Logger.Warnf()` | log.go:33 | PORTED | ILogger.LogWarning() | Maps to LogWarning level |
|
|
| `Logger.Fatalf()` | log.go:36 | PORTED | ILogger.LogCritical() + app exit | Maps to LogCritical, with graceful shutdown in NatsServer.cs |
|
|
| `Logger.Errorf()` | log.go:39 | PORTED | ILogger.LogError() | Maps to LogError level |
|
|
| `Logger.Debugf()` | log.go:42 | PORTED | ILogger.LogDebug() | Maps to LogDebug level |
|
|
| `Logger.Tracef()` | log.go:45 | PORTED | ILogger.LogTrace() (Verbose in Serilog) | Maps to Verbose/Trace level |
|
|
| `Server.ConfigureLogger()` | log.go:49-101 | PORTED | Program.cs LoggerConfiguration setup | .NET uses Serilog configuration in Program.cs instead of per-server method |
|
|
| `Server.Logger()` | log.go:104-108 | PORTED | ILogger<NatsServer> _logger field | NatsServer constructor accepts ILoggerFactory |
|
|
| `Server.SetLogger()` | log.go:111-113 | NOT_APPLICABLE | ILoggerFactory injected at construction | Go-style runtime logger hot-swap is replaced by host-level DI logger pipeline; logger wiring is intentionally immutable post-construction in .NET |
|
|
| `Server.SetLoggerV2()` | log.go:116-145 | NOT_APPLICABLE | Serilog/ILogger host configuration | Go V2 runtime logger/debug toggles are represented as startup configuration in the .NET host pipeline rather than mutable server methods |
|
|
| `Server.ReOpenLogFile()` | log.go:150-178 | PORTED | Program.cs server.ReOpenLogFile callback | Handler delegate set in Program.cs to close and recreate Serilog logger |
|
|
| `Server.Noticef()` | log.go:181-185 | PORTED | _logger.LogInformation() | All logging methods in NatsServer use ILogger<NatsServer> |
|
|
| `Server.Errorf()` | log.go:188-192 | PORTED | _logger.LogError() | Direct logging to ILogger |
|
|
| `Server.Errors()` | log.go:195-199 | PORTED | _logger.LogError() + structured args | Error logging with scope context |
|
|
| `Server.Errorc()` | log.go:202-206 | PORTED | _logger.LogError() + structured args | Error logging with context |
|
|
| `Server.Errorsc()` | log.go:209-213 | PORTED | _logger.LogError() + structured args | Error logging with scope and context |
|
|
| `Server.Warnf()` | log.go:216-220 | PORTED | _logger.LogWarning() | Direct logging to ILogger |
|
|
| `Server.rateLimitFormatWarnf()` | log.go:222-228 | NOT_APPLICABLE | Rate limiting via sync.Map, not implemented in .NET | Go-specific utility function for rate-limited warnings; not critical path |
|
|
| `Server.RateLimitWarnf()` | log.go:230-236 | NOT_APPLICABLE | Rate limiting via sync.Map, not implemented in .NET | Go-specific utility function; can be added if needed |
|
|
| `Server.RateLimitDebugf()` | log.go:238-244 | NOT_APPLICABLE | Rate limiting via sync.Map, not implemented in .NET | Go-specific utility function; can be added if needed |
|
|
| `Server.Fatalf()` | log.go:247-255 | NOT_APPLICABLE | _logger.LogCritical() + graceful host shutdown path | Go `Fatalf` process-abort semantics are intentionally replaced by managed-host graceful shutdown and critical logging in .NET |
|
|
| `Server.Debugf()` | log.go:258-266 | PORTED | _logger.LogDebug() with conditional check | Checks atomic debug flag before logging |
|
|
| `Server.Tracef()` | log.go:269-277 | PORTED | _logger.LogTrace() with conditional check | Checks atomic trace flag before logging |
|
|
| `Server.executeLogCall()` | log.go:279-287 | PORTED | ILogger methods directly called | .NET doesn't need wrapper; calls ILogger directly |
|
|
|
|
### golang/nats-server/logger/log.go
|
|
|
|
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|
|
|-----------|:-------------|--------|:----------------|-------|
|
|
| `Logger` struct | log.go:32-44 | PORTED | Serilog ILogger via Microsoft.Extensions.Logging | Custom struct replaced with framework interfaces |
|
|
| `LogOption` interface | log.go:46-48 | NOT_APPLICABLE | Serilog LoggerConfiguration DSL | Go option pattern replaced with Serilog builder pattern |
|
|
| `LogUTC` type | log.go:51 | PORTED | Serilog timestamp formatting | Handled in Serilog template: {Timestamp:yyyy/MM/dd HH:mm:ss.ffffff} or local |
|
|
| `logFlags()` | log.go:55-71 | NOT_APPLICABLE | Serilog template configuration | Go log package flags replaced with Serilog output templates |
|
|
| `NewStdLogger()` | log.go:74-95 | PORTED | logConfig.WriteTo.Console() in Program.cs | Creates console sink with color detection (AnsiConsoleTheme.Code) |
|
|
| `NewFileLogger()` | log.go:98-124 | PORTED | logConfig.WriteTo.File() in Program.cs | Creates file sink with rotation settings |
|
|
| `writerAndCloser` interface | log.go:126-130 | NOT_APPLICABLE | io.Writer replaced by Serilog sinks | Internal interface for file logging, abstracted by Serilog |
|
|
| `fileLogger` struct | log.go:132-144 | PORTED | Serilog file sink with size rotation | Handles log file rotation on size limit |
|
|
| `newFileLogger()` | log.go:146-165 | PORTED | Serilog WriteTo.File() setup | File creation and sizing handled by Serilog |
|
|
| `fileLogger.setLimit()` | log.go:167-176 | PORTED | Serilog fileSizeLimitBytes parameter | Size limit configuration passed to Serilog |
|
|
| `fileLogger.setMaxNumFiles()` | log.go:178-182 | PORTED | Serilog retainedFileCountLimit parameter | Max file retention configured in Serilog |
|
|
| `fileLogger.logDirect()` | log.go:184-203 | PORTED | src/NATS.Server.Host/Program.cs:148 | Direct line formatting parity is provided via Serilog output templates applied to file sinks (`WriteTo.File(..., outputTemplate: template)`) |
|
|
| `fileLogger.logPurge()` | log.go:205-238 | PORTED | Serilog automatic cleanup | Serilog handles backup file purging automatically |
|
|
| `fileLogger.Write()` | log.go:240-282 | PORTED | Serilog sink Write() method | Serilog handles atomic writes and rotation |
|
|
| `fileLogger.close()` | log.go:284-293 | PORTED | Log.CloseAndFlush() in Program.cs | Proper cleanup via Serilog disposal |
|
|
| `Logger.SetSizeLimit()` | log.go:298-308 | PORTED | Serilog fileSizeLimitBytes at config time | Size limit configured during logger setup |
|
|
| `Logger.SetMaxNumFiles()` | log.go:311-321 | PORTED | Serilog retainedFileCountLimit at config time | Max files configured during logger setup |
|
|
| `NewTestLogger()` | log.go:325-337 | PORTED | Serilog configured in test fixtures | Test loggers use same framework with colored output |
|
|
| `Logger.Close()` | log.go:342-347 | PORTED | Log.CloseAndFlush() via finally block | Cleanup handled in Program.cs finally block |
|
|
| `pidPrefix()` | log.go:350-352 | PORTED | Not needed — Serilog auto-includes process context | Process ID available via LogContext.PushProperty() if needed |
|
|
| `setPlainLabelFormats()` | log.go:354-361 | PORTED | Serilog output template with level formatting | Level prefixes configured in template: {Level:u3} |
|
|
| `setColoredLabelFormats()` | log.go:363-371 | PORTED | Serilog AnsiConsoleTheme.Code for colors | Color formatting handled by Serilog theme |
|
|
| `Logger.Noticef()` | log.go:374-376 | PORTED | ILogger.LogInformation() | Maps notice → information |
|
|
| `Logger.Warnf()` | log.go:379-381 | PORTED | ILogger.LogWarning() | Maps warning → warning |
|
|
| `Logger.Errorf()` | log.go:384-386 | PORTED | ILogger.LogError() | Maps error → error |
|
|
| `Logger.Fatalf()` | log.go:389-391 | PORTED | ILogger.LogCritical() + log.Fatal() | Maps fatal → critical + exit behavior |
|
|
| `Logger.Debugf()` | log.go:394-398 | PORTED | ILogger.LogDebug() with conditional check | Conditional on debug flag |
|
|
| `Logger.Tracef()` | log.go:401-405 | PORTED | ILogger.LogTrace() with conditional check | Conditional on trace flag |
|
|
|
|
### golang/nats-server/logger/syslog.go
|
|
|
|
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|
|
|-----------|:-------------|--------|:----------------|-------|
|
|
| `SysLogger` struct (Unix) | syslog.go:28-32 | PORTED | Serilog.Sinks.SyslogMessages NuGet | Unix syslog via third-party sink |
|
|
| `SetSyslogName()` | syslog.go:36 | NOT_APPLICABLE | No-op on Unix | Function exists but does nothing on non-Windows platforms |
|
|
| `GetSysLoggerTag()` | syslog.go:42-51 | PORTED | Hardcoded "nats-server" in Program.cs | Tag name extracted once at startup |
|
|
| `NewSysLogger()` | syslog.go:54-65 | PORTED | logConfig.WriteTo.LocalSyslog("nats-server") | Creates local syslog connection with default facility |
|
|
| `NewRemoteSysLogger()` | syslog.go:68-80 | PORTED | logConfig.WriteTo.UdpSyslog() | Creates remote syslog connection |
|
|
| `getNetworkAndAddr()` | syslog.go:82-98 | PORTED | URL parsing in Program.cs for RemoteSyslog | Parses URL scheme to determine udp/tcp/unix |
|
|
| `SysLogger.Noticef()` | syslog.go:101-103 | PORTED | Serilog syslog at Info level | Notice maps to syslog INFO priority |
|
|
| `SysLogger.Warnf()` | syslog.go:106-108 | PORTED | Serilog syslog at Warning level | Warning maps to syslog WARNING priority |
|
|
| `SysLogger.Fatalf()` | syslog.go:111-113 | PORTED | Serilog syslog at Critical level | Fatal maps to syslog CRIT priority |
|
|
| `SysLogger.Errorf()` | syslog.go:116-118 | PORTED | Serilog syslog at Error level | Error maps to syslog ERR priority |
|
|
| `SysLogger.Debugf()` | syslog.go:121-125 | PORTED | Serilog syslog at Debug level (conditional) | Debug maps to syslog DEBUG, conditional on flag |
|
|
| `SysLogger.Tracef()` | syslog.go:128-132 | PORTED | Serilog syslog at Info level (conditional) | Trace maps to syslog INFO, conditional on flag |
|
|
|
|
### golang/nats-server/logger/syslog_windows.go
|
|
|
|
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|
|
|-----------|:-------------|--------|:----------------|-------|
|
|
| `SysLogger` struct (Windows) | syslog_windows.go:33-37 | DEFERRED | Windows Event Log not yet integrated | Go uses golang.org/x/sys/windows/svc/eventlog; .NET side not implemented |
|
|
| `SetSyslogName()` | syslog_windows.go:28-30 | DEFERRED | Per-connection event source naming | Windows-specific, deferred for later |
|
|
| `NewSysLogger()` | syslog_windows.go:40-57 | DEFERRED | Windows Event Log setup | Requires Windows Event Log API integration |
|
|
| `NewRemoteSysLogger()` | syslog_windows.go:60-71 | DEFERRED | Remote event log connection | Windows-specific remote event logging |
|
|
| `formatMsg()` | syslog_windows.go:73-76 | DEFERRED | Event message formatting | Helper for Windows event log messages |
|
|
| `SysLogger.Noticef()` | syslog_windows.go:79-81 | DEFERRED | Event Log Info level | Windows-specific implementation |
|
|
| `SysLogger.Warnf()` | syslog_windows.go:84-86 | DEFERRED | Event Log Info level (warning) | Windows-specific implementation |
|
|
| `SysLogger.Fatalf()` | syslog_windows.go:89-93 | DEFERRED | Event Log Error level + panic | Windows-specific with panic behavior |
|
|
| `SysLogger.Errorf()` | syslog_windows.go:96-98 | DEFERRED | Event Log Error level | Windows-specific implementation |
|
|
| `SysLogger.Debugf()` | syslog_windows.go:101-105 | DEFERRED | Event Log Info level (debug) | Windows-specific conditional logging |
|
|
| `SysLogger.Tracef()` | syslog_windows.go:108-112 | DEFERRED | Event Log Info level (trace) | Windows-specific conditional logging |
|
|
|
|
---
|
|
|
|
## Keeping This File Updated
|
|
|
|
After porting work is completed:
|
|
|
|
1. **Update status**: Change `MISSING → PORTED` or `PARTIAL → PORTED` for each item completed
|
|
2. **Add .NET path**: Fill in the ".NET Equivalent" column with the actual file:line
|
|
3. **Re-count LOC**: Update the LOC numbers in `stillmissing.md`:
|
|
```bash
|
|
# Re-count .NET source LOC for this module
|
|
# No custom logging files — uses Microsoft.Extensions.Logging NuGet
|
|
# Re-count .NET test LOC for this module
|
|
# No dedicated logging test files
|
|
```
|
|
4. **Add a changelog entry** below with date and summary of what was ported
|
|
5. **Update the parity DB** if new test mappings were created:
|
|
```bash
|
|
sqlite3 docs/test_parity.db "INSERT INTO test_mappings (go_test_id, dotnet_test_id, confidence, notes) VALUES (?, ?, 'manual', 'ported in YYYY-MM-DD session')"
|
|
```
|
|
|
|
## Change Log
|
|
|
|
| Date | Change | By |
|
|
|------|--------|----|
|
|
| 2026-02-25 | Complete gap inventory analysis: 56 Go symbols analyzed, 44 PORTED, 7 PARTIAL, 0 MISSING, 4 NOT_APPLICABLE, 11 DEFERRED | Claude Code |
|
|
| 2026-02-25 | File created with LLM analysis instructions | auto |
|