Skip to main content

Anti-Patterns

Purpose

This document lists common mistakes discovered during sovdev-logger development. Following these patterns ensures consistent behavior across all language implementations. Compressed into a table per PLAN-003 — the pattern content itself is unchanged, only the format is (previously: ~50 lines of prose/code-example per row, now: one row per pattern). The original bad/correct code examples and full "why this matters" prose are in this file's git history (pre-2026-07-08 commits) if a specific one is ever needed.

"Current status" below reflects the state of python/src/logger.py and typescript/src/logger.ts as of 2026-07-08, re-verified against the code while writing this table — not carried forward from when each pattern was first documented.


Code Anti-Patterns

#Don'tDo InsteadCurrent Status
1Use the internal module name (__name__, module.exports path) for scope_nameUse the service name passed to sovdev_initialize()Fixed in both — Python: logging.getLogger(service_name); TypeScript: logs.getLogger(options.serviceName, ...)
2Use the language's native exception type name (ValueError, HTTPError, ...) for exception_typeAlways emit "Error", regardless of the actual exception class — keeps cross-language Grafana alerts language-independentPython: correct, hardcoded "Error" (logger.py:409). TypeScript is not fully correct: exception_type: clean_exception.constructor?.name || clean_exception.name || 'Error' (logger.ts:481) only resolves to "Error" for a plain Error instance — a custom subclass (class HTTPError extends Error) would leak its class name. Not fixed here (doc-only plan); flagged for a future fix.
3Nest objects in log entries (log_entry.exception = {...})Flatten everything to root-level, prefixed field names (exception_type, http_status_code, ...) — OTLP/Loki/Prometheus require flat structureFixed in both — confirmed by compare-with-master.sh matching field-for-field
4Include unredacted credentials in stack traces, or truncate before redacting (redaction can't remove what's already cut off)Redact first (Authorization, Bearer, API keys, passwords, JWTs, session IDs, cookies), truncate to 350 chars secondPython is more thorough than TypeScript. Python: full regex-based redaction of all 7 categories above (logger.py:301-334), then truncates. TypeScript: only strips config.auth/config.headers.Authorization from axios-shaped error objects (logger.ts:460-467) — no generic regex redaction of the stack trace text itself. Not fixed here; flagged for a future fix since TypeScript is the reference implementation.
5Generate a new traceId for each log call within one logical transactionGenerate once per transaction (typically via sovdev_start_span()), reuse for every related logBy design in both — trace ID comes from the active span, not a per-call generator
6Exit without calling sovdev_shutdown(), or call sovdev_flush() more than once expecting it to also shut downAlways call sovdev_shutdown() before exit, including on error paths and signal handlers (SIGINT/SIGTERM) — OTel batches logs, the final batch is only sent on shutdown. Use sovdev_flush() (safe to call repeatedly) for anywhere short of true process endFixed in both — TypeScript's sovdev_flush() used to also shut down the SDK, so a second call silently stopped metrics (not logs) from being recorded, no error either way; split into sovdev_flush() (repeatable) + sovdev_shutdown() (terminal) in both languages. Confirmed followed by both E2E test scripts (company-lookup.ts/.py call sovdev_shutdown() at the end of main())
7Generate a new sessionId per function callGenerate once in sovdev_initialize(), reuse for the entire process lifetimeFixed in both — logger.py:814, logger.ts:1315, both generate once at initialize
8Define input/response objects inline at each sovdev_log() call siteDefine once as a variable, reuse across the request/response/error logs for that operationUsage pattern — confirmed followed in both E2E test scripts
9Hardcode the function name as a string literal at every log callDefine a FUNCTIONNAME constant once per functionUsage pattern — confirmed followed in both E2E test scripts
10Skip file rotation configuration (unbounded log growth)Configure rotation: main log 50MB × 5 files (~250MB), error log 10MB × 3 files (~30MB)Fixed identically in both — logger.py:245-262, logger.ts:291-310
11Implement custom file writing/rotation from scratchUse an established logging library (handles locking, buffering, atomicity)Fixed in both — Python: stdlib logging + RotatingFileHandler; TypeScript: Winston

Required libraries by language (item 11, for languages not yet implemented):

LanguageLibrary
Gozap or logrus + lumberjack
JavaSLF4J + Logback or Log4j2
C#Serilog or NLog
PHPMonolog
Rusttracing or log + env_logger

Implementation Process Pitfalls

Discovered during the original Python implementation attempt; kept as a table since they're process lessons, not code rules to check off.

#Don'tDo InsteadCurrent Status
12Assume metric names need dots (sovdev.operations.total) sanitized to underscores manuallyThis pitfall no longer holds as documented. TypeScript emits OTel-conventional dot-separated names at the SDK level (sovdev.operations.total, logger.ts:752-767); Python hardcodes underscores directly (sovdev_operations_total, logger.py:86-104). Both currently resolve to underscore names in Prometheus — confirmed by tools/validation/uis/query-prometheus.sh's own default metric name being the underscore form — because the OTel Collector's Prometheus exporter sanitizes dots automatically. The original 30-minute debugging incident this documents likely predates that sanitizing step being in place, or came from a different export path. Neither implementation currently fails because of this.
13Convert an enum to string with str()/.toString() (returns the enum name, e.g. "SOVDEV_LOGLEVELS.ERROR", not its value)Use .value (Python) / the string literal directly (TypeScript)Fixed — Python uses level.value throughout (logger.py:909 and others); this was the one historically-documented bug PLAN-001's file-log comparison could actually catch, and it's confirmed still fixed
14Omit severity_text/severity_number needed by Grafana's error panelEnsure the OTel log record carries bothHandled differently in both, neither is missing it: TypeScript sets severityNumber/severityText explicitly via a manual level map (logger.ts:159-165, 219-220); Python has no equivalent code at all because it relies on the OTel SDK's own LoggingHandler bridge, which derives severity from the stdlib logging level automatically — that's the idiomatic way to do it for that SDK, not a gap.
15Spend time debugging kubectl connectivity when validating against local UIStools/validation/uis/query-*.sh need kubectl access by design (they query the local cluster directly); if kubectl genuinely isn't available, use tools/validation/grafana-cloud/ against a real Grafana Cloud backend instead — no kubectl involved at allProcess guidance, not a code pattern — no current-status check applies

Document Status: ✅ v2.0.0 — compressed into tables 2026-07-08, technical content unchanged, verified against current code Part of: sovdev-logger specification