@memhtml/telemetry: the opt-in trace exporter. It sends the spans memhtml already records to an OpenTelemetry collector over OTLP HTTP, and it does nothing unless OTEL_EXPORTER_OTLP_ENDPOINT is set.
The package exports one function and two interfaces. layerTelemetry takes a TelemetryOptions and returns an Effect layer that a runtime assembly provides. The CLI provides it with the service name memhtml-cli (apps/cli/src/run.ts), and the MCP server with memhtml-mcp (apps/mcp/src/server.ts).
With OTEL_EXPORTER_OTLP_ENDPOINT unset or blank, the layer is Layer.empty and no socket is opened.
With the variable set, the layer replaces Effect’s tracer with Effect’s own OtlpTracer, which batches spans and posts them as JSON to <endpoint>/v1/traces, with trailing slashes on the endpoint dropped. The service.name resource attribute is OTEL_SERVICE_NAME when that variable is set and not blank, and the assembly’s serviceName otherwise.
layerTelemetry reads OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_SERVICE_NAME when it is called, from options.env when given and process.env otherwise. TelemetryModules names the two Effect modules the exporter needs, effect/http and effect/observability. This package imports them only when the endpoint is set, through a loader that options.load replaces so a test can check that the disabled path never calls it.
A failed export does not fail the command or change the envelope on stdout, the exit code or the MCP stream. The exporter retries transient failures, then stops exporting for a while. The first failed connection to the collector logs one warning on stderr, and an error status from the collector logs none.
Modules
Section titled “Modules”The package is one module, src/index.ts, published as the import path @memhtml/telemetry, so this page is its whole exported surface.
Interfaces
Section titled “Interfaces”TelemetryModules
Section titled “TelemetryModules”The two Effect modules the exporter needs, imported here only when the endpoint is set.
Properties
Section titled “Properties”readonly http: __module;The effect/http module, for the fetch-backed HTTP client the exporter posts spans with.
observability
Section titled “observability”readonly observability: __module;The effect/observability module, for OtlpTracer and its JSON serialization.
TelemetryOptions
Section titled “TelemetryOptions”What a runtime assembly passes to layerTelemetry.
Properties
Section titled “Properties”readonly env?: Record<string, string | undefined>;Where the endpoint and OTEL_SERVICE_NAME are read, defaulting to process.env, when the
factory is CALLED rather than through effect/Config. Config’s default provider is built once
per process and cached (hookConfigProvider in apps/cli/src/hook.ts records the probe), and
this factory runs once per invocation, so either variable set by a test after an earlier read
must still be seen.
readonly load?: () => Promise<TelemetryModules>;The module loader, injectable so a test can PROVE the disabled path loads nothing: the acceptance bar is not “no spans exported” but “no exporter code even evaluated”, and only a loader spy can tell those apart.
Returns
Section titled “Returns”Promise<TelemetryModules>
serviceName
Section titled “serviceName”readonly serviceName: string;The service.name resource attribute when OTEL_SERVICE_NAME is unset or blank: which of the
runtime assemblies this process is (memhtml-cli, memhtml-mcp), because one collector
typically receives both and the operator’s first question is which process a trace came from.
Functions
Section titled “Functions”layerTelemetry()
Section titled “layerTelemetry()”function layerTelemetry(options): Layer<never>;The tracer layer for one runtime assembly.
OTEL_EXPORTER_OTLP_ENDPOINT unset or blank returns Layer.empty: the program runs as it would
without this package, with no exporter module loaded and no socket opened. Set, the layer
overrides effect’s tracer reference with a batching OTLP HTTP exporter aimed at
<endpoint>/v1/traces (the standard per-signal path; a trailing slash on the endpoint is
tolerated because both spellings arrive from shell profiles).
A failed export never fails a command. The exporter retries transient failures, then disables itself and drops the batch, and the program’s error channel never sees it, so the envelope, the exit code, and the MCP stream are exactly what they would have been. The final batch of a short-lived process is flushed when the layer’s scope closes, bounded by the exporter’s own shutdown timeout so a dead collector delays exit by seconds, never hangs it.
Parameters
Section titled “Parameters”options
Section titled “options”Returns
Section titled “Returns”Layer<never>