Skip to content

telemetry.ts console-exporter fallback floods stdout in containers when OTEL_EXPORTER_OTLP_ENDPOINT is unset #505

Description

@oskarszoon

Summary

infra/*/src/telemetry.ts silently falls back to the console exporters — with the unbatched Simple*Processor — whenever OTEL_EXPORTER_OTLP_ENDPOINT is unset. In a container that is not dev-safe: it turns every span, every metric datapoint and every log record into a line on stdout, which the cluster log agent then harvests and ships. We hit this in a deployment and log volume went up roughly 4x, with about 99.8% of it landing as severity=info.

Detail

From infra/overlay-server/src/telemetry.ts (the same shape is in all seven components listed in infra/OBSERVABILITY.md):

const useOtlp = typeof otlpEndpoint === 'string' && otlpEndpoint.length > 0

const traceExporter  = useOtlp ? new OTLPTraceExporter()  : new ConsoleSpanExporter()
const metricExporter = useOtlp ? new OTLPMetricExporter() : new ConsoleMetricExporter()
const logExporter    = useOtlp ? new OTLPLogExporter()    : new ConsoleLogRecordExporter()

const logRecordProcessor = useOtlp
    ? new BatchLogRecordProcessor({ exporter: logExporter })
    : new SimpleLogRecordProcessor({ exporter: logExporter })

and the span processor likewise uses SimpleSpanProcessor in the fallback.

The docstring calls the fallback "dev-safe", and it is — locally. The problem is that nothing distinguishes "developer ran this with no collector" from "this is in production and the endpoint was never wired". The second case is the dangerous one and it is the silent one:

  • Full auto-instrumentation is on. getNodeAutoInstrumentations() plus RuntimeNodeInstrumentation() means http, express, mongodb, mysql2 and dns spans, and nodejs.eventloop.* / v8js.memory.heap.* / GC metrics, all rendered to stdout.
  • Simple*Processor means one write per record, no batching.
  • Console output carries no severity, so a log pipeline classifies the whole flood as info — it is indistinguishable from application logging without parsing it.
  • console.* is double-written. The bridge emits each call to the OTel Logs API and calls the original, so in the fallback path the same message goes to stdout twice: once as the raw console.* line and once as a ConsoleLogRecordExporter record.
  • The image CMD preloads telemetry.js, so this is the default behaviour of the published image, not something a user opts into.

Suggested fixes

Any of these would have prevented it; the first two feel right together.

  1. Do not export at all when no endpoint is configured unless explicitly asked, e.g. OTEL_CONSOLE_EXPORTERS=true. A process with nowhere to send telemetry should be quiet, not verbose.
  2. Gate the fallback on environment. DEPLOY_ENV / NODE_ENV is already read a few lines above for deployment.environment; refusing console exporters when it is production costs nothing.
  3. If the fallback stays, batch itBatchSpanProcessor / BatchLogRecordProcessor even for console, and a much longer exportIntervalMillis for the metric reader.
  4. Warn loudly once at startup when falling back, naming the variable that is missing. Right now the only signal is the volume itself.

Happy to send a PR for whichever shape you prefer.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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