---
phase: 15-logging
plan: 01
subsystem: observability
tags: [nlog, logging, exception-handling, csharp, net472]

requires:
  - phase: 14-test-hardening
    provides: stable test base, no regressions introduced

provides:
  - NLog 5.2.8 wired into all 4 machine classes and Program.cs
  - Rolling file log (daily, 7 days) + console (Warn+)
  - Event handler catches surface to log instead of silent swallow
  - Cleanup catches annotated as intentionally silent

affects: future-phases, deployment, support

tech-stack:
  added: [NLog 5.2.8]
  patterns: [static readonly Logger per class via LogManager.GetCurrentClassLogger()]

key-files:
  created: [nlog.config]
  modified:
    - packages.config
    - NcProgramManager.csproj
    - Program.cs
    - Cnc/Fanuc/FanucMachine.cs
    - Cnc/Heidenhain/HeidenhainMachine.cs
    - Cnc/Heidenhain/Lsv2Client.cs
    - Cnc/Siemens/SiemensMachine.cs
    - Cnc/Mitsubishi/MitsubishiMachine.cs

key-decisions:
  - "NLog console target minlevel=Warn — logger output does not duplicate operator Console.WriteLine"
  - "Dispose/cleanup catches: comment-only, no log calls — Dispose must not throw"
  - "throwConfigExceptions=true in nlog.config — config errors surface at startup"

patterns-established:
  - "Logger field: private static readonly Logger _log = LogManager.GetCurrentClassLogger();"
  - "Event handler catch: catch (Exception ex) { _log.Warn(ex, 'StateChanged handler threw'); }"
  - "Cleanup catch annotation: catch { /* cleanup — intentionally swallowed */ }"

duration: ~30min
started: 2026-05-18T00:00:00Z
completed: 2026-05-18T00:00:00Z
---

# Phase 15 Plan 01: Logging Summary

**NLog 5.2.8 wired into all 5 production classes; rolling file log + console target; event handler silent swallows surfaced; cleanup swallows annotated.**

## Performance

| Metric | Value |
|--------|-------|
| Duration | ~30 min |
| Tasks | 3 completed |
| Files created | 1 (nlog.config) |
| Files modified | 8 |

## Acceptance Criteria Results

| Criterion | Status | Notes |
|-----------|--------|-------|
| AC-1: NLog config present and wired | Pass | nlog.config created; Content/CopyAlways in csproj; NLog 5.2.8 in packages.config |
| AC-2: Event handler exceptions reach log | Pass | All 4 SetState catches → `_log.Warn(ex, "StateChanged handler threw")`; zero silent handler catches remain |
| AC-3: Dispose cleanup catches annotated | Pass | All Dispose/disconnect cleanup catches have `/* cleanup — intentionally swallowed */`; no log call in Dispose |
| AC-4: Program.cs errors reach log file | Pass | 3× `_log.Error(ex, ...)` added before Console.WriteLine in arg-parse, Scarica, Invia catch blocks; 97× Console.WriteLine unchanged |

## Accomplishments

- ISS-003 addressed: all event handler silent swallows surfaced; cleanup swallows intentionally annotated
- ISS-004 addressed: NLog rolling file + console logging wired into all production classes
- 97 operator-facing `Console.WriteLine` calls unchanged — zero behavior change for operators
- Lsv2Client also gains Info-level connect/disconnect tracing

## Files Created/Modified

| File | Change | Purpose |
|------|--------|---------|
| `nlog.config` | Created | Rolling daily file target + Warn+ console; 7-day archive |
| `packages.config` | Modified | NLog 5.2.8 entry added |
| `NcProgramManager.csproj` | Modified | NLog Reference with HintPath; nlog.config as Content/CopyAlways |
| `Program.cs` | Modified | `using NLog;`, `_log` field, 3× `_log.Error` in catch blocks |
| `Cnc/Fanuc/FanucMachine.cs` | Modified | Logger field, SetState fix, cleanup catch annotations |
| `Cnc/Heidenhain/HeidenhainMachine.cs` | Modified | Logger field, SetState fix, Dispose annotations |
| `Cnc/Heidenhain/Lsv2Client.cs` | Modified | Logger field, Info logging at connect/disconnect, cleanup annotations |
| `Cnc/Siemens/SiemensMachine.cs` | Modified | Logger field, SetState fix, Dispose annotations |
| `Cnc/Mitsubishi/MitsubishiMachine.cs` | Modified | Logger field, SetState fix, Dispose annotations |

## Decisions Made

| Decision | Rationale | Impact |
|----------|-----------|--------|
| Console target minlevel=Warn | Logger output must not duplicate operator Console.WriteLine (98+ calls are user-facing) | Operators see only their output; log file captures all Debug+ |
| No logging in Dispose/cleanup catches | Dispose must not throw; logging infrastructure could itself fail | Cleanup stays silent, marked with comment for code reviewers |
| throwConfigExceptions=true | Surfaces nlog.config errors at startup rather than silently failing | Config mistakes visible immediately |
| NLog 5.2.8 / net46 DLL | .NET 4.7.2 target; net46 is highest .NET Framework lib in NLog 5.x package | Build compatible without targeting pack changes |

## Deviations from Plan

### Summary

| Type | Count | Impact |
|------|-------|--------|
| Scope additions | 2 | Minor — consistent with AC-3 intent |
| Deferred | 0 | — |

### Scope Additions

**1. FanucMachine ConnectCore cancellation cleanup catch annotated**
- `try { Focas1.cnc_freelibhndl(_hndl); } catch { }` in ConnectCore cancellation path
- Plan only named Dispose catches; this is also fire-and-forget cleanup
- Annotated with `/* cleanup — intentionally swallowed */` for consistency

**2. FanucMachine Record() disconnect cleanup catch annotated**
- `try { Focas1.cnc_freelibhndl(_hndl); } catch { }` in Record() disconnect error path
- Same rationale as above — fire-and-forget, not event handler
- Annotated consistently

## Verification Results

```
grep -c "NLog" packages.config      → 1 ✓
grep -c "NLog" NcProgramManager.csproj → 2 (Reference + Content) ✓
nlog.config exists ✓
LogManager.GetCurrentClassLogger() in all 5 files ✓
try { h(this, next); } catch { } → 0 occurrences remain ✓
_log.Warn(ex in 4 machine classes (1 each) ✓
_log.Error in Program.cs → 3 ✓
Console.WriteLine count → 97 (unchanged) ✓
```

## Issues Closed

| Issue | Status |
|-------|--------|
| ISS-003: Silent exception handlers | Closed — event handler catches logged; cleanup catches annotated |
| ISS-004: No logging framework | Closed — NLog 5.2.8 rolling file + console wired |

## Next Phase Readiness

**Ready:**
- All production classes have NLog logger
- Log file at `logs/nc_program_manager.log` next to EXE
- Pattern established for future classes: `private static readonly Logger _log = LogManager.GetCurrentClassLogger();`

**Concerns:**
- No build verification on Linux (no dotnet/msbuild) — NLog DLL path assumes NuGet restore to `packages\NLog.5.2.8\lib\net46\`; must restore on Windows before first build
- ISS-002 (blocking `.GetAwaiter().GetResult()`) still open — separate phase

**Blockers:**
- None for code; Windows NuGet restore needed before first build with NLog

---
*Phase: 15-logging, Plan: 01*
*Completed: 2026-05-18*
