Skip to content
This repository was archived by the owner on Jul 13, 2026. It is now read-only.
This repository was archived by the owner on Jul 13, 2026. It is now read-only.

Production integration feedback: 1 bug, 1 unshipped documented API, a feature request, doc gaps, and outdated OTel deps #23

Description

@terchris

We've been running @terchris/sovdev-logger@1.0.2 in production for a few
weeks (a TypeScript/Node 22 API, esbuild-bundled to a single .mjs file,
deployed on Cloud Run) and collected a batch of findings along the way.
Grouped by category, strongest first.

Bugs

1. sovdev_generate_trace_id is documented but not exported in 1.0.2.

The README's "Common Logging Patterns" section ("Using traceId to Link
Operations") documents this function, but it isn't in the published
package:

$ node -e "console.log(Object.keys(await import('@terchris/sovdev-logger')))"
# 13 exports listed — sovdev_generate_trace_id is not among them
$ tsc --noEmit
# Module '"@terchris/sovdev-logger"' has no exported member 'sovdev_generate_trace_id'

So the README's whole "link a multi-step operation under one traceId"
pattern is currently unusable as written. We worked around it with
sovdev_start_span/sovdev_end_span (which do exist and work, and give
real span duration on top of trace correlation) — but a documented public
API that silently doesn't exist will trip up anyone who follows the
README rather than the E2E example. Either ship the function or remove it
from the docs until it is.

2. sovdev_test_otlp_connection() treats HTTP 204 as a failure for the logs endpoint.

Against otlp-gateway-prod-eu-west-0.grafana.net, the helper returns:

{ "reachable": false, "error": "HTTP 204: No Content" }

204 is normally a healthy, empty-body response, not an error — and the
metrics/traces checks (same host, different paths) both reported
reachable: true. The actual sovdev_log() call afterward completed with
no flush error, so logs were very likely delivered — the connectivity
check's logs-endpoint branch appears to require a non-204 response when it
probably shouldn't.

Feature request: request-scoped context propagation

sovdev_log()'s signature requires every field explicit on every call
(level, function_name, message, peer_service, input_json?, response_json?, exception_object?) — there's no ambient/"request-scoped" way to set a
value once (e.g. per HTTP request) and have it inherit into every
sovdev_log call downstream without re-passing it. In a service with many
call sites, this means genuinely cross-cutting fields (a caller/application
identifier, an acting-user id) have to be manually threaded through every
function signature down to the log call.

A concrete ask: something like sovdev_set_context({...}), called once
per request/middleware, backed by AsyncLocalStorage (or OTel's own
context/baggage primitives, since the library already wraps the OTel
SDK — sovdev_start_span returns a real @opentelemetry/api Span, so
this machinery likely already exists inside the library). Every
sovdev_log() call downstream would read the ambient context and merge it
into the emitted log automatically.

This isn't a novel idea — it's the same shape as:

  • OpenTelemetry's own context API (Node's AsyncLocalStorageContextManager)
  • Java/Logback's MDC, .NET's logger scopes (AsyncLocal<T>), Python
    structlog's contextvars — every mature logging ecosystem converged on
    this independently
  • W3C Baggage, if the value ever needs to survive a network hop between
    services (not our immediate need, but worth the API being extensible
    toward it)

We're not asking the library to decide what the field should be named or
how trustworthy it is (self-reported vs. verified) — just for a mechanism
to propagate whatever we choose without re-passing it at every call site.

Documentation gaps

  • LOG_TO_FILE isn't mentioned in the onboarding guide. Local file
    logging (./logs/dev.log) is on by default, which has no value on
    ephemeral/serverless filesystems (Cloud Run, Lambda, etc.). Setting
    LOG_TO_FILE=false cleanly disables it, but we only found the env var by
    reading source — worth a line in the onboarding guide recommending it for
    containerized/serverless deployments.
  • Auto-instrumentation's import-order requirement is only in a code
    comment, not a warning.
    The E2E example dynamically import()s a
    module inside a function specifically so OpenTelemetry's
    auto-instrumentation can patch it before use — because ES module imports
    are hoisted, a top-of-file import runs before sovdev_initialize() ever
    could. The example's comment notes production apps should use a separate
    instrumentation.js + node --require instead, but this isn't called
    out as a warning anywhere prominent (onboarding guide, README
    quick-start). A new integrator following the quick-start (not the E2E
    example) could get silently-unpatched auto-instrumentation with no
    warning at all.
  • The ERROR/FATAL → ServiceNow-incident behavior isn't in the onboarding
    guide.
    The README's "Compliance" section documents it clearly, but the
    onboarding guide doesn't repeat the warning — worth a callout there too
    ("avoid ERROR/FATAL during initial integration testing — they may open
    real incidents"), since onboarding is where a new integrator is most
    likely to log a test ERROR before knowing this.
  • Expected bundle size growth isn't documented. A minimal integration
    (a few lines + the library) added ~7.5 MB to an esbuild --bundle
    single-file output. Not disqualifying at typical serverless scale, but
    worth a line in the onboarding guide so integrators aren't surprised.

Dependency versions — 4 Dependabot alerts (2 high, 2 moderate)

1.0.2's pinned OpenTelemetry dependencies are behind their patched
versions:

Package Installed via ^ range Patched version
@opentelemetry/sdk-node ^0.55.0 0.217.0
@opentelemetry/auto-instrumentations-node ^0.51.0 0.75.0
@opentelemetry/core (transitive) pinned 1.28.0/1.30.1 across the tree 2.8.0
uuid (transitive, via gcp-metadata/gaxios) ^9.0.1 11.1.1

We checked the real-world exposure for each and none have an exploitable
path in our own deployment (the high-severity ones require the Prometheus
pull-exporter to be enabled, which we don't use; the moderate ones require
either inbound W3C baggage header extraction or a direct uuid.v3/v5/v6
call with an external buffer, neither of which we do) — but that's
deployment-specific luck, not something the package should rely on
integrators to verify individually. The current ^0.51.0/^0.55.0 ranges
don't reach the patched versions, so this needs a version bump in a future
release rather than something we can resolve on our end.

A couple of things that worked well, for what it's worth

  • esbuild --bundle --platform=node --format=esm --target=node22 bundled
    cleanly — ran identically bundled and unbundled against the real OTLP
    endpoints, verified on the Grafana dashboard (both forms delivered real
    telemetry). The feared auto-instrumentation-vs-single-file-bundle
    conflict didn't materialize for a manual sovdev_log/span usage pattern.
  • The company-lookup.ts E2E example is a genuinely good teaching
    artifact — more prescriptive than the quick-start alone (paired
    start/success/error logs, job_status/job_progress for batch work,
    trace correlation via spans). Worth pointing new integrators to it
    directly.

Happy to share more detail on any of the above if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions