@smthrs/observability 0.35.0 → 1.0.0-rc.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +56 -0
- package/LICENSE +1 -1
- package/README.md +87 -0
- package/dist/cjs/BrowserOtel.d.ts +37 -0
- package/dist/cjs/BrowserOtel.d.ts.map +1 -0
- package/dist/cjs/BrowserOtel.js +47 -0
- package/dist/cjs/BrowserOtel.js.map +7 -0
- package/dist/cjs/Endpoint.d.ts +70 -0
- package/dist/cjs/Endpoint.d.ts.map +1 -0
- package/dist/cjs/Endpoint.js +85 -0
- package/dist/cjs/Endpoint.js.map +7 -0
- package/dist/cjs/JournalLogger.d.ts +232 -0
- package/dist/cjs/JournalLogger.d.ts.map +1 -0
- package/dist/cjs/JournalLogger.js +415 -0
- package/dist/cjs/JournalLogger.js.map +7 -0
- package/dist/cjs/Logger.d.ts +56 -0
- package/dist/cjs/Logger.d.ts.map +1 -0
- package/dist/cjs/Logger.js +72 -0
- package/dist/cjs/Logger.js.map +7 -0
- package/dist/cjs/Metric.d.ts +67 -0
- package/dist/cjs/Metric.d.ts.map +1 -0
- package/dist/cjs/Metric.js +68 -0
- package/dist/cjs/Metric.js.map +7 -0
- package/dist/cjs/NodeOtel.d.ts +42 -0
- package/dist/cjs/NodeOtel.d.ts.map +1 -0
- package/dist/cjs/NodeOtel.js +78 -0
- package/dist/cjs/NodeOtel.js.map +7 -0
- package/dist/cjs/Otel.d.ts +63 -0
- package/dist/cjs/Otel.d.ts.map +1 -0
- package/dist/cjs/Otel.js +75 -0
- package/dist/cjs/Otel.js.map +7 -0
- package/dist/cjs/Otlp.d.ts +203 -0
- package/dist/cjs/Otlp.d.ts.map +1 -0
- package/dist/cjs/Otlp.js +251 -0
- package/dist/cjs/Otlp.js.map +7 -0
- package/dist/cjs/Resource.d.ts +163 -0
- package/dist/cjs/Resource.d.ts.map +1 -0
- package/dist/cjs/Resource.js +186 -0
- package/dist/cjs/Resource.js.map +7 -0
- package/dist/cjs/index.d.ts +61 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +47 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/internal/schemaIssuePath.d.ts +31 -0
- package/dist/cjs/internal/schemaIssuePath.d.ts.map +1 -0
- package/dist/cjs/internal/schemaIssuePath.js +49 -0
- package/dist/cjs/internal/schemaIssuePath.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/esm/BrowserOtel.d.ts +37 -0
- package/dist/esm/BrowserOtel.d.ts.map +1 -0
- package/dist/esm/BrowserOtel.js +24 -0
- package/dist/esm/BrowserOtel.js.map +1 -0
- package/dist/esm/Endpoint.d.ts +70 -0
- package/dist/esm/Endpoint.d.ts.map +1 -0
- package/dist/esm/Endpoint.js +110 -0
- package/dist/esm/Endpoint.js.map +1 -0
- package/dist/esm/JournalLogger.d.ts +232 -0
- package/dist/esm/JournalLogger.d.ts.map +1 -0
- package/dist/esm/JournalLogger.js +520 -0
- package/dist/esm/JournalLogger.js.map +1 -0
- package/dist/esm/Logger.d.ts +56 -0
- package/dist/esm/Logger.d.ts.map +1 -0
- package/dist/esm/Logger.js +61 -0
- package/dist/esm/Logger.js.map +1 -0
- package/dist/esm/Metric.d.ts +67 -0
- package/dist/esm/Metric.d.ts.map +1 -0
- package/dist/esm/Metric.js +80 -0
- package/dist/esm/Metric.js.map +1 -0
- package/dist/esm/NodeOtel.d.ts +42 -0
- package/dist/esm/NodeOtel.d.ts.map +1 -0
- package/dist/esm/NodeOtel.js +56 -0
- package/dist/esm/NodeOtel.js.map +1 -0
- package/dist/esm/Otel.d.ts +63 -0
- package/dist/esm/Otel.d.ts.map +1 -0
- package/dist/esm/Otel.js +52 -0
- package/dist/esm/Otel.js.map +1 -0
- package/dist/esm/Otlp.d.ts +203 -0
- package/dist/esm/Otlp.d.ts.map +1 -0
- package/dist/esm/Otlp.js +384 -0
- package/dist/esm/Otlp.js.map +1 -0
- package/dist/esm/Resource.d.ts +163 -0
- package/dist/esm/Resource.d.ts.map +1 -0
- package/dist/esm/Resource.js +238 -0
- package/dist/esm/Resource.js.map +1 -0
- package/dist/esm/index.d.ts +61 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +61 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/schemaIssuePath.d.ts +31 -0
- package/dist/esm/internal/schemaIssuePath.d.ts.map +1 -0
- package/dist/esm/internal/schemaIssuePath.js +59 -0
- package/dist/esm/internal/schemaIssuePath.js.map +1 -0
- package/docs/README.md +164 -0
- package/docs/api.md +780 -0
- package/docs/concepts/instrumentation-and-export.md +98 -0
- package/docs/concepts/layer-map.md +73 -0
- package/docs/concepts/validated-acquisition.md +106 -0
- package/docs/guides/export-to-a-collector.md +190 -0
- package/docs/guides/forward-logs-to-the-journal.md +226 -0
- package/docs/guides/install-a-logger.md +111 -0
- package/docs/guides/read-runtime-metrics.md +83 -0
- package/docs/guides/testing.md +134 -0
- package/docs/guides/wire-an-opentelemetry-sdk.md +226 -0
- package/docs/installation.md +102 -0
- package/docs/quickstart.md +145 -0
- package/docs/troubleshooting.md +168 -0
- package/package.json +221 -32
- package/src/BrowserOtel.ts +51 -0
- package/src/Endpoint.ts +131 -0
- package/src/JournalLogger.ts +635 -0
- package/src/Logger.ts +107 -0
- package/src/Metric.ts +88 -0
- package/src/NodeOtel.ts +90 -0
- package/src/Otel.ts +118 -0
- package/src/Otlp.ts +514 -0
- package/src/Resource.ts +325 -0
- package/src/index.ts +67 -0
- package/src/internal/schemaIssuePath.ts +65 -0
- package/docker-compose.otel.yml +0 -104
- package/grafana/dashboards/smithers-dashboard.json +0 -453
- package/grafana/provisioning/dashboards/dashboards.yml +0 -12
- package/grafana/provisioning/datasources/datasources.yml +0 -49
- package/loki/loki-config.yaml +0 -35
- package/otel-collector-config.yml +0 -38
- package/prometheus/prometheus.yml +0 -8
- package/src/MetricName.ts +0 -1
- package/src/MetricsServiceLive.js +0 -9
- package/src/README.md +0 -28
- package/src/ResolvedSmithersObservabilityOptions.ts +0 -12
- package/src/SmithersEvent.ts +0 -843
- package/src/SmithersLogFormat.ts +0 -1
- package/src/SmithersMetricDefinition.ts +0 -15
- package/src/SmithersMetricType.ts +0 -1
- package/src/SmithersMetricUnit.ts +0 -1
- package/src/SmithersObservability.js +0 -15
- package/src/SmithersObservabilityOptions.ts +0 -12
- package/src/SmithersObservabilityService.ts +0 -13
- package/src/_coreCorrelation/CorrelationContext.ts +0 -10
- package/src/_coreCorrelation/CorrelationContextLive.js +0 -11
- package/src/_coreCorrelation/CorrelationContextService.js +0 -15
- package/src/_coreCorrelation/CorrelationContextServiceShape.ts +0 -12
- package/src/_coreCorrelation/CorrelationPatch.ts +0 -3
- package/src/_coreCorrelation/README.md +0 -20
- package/src/_coreCorrelation/_correlationStorage.js +0 -4
- package/src/_coreCorrelation/correlationContextFiberRef.js +0 -5
- package/src/_coreCorrelation/correlationContextToLogAnnotations.js +0 -18
- package/src/_coreCorrelation/getCurrentCorrelationContext.js +0 -9
- package/src/_coreCorrelation/getCurrentCorrelationContextEffect.js +0 -11
- package/src/_coreCorrelation/index.js +0 -14
- package/src/_coreCorrelation/mergeCorrelationContext.js +0 -51
- package/src/_coreCorrelation/runWithCorrelationContext.js +0 -14
- package/src/_coreCorrelation/updateCurrentCorrelationContext.js +0 -17
- package/src/_coreCorrelation/withCorrelationContext.js +0 -38
- package/src/_coreCorrelation/withCurrentCorrelationContext.js +0 -11
- package/src/_coreMetrics.js +0 -14
- package/src/_coreMetricsShape.ts +0 -44
- package/src/_corePrometheus.js +0 -91
- package/src/_corePrometheusShape.ts +0 -11
- package/src/_coreTracing.js +0 -132
- package/src/_coreTracingShape.ts +0 -17
- package/src/_otelLogBuilders.js +0 -102
- package/src/_sessionFileResolvers.js +0 -164
- package/src/_smithersSpanAttributeAliases.js +0 -19
- package/src/_smithersTraceSpanStorage.js +0 -3
- package/src/_traceEventNormalizers.js +0 -499
- package/src/_traceRedaction.js +0 -162
- package/src/agentSessionEventToOtelLogRecord.js +0 -55
- package/src/agentTrace.ts +0 -144
- package/src/agentTraceCapabilities.js +0 -151
- package/src/annotateSmithersTrace.js +0 -11
- package/src/canonicalTraceEventToOtelLogRecord.js +0 -55
- package/src/correlation.js +0 -56
- package/src/createSmithersObservabilityLayer.js +0 -59
- package/src/createSmithersOtelLayer.js +0 -23
- package/src/createSmithersRuntimeLayer.js +0 -2
- package/src/detectAgentFamily.js +0 -26
- package/src/detectCaptureMode.js +0 -29
- package/src/emitOtelLogRecord.js +0 -19
- package/src/getCurrentSmithersTraceAnnotations.js +0 -14
- package/src/getCurrentSmithersTraceSpan.js +0 -7
- package/src/index.d.ts +0 -1392
- package/src/index.js +0 -137
- package/src/kindPhase.js +0 -17
- package/src/logging.js +0 -264
- package/src/makeSmithersSpanAttributes.js +0 -20
- package/src/metrics/README.md +0 -22
- package/src/metrics/SmithersMetricDefinition.ts +0 -17
- package/src/metrics/SmithersMetricType.ts +0 -1
- package/src/metrics/SmithersMetricUnit.ts +0 -1
- package/src/metrics/_asyncExternalWaitCounts.js +0 -4
- package/src/metrics/_buckets.js +0 -37
- package/src/metrics/_incrementGauge.js +0 -11
- package/src/metrics/_processStartMs.js +0 -1
- package/src/metrics/activeNodes.js +0 -2
- package/src/metrics/activeRuns.js +0 -2
- package/src/metrics/agentActionsTotal.js +0 -2
- package/src/metrics/agentDurationMs.js +0 -3
- package/src/metrics/agentErrorsTotal.js +0 -2
- package/src/metrics/agentEventsTotal.js +0 -2
- package/src/metrics/agentInvocationsTotal.js +0 -2
- package/src/metrics/agentRetriesTotal.js +0 -2
- package/src/metrics/agentSessionsTotal.js +0 -2
- package/src/metrics/agentTokensTotal.js +0 -2
- package/src/metrics/alertDeliveriesAttempted.js +0 -2
- package/src/metrics/alertDeliveriesSuppressed.js +0 -2
- package/src/metrics/alertsAcknowledgedTotal.js +0 -2
- package/src/metrics/alertsActive.js +0 -2
- package/src/metrics/alertsEscalatedTotal.js +0 -2
- package/src/metrics/alertsFiredTotal.js +0 -2
- package/src/metrics/alertsReopenedTotal.js +0 -2
- package/src/metrics/alertsResolvedTotal.js +0 -2
- package/src/metrics/alertsSilencedTotal.js +0 -2
- package/src/metrics/approvalPending.js +0 -2
- package/src/metrics/approvalWaitDuration.js +0 -5
- package/src/metrics/approvalsDenied.js +0 -2
- package/src/metrics/approvalsGranted.js +0 -2
- package/src/metrics/approvalsRequested.js +0 -2
- package/src/metrics/attemptDuration.js +0 -3
- package/src/metrics/attentionBacklog.js +0 -2
- package/src/metrics/cacheHits.js +0 -2
- package/src/metrics/cacheMisses.js +0 -2
- package/src/metrics/dbQueryDuration.js +0 -3
- package/src/metrics/dbRetries.js +0 -2
- package/src/metrics/dbTransactionDuration.js +0 -3
- package/src/metrics/dbTransactionRetries.js +0 -2
- package/src/metrics/dbTransactionRollbacks.js +0 -2
- package/src/metrics/devtoolsActiveSubscribers.js +0 -2
- package/src/metrics/devtoolsBackpressureDisconnectTotal.js +0 -2
- package/src/metrics/devtoolsDeltaBuildMs.js +0 -3
- package/src/metrics/devtoolsEventBytes.js +0 -3
- package/src/metrics/devtoolsEventTotal.js +0 -2
- package/src/metrics/devtoolsSnapshotBuildMs.js +0 -5
- package/src/metrics/devtoolsSubscribeTotal.js +0 -2
- package/src/metrics/errorsTotal.js +0 -2
- package/src/metrics/eventsEmittedTotal.js +0 -2
- package/src/metrics/externalWaitAsyncPending.js +0 -2
- package/src/metrics/gatewayApprovalDecisionsTotal.js +0 -2
- package/src/metrics/gatewayAuthEventsTotal.js +0 -2
- package/src/metrics/gatewayConnectionsActive.js +0 -2
- package/src/metrics/gatewayConnectionsClosedTotal.js +0 -2
- package/src/metrics/gatewayConnectionsTotal.js +0 -2
- package/src/metrics/gatewayCronTriggersTotal.js +0 -2
- package/src/metrics/gatewayErrorsTotal.js +0 -2
- package/src/metrics/gatewayHeartbeatTicksTotal.js +0 -2
- package/src/metrics/gatewayMessagesReceivedTotal.js +0 -2
- package/src/metrics/gatewayMessagesSentTotal.js +0 -2
- package/src/metrics/gatewayRpcCallsTotal.js +0 -2
- package/src/metrics/gatewayRpcDuration.js +0 -3
- package/src/metrics/gatewayRunEventBackpressureDisconnectTotal.js +0 -4
- package/src/metrics/gatewayRunsCompletedTotal.js +0 -2
- package/src/metrics/gatewayRunsStartedTotal.js +0 -2
- package/src/metrics/gatewaySignalsTotal.js +0 -2
- package/src/metrics/gatewayWebhooksReceivedTotal.js +0 -2
- package/src/metrics/gatewayWebhooksRejectedTotal.js +0 -2
- package/src/metrics/gatewayWebhooksVerifiedTotal.js +0 -2
- package/src/metrics/heartbeatDataSizeBytes.js +0 -5
- package/src/metrics/heartbeatIntervalMs.js +0 -3
- package/src/metrics/hotReloadDuration.js +0 -3
- package/src/metrics/hotReloadFailures.js +0 -2
- package/src/metrics/hotReloads.js +0 -2
- package/src/metrics/httpRequestDuration.js +0 -3
- package/src/metrics/httpRequests.js +0 -2
- package/src/metrics/index.d.ts +0 -1222
- package/src/metrics/index.js +0 -168
- package/src/metrics/memoryFactReads.js +0 -3
- package/src/metrics/memoryFactWrites.js +0 -3
- package/src/metrics/memoryMessageSaves.js +0 -3
- package/src/metrics/memoryRecallDuration.js +0 -11
- package/src/metrics/memoryRecallQueries.js +0 -3
- package/src/metrics/metricsServiceAdapter.js +0 -181
- package/src/metrics/nodeDuration.js +0 -3
- package/src/metrics/nodeRetriesTotal.js +0 -2
- package/src/metrics/nodesFailed.js +0 -2
- package/src/metrics/nodesFinished.js +0 -2
- package/src/metrics/nodesStarted.js +0 -2
- package/src/metrics/openApiToolCallErrorsTotal.js +0 -3
- package/src/metrics/openApiToolCallsTotal.js +0 -3
- package/src/metrics/openApiToolDuration.js +0 -9
- package/src/metrics/processHeapUsedBytes.js +0 -2
- package/src/metrics/processMemoryRssBytes.js +0 -2
- package/src/metrics/processUptimeSeconds.js +0 -2
- package/src/metrics/promptSizeBytes.js +0 -3
- package/src/metrics/replaysStarted.js +0 -3
- package/src/metrics/responseSizeBytes.js +0 -3
- package/src/metrics/rewindDurationMs.js +0 -3
- package/src/metrics/rewindFramesDeleted.js +0 -3
- package/src/metrics/rewindRollbackTotal.js +0 -2
- package/src/metrics/rewindSandboxesReverted.js +0 -3
- package/src/metrics/rewindTotal.js +0 -2
- package/src/metrics/runDuration.js +0 -3
- package/src/metrics/runForksCreated.js +0 -3
- package/src/metrics/runsAncestryDepth.js +0 -3
- package/src/metrics/runsCancelledTotal.js +0 -2
- package/src/metrics/runsCarriedStateBytes.js +0 -5
- package/src/metrics/runsContinuedTotal.js +0 -2
- package/src/metrics/runsFailedTotal.js +0 -2
- package/src/metrics/runsFinishedTotal.js +0 -2
- package/src/metrics/runsResumedTotal.js +0 -2
- package/src/metrics/runsTotal.js +0 -2
- package/src/metrics/sandboxActive.js +0 -2
- package/src/metrics/sandboxBundleSizeBytes.js +0 -5
- package/src/metrics/sandboxCompletedTotal.js +0 -2
- package/src/metrics/sandboxCreatedTotal.js +0 -2
- package/src/metrics/sandboxDurationMs.js +0 -3
- package/src/metrics/sandboxPatchCount.js +0 -3
- package/src/metrics/sandboxTransportDurationMs.js +0 -5
- package/src/metrics/schedulerConcurrencyUtilization.js +0 -2
- package/src/metrics/schedulerQueueDepth.js +0 -2
- package/src/metrics/schedulerWaitDuration.js +0 -5
- package/src/metrics/scorerDuration.js +0 -9
- package/src/metrics/scorerEventsFailed.js +0 -2
- package/src/metrics/scorerEventsFinished.js +0 -2
- package/src/metrics/scorerEventsStarted.js +0 -2
- package/src/metrics/scorersFailed.js +0 -3
- package/src/metrics/scorersFinished.js +0 -3
- package/src/metrics/scorersStarted.js +0 -3
- package/src/metrics/smithersMetricCatalog.js +0 -893
- package/src/metrics/smithersMetricCatalogByKey.js +0 -2
- package/src/metrics/smithersMetricCatalogByName.js +0 -2
- package/src/metrics/smithersMetricCatalogByPrometheusName.js +0 -4
- package/src/metrics/snapshotDuration.js +0 -9
- package/src/metrics/snapshotsCaptured.js +0 -3
- package/src/metrics/supervisorPollDuration.js +0 -5
- package/src/metrics/supervisorPollsTotal.js +0 -2
- package/src/metrics/supervisorResumeLag.js +0 -5
- package/src/metrics/supervisorResumedTotal.js +0 -2
- package/src/metrics/supervisorSkippedTotal.js +0 -2
- package/src/metrics/supervisorStaleDetected.js +0 -2
- package/src/metrics/taskHeartbeatTimeoutTotal.js +0 -2
- package/src/metrics/taskHeartbeatsTotal.js +0 -2
- package/src/metrics/timerDelayDuration.js +0 -3
- package/src/metrics/timersCancelled.js +0 -2
- package/src/metrics/timersCreated.js +0 -2
- package/src/metrics/timersFired.js +0 -2
- package/src/metrics/timersPending.js +0 -2
- package/src/metrics/toPrometheusMetricName.js +0 -8
- package/src/metrics/tokensCacheReadTotal.js +0 -2
- package/src/metrics/tokensCacheWriteTotal.js +0 -2
- package/src/metrics/tokensContextWindowBucketTotal.js +0 -2
- package/src/metrics/tokensContextWindowPerCall.js +0 -5
- package/src/metrics/tokensInputPerCall.js +0 -3
- package/src/metrics/tokensInputTotal.js +0 -2
- package/src/metrics/tokensOutputPerCall.js +0 -3
- package/src/metrics/tokensOutputTotal.js +0 -2
- package/src/metrics/tokensReasoningTotal.js +0 -2
- package/src/metrics/toolCallErrorsTotal.js +0 -2
- package/src/metrics/toolCallsTotal.js +0 -2
- package/src/metrics/toolDuration.js +0 -3
- package/src/metrics/toolOutputTruncatedTotal.js +0 -2
- package/src/metrics/trackEvent.js +0 -608
- package/src/metrics/updateAsyncExternalWaitPending.js +0 -18
- package/src/metrics/updateProcessMetrics.js +0 -20
- package/src/metrics/vcsDuration.js +0 -3
- package/src/normalizeStructuredEvent.js +0 -15
- package/src/prometheusContentType.js +0 -1
- package/src/renderPrometheusMetrics.js +0 -212
- package/src/resolveAgentTraceCapabilities.js +0 -59
- package/src/resolveSmithersObservabilityOptions.js +0 -109
- package/src/smithersMetrics.js +0 -2
- package/src/smithersSpanNames.js +0 -6
- package/src/unsupportedKindsForCapabilities.js +0 -32
- package/src/withSmithersSpan.js +0 -17
- package/tempo/tempo.yml +0 -17
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Wire an OpenTelemetry SDK"
|
|
3
|
+
description: "Use NodeOtel, BrowserOtel, or Otel when a collector endpoint is not enough: the Node OTLP/HTTP layer, browser processors you construct, and bridging providers an application already built."
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 2
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
[`Otlp`](./export-to-a-collector.md) covers a collector endpoint and nothing
|
|
9
|
+
else. Three builders exist for the cases it does not cover: existing
|
|
10
|
+
OpenTelemetry instrumentation you must feed, a vendor exporter that is not
|
|
11
|
+
OTLP/HTTP JSON, and a provider a framework hands you already constructed.
|
|
12
|
+
|
|
13
|
+
All three validate `Resource.Configuration`. `NodeOtel` and `BrowserOtel`
|
|
14
|
+
construct providers with that resource. `Otel` supplies it to provider factories
|
|
15
|
+
and metrics; already-created providers retain their own resource.
|
|
16
|
+
|
|
17
|
+
## Node: build the SDK for me
|
|
18
|
+
|
|
19
|
+
`NodeOtel.layerOtel` allocates the whole Node OpenTelemetry SDK: a
|
|
20
|
+
`BatchSpanProcessor`, a `BatchLogRecordProcessor`, and a
|
|
21
|
+
`PeriodicExportingMetricReader`, each behind its own OTLP/HTTP exporter.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import * as NodeOtel from "@smthrs/observability/NodeOtel"
|
|
25
|
+
import * as Effect from "effect/Effect"
|
|
26
|
+
|
|
27
|
+
const telemetry = NodeOtel.layerOtel({
|
|
28
|
+
endpoint: "http://localhost:4318",
|
|
29
|
+
resource: { serviceName: "deploy-status", serviceVersion: "2.4.1" },
|
|
30
|
+
exportIntervalMillis: 60_000,
|
|
31
|
+
shutdownTimeout: "10 seconds"
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const outcome = program.pipe(Effect.provide(telemetry), Effect.scoped)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The exporters are created when the scoped layer is built, not when the module
|
|
38
|
+
is imported, so holding the builder costs nothing. Closing the scope
|
|
39
|
+
force-flushes both batch processors and collects the metric reader once, which
|
|
40
|
+
makes release, rather than the interval, the deterministic flush.
|
|
41
|
+
|
|
42
|
+
The endpoint option is named `endpoint` here and `baseUrl` on `Otlp`; each
|
|
43
|
+
refusal names the option it arrived on, so the message points at your own
|
|
44
|
+
field.
|
|
45
|
+
|
|
46
|
+
`NodeOtel` takes no `headers` option. The OpenTelemetry exporters read the
|
|
47
|
+
standard `OTEL_EXPORTER_OTLP_HEADERS`, `_TIMEOUT`, `_COMPRESSION`, and TLS
|
|
48
|
+
certificate variables from the process environment and send them to your
|
|
49
|
+
explicit `endpoint`, which always wins over `OTEL_EXPORTER_OTLP_ENDPOINT`.
|
|
50
|
+
Set those variables only for this collector. `Otlp` reads none of them.
|
|
51
|
+
|
|
52
|
+
Import `NodeOtel` only from code that runs on Node. See
|
|
53
|
+
[the layer map](../concepts/layer-map.md) for why it is absent from the root
|
|
54
|
+
entry point.
|
|
55
|
+
|
|
56
|
+
## Browser: bring your own processors
|
|
57
|
+
|
|
58
|
+
`BrowserOtel.layerOtel` builds the web SDK around processors and readers the
|
|
59
|
+
application constructs. That keeps browser exporter policy, and every exporter
|
|
60
|
+
package a bundle would have to carry, in the application rather than in this
|
|
61
|
+
one.
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http"
|
|
65
|
+
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base"
|
|
66
|
+
import * as BrowserOtel from "@smthrs/observability/BrowserOtel"
|
|
67
|
+
|
|
68
|
+
const telemetry = BrowserOtel.layerOtel({
|
|
69
|
+
resource: { serviceName: "console-ui" },
|
|
70
|
+
spanProcessor: () =>
|
|
71
|
+
new BatchSpanProcessor(
|
|
72
|
+
new OTLPTraceExporter({ url: "https://otlp.example.com/v1/traces" })
|
|
73
|
+
)
|
|
74
|
+
})
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Every processor field is optional, and a layer with none still builds: it
|
|
78
|
+
provides the resource and bridges nothing. Return an array to install several
|
|
79
|
+
processors for one signal. Each field is a factory because the layer owns what
|
|
80
|
+
it returns: the factory runs once at acquisition, and the web SDK shuts down
|
|
81
|
+
every processor and reader when the scope closes.
|
|
82
|
+
|
|
83
|
+
If all you need in a browser is OTLP delivery, use `Otlp.layerFetch` instead.
|
|
84
|
+
It is smaller, needs no SDK, and is browser-safe by construction.
|
|
85
|
+
|
|
86
|
+
## Bridge providers you control
|
|
87
|
+
|
|
88
|
+
`Otel.layerOtel` allocates no exporter at all. It takes an OpenTelemetry
|
|
89
|
+
`TracerProvider` and `LoggerProvider`, plus a factory that builds the metric
|
|
90
|
+
readers, and bridges Effect's tracer, logger, and metrics onto them. Provider
|
|
91
|
+
fields also accept synchronous factories. Every factory runs during layer
|
|
92
|
+
acquisition, after resource validation, and the provider factories receive the
|
|
93
|
+
same resource as the metric producer. Pass it to the SDK constructor to export
|
|
94
|
+
all signals with the configured service identity:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import { LoggerProvider } from "@opentelemetry/sdk-logs"
|
|
98
|
+
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics"
|
|
99
|
+
import { BasicTracerProvider } from "@opentelemetry/sdk-trace-base"
|
|
100
|
+
import * as Otel from "@smthrs/observability/Otel"
|
|
101
|
+
import * as Effect from "effect/Effect"
|
|
102
|
+
|
|
103
|
+
let tracerProvider: BasicTracerProvider | undefined
|
|
104
|
+
let loggerProvider: LoggerProvider | undefined
|
|
105
|
+
const telemetry = Otel.layerOtel({
|
|
106
|
+
resource: {
|
|
107
|
+
serviceName: "deploy-status",
|
|
108
|
+
serviceVersion: "2.4.1",
|
|
109
|
+
attributes: { region: "us-west" }
|
|
110
|
+
},
|
|
111
|
+
tracerProvider: (resource) =>
|
|
112
|
+
tracerProvider = new BasicTracerProvider({
|
|
113
|
+
resource,
|
|
114
|
+
spanProcessors: [spanProcessor]
|
|
115
|
+
}),
|
|
116
|
+
loggerProvider: (resource) =>
|
|
117
|
+
loggerProvider = new LoggerProvider({
|
|
118
|
+
resource,
|
|
119
|
+
processors: [logRecordProcessor]
|
|
120
|
+
}),
|
|
121
|
+
metricReader: () => new PeriodicExportingMetricReader({ exporter: metricExporter }),
|
|
122
|
+
metricTemporality: "cumulative"
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
await Effect.runPromise(program.pipe(Effect.provide(telemetry), Effect.scoped))
|
|
127
|
+
} finally {
|
|
128
|
+
await Promise.all([tracerProvider?.shutdown(), loggerProvider?.shutdown()])
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Ownership splits between the two kinds of field. Providers are borrowed: the
|
|
133
|
+
application owns their flushing and shutdown, including providers returned by
|
|
134
|
+
factories, and this layer never shuts one down. Readers are owned: the
|
|
135
|
+
`metricReader` factory runs once during acquisition, and closing the scope shuts
|
|
136
|
+
down every reader it returned. Build the readers inside the factory rather than
|
|
137
|
+
handing over one a longer-lived composition still exports through, which that
|
|
138
|
+
shutdown would disable. Construct `spanProcessor` and `logRecordProcessor` with
|
|
139
|
+
your exporters and give them to the provider you build.
|
|
140
|
+
|
|
141
|
+
You can still pass existing providers directly. The layer cannot change the
|
|
142
|
+
resource they captured at construction. In that form, the resource option
|
|
143
|
+
controls only the metric resource, not log or span resource attributes.
|
|
144
|
+
Whichever form you use, spans and log records carry the instrumentation scope
|
|
145
|
+
`effect` with the Effect version, and the span status and log attributes
|
|
146
|
+
described in
|
|
147
|
+
[What the collector receives](./export-to-a-collector.md#what-the-collector-receives). Construct both providers with the intended resource before injection,
|
|
148
|
+
or use the factories above to receive the validated resource.
|
|
149
|
+
|
|
150
|
+
Each of the three is optional and each is bridged only when supplied, so a
|
|
151
|
+
composition with a tracer and no meter installs only the tracer bridge. A
|
|
152
|
+
`metricReader` factory that returns an empty array registers no reader at all
|
|
153
|
+
rather than a misconfigured one.
|
|
154
|
+
|
|
155
|
+
Two options control merge behavior: `loggerMergeWithExisting` keeps the ambient
|
|
156
|
+
Effect loggers alongside the OpenTelemetry logger, and `metricTemporality`
|
|
157
|
+
selects the temporality preference the metric bridge reports.
|
|
158
|
+
|
|
159
|
+
`Otel.layerNoop` is the explicit empty slot for a composition that wants the
|
|
160
|
+
option without the wiring.
|
|
161
|
+
|
|
162
|
+
## Provide only the resource
|
|
163
|
+
|
|
164
|
+
`Resource.layer` provides the validated OpenTelemetry resource by itself, for a
|
|
165
|
+
composition that assembles the rest of the SDK on its own:
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import * as Resource from "@smthrs/observability/Resource"
|
|
169
|
+
|
|
170
|
+
const resource = Resource.layer({
|
|
171
|
+
serviceName: "deploy-status",
|
|
172
|
+
attributes: { "deployment.environment.name": "production" }
|
|
173
|
+
})
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`Resource.configToAttributes` is the pure projection of the same configuration
|
|
177
|
+
into OpenTelemetry attributes, for code that needs the attribute record rather
|
|
178
|
+
than a layer. It reads no environment variables: attributes come from your
|
|
179
|
+
configuration and the SDK's `telemetry.sdk.name`, `telemetry.sdk.language` and
|
|
180
|
+
`telemetry.sdk.version` fields, which are included in the admission budget. An
|
|
181
|
+
attribute of yours with one of those three names replaces the SDK's value.
|
|
182
|
+
|
|
183
|
+
## Default OTLP export limits
|
|
184
|
+
|
|
185
|
+
`Otlp.layer` and `Otlp.layerFetch` share a limit of four active HTTP requests
|
|
186
|
+
across logs, traces, and metrics within each layer acquisition. Each request has
|
|
187
|
+
a ten-second timeout that interrupts the transport and aborts a stalled fetch.
|
|
188
|
+
A request ends when the collector's status arrives: the response body is not
|
|
189
|
+
read, so a collector that never finishes its answer holds no slot.
|
|
190
|
+
`shutdownTimeout` separately bounds the final scope flush.
|
|
191
|
+
|
|
192
|
+
Logs and traces flush at 1,000 records or the export interval. The transport has
|
|
193
|
+
no waiting queue: a batch arriving while all four slots are occupied is dropped.
|
|
194
|
+
Serialized JSON payloads larger than 1 MiB are also dropped, limiting active
|
|
195
|
+
request payloads to 4 MiB. This limit applies after serialization; it does not
|
|
196
|
+
bound individual application records, temporary serialization allocations, or
|
|
197
|
+
the application's metric registry.
|
|
198
|
+
|
|
199
|
+
Resource admission caps cumulative encoded metadata at 128 KiB and bounds
|
|
200
|
+
arrays at 256 elements. The byte budget includes SDK-added telemetry fields,
|
|
201
|
+
reserved even for the default Effect exporter. Both the default exporter and
|
|
202
|
+
the Node SDK layer isolate ambient resource configuration after validation.
|
|
203
|
+
The default exporter also budgets the request envelope
|
|
204
|
+
at 8 KiB and reserves `Otlp.reservedBatchBytes` (888 KiB) for signals: roughly
|
|
205
|
+
900 bytes per record
|
|
206
|
+
in a 1,000-record log or span batch. Every layer validates these resource limits
|
|
207
|
+
before allocating exporters, including injected SDK layers.
|
|
208
|
+
|
|
209
|
+
The Effect counter `flows_observability_otlp_dropped` increments once per batch
|
|
210
|
+
discarded for saturation, payload size, or timeout. Discards are terminal and
|
|
211
|
+
are not retried. Read the counter locally
|
|
212
|
+
with `Metric.value(Metric.counter("flows_observability_otlp_dropped"))` during an
|
|
213
|
+
outage. Its exported value becomes available when the collector recovers.
|
|
214
|
+
|
|
215
|
+
Because that counter shares the export path, each acquisition also emits at
|
|
216
|
+
most one warning every 60 seconds through the ambient loggers captured before
|
|
217
|
+
installing the OTLP logger. The warning has code `otlp_export_discarded`, reason
|
|
218
|
+
`oversized`, `saturated`, or `stalled`, encoded bytes, request limit, and total
|
|
219
|
+
discarded batches. It includes no payload, URL, or inherited annotations and
|
|
220
|
+
cannot re-enter this exporter even during an application-triggered flush. Keep
|
|
221
|
+
an ambient logger installed if loss diagnostics should remain visible during
|
|
222
|
+
an export outage.
|
|
223
|
+
|
|
224
|
+
Ordinary HTTP and network failures retain Effect's retry policy. These limits
|
|
225
|
+
apply to the default `Otlp` layers; SDK processors supplied to the other builders
|
|
226
|
+
retain their own export policies.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Installation"
|
|
3
|
+
description: "Install @smthrs/observability, its runtime requirements and Effect version, its import forms, and the rule that keeps the root entry point bundling for a browser."
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 1
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Install the package
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @smthrs/observability@next effect
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The `next` tag carries the 1.0 release candidates.
|
|
15
|
+
|
|
16
|
+
The package requires Node.js 26.4.0 or later and ships as both ESM and
|
|
17
|
+
CommonJS with TypeScript declarations.
|
|
18
|
+
|
|
19
|
+
## Install one Effect release
|
|
20
|
+
|
|
21
|
+
Effect services are identified by module identity, so a composition that
|
|
22
|
+
resolves two copies of `effect` splits its context and a provided layer stops
|
|
23
|
+
satisfying a requirement. Install one `effect` in the range `^4.0.4`.
|
|
24
|
+
|
|
25
|
+
`Otlp` is written entirely against `effect`, including its HTTP client and its
|
|
26
|
+
`effect/observability/Otlp` exporters. The `@effect/opentelemetry@4.0.4`
|
|
27
|
+
dependency, the required `@opentelemetry/api@1.9.1` peer, and the logs and
|
|
28
|
+
metrics dependencies support the root `Otel` and `Resource` modules. They
|
|
29
|
+
install with ordinary peer-aware package managers. `@effect/opentelemetry`
|
|
30
|
+
4.0.4 accepts any `effect` in `^4.0.4`. The default install includes no HTTP exporters or trace SDK.
|
|
31
|
+
|
|
32
|
+
`NodeOtel` selects the optional Node trace SDK and three HTTP exporters:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pnpm add @opentelemetry/exporter-logs-otlp-http@0.222.0 @opentelemetry/exporter-metrics-otlp-http@0.222.0 @opentelemetry/exporter-trace-otlp-http@0.222.0 @opentelemetry/sdk-trace-base@2.11.0 @opentelemetry/sdk-trace-node@2.11.0
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`BrowserOtel` selects the optional browser trace SDK instead:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pnpm add @opentelemetry/sdk-trace-base@2.11.0 @opentelemetry/sdk-trace-web@2.11.0
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The browser adapter accepts the exporters you supply; those HTTP exporter
|
|
45
|
+
packages are not prerequisites for constructing its layer.
|
|
46
|
+
|
|
47
|
+
## Import forms
|
|
48
|
+
|
|
49
|
+
The root entry point re-exports seven modules as namespaces:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { Endpoint, JournalLogger, Logger, Metric, Otel, Otlp, Resource } from "@smthrs/observability"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Each is also importable from its own subpath, which is the form the
|
|
56
|
+
[API reference](./api.md) uses:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import * as JournalLogger from "@smthrs/observability/JournalLogger"
|
|
60
|
+
import * as Otlp from "@smthrs/observability/Otlp"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`@smthrs/observability/package.json` is exported.
|
|
64
|
+
|
|
65
|
+
## The two subpath-only modules
|
|
66
|
+
|
|
67
|
+
`NodeOtel` and `BrowserOtel` are not re-exported from the root, and importing
|
|
68
|
+
them by subpath is the only way to reach them:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import * as BrowserOtel from "@smthrs/observability/BrowserOtel"
|
|
72
|
+
import * as NodeOtel from "@smthrs/observability/NodeOtel"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`NodeOtel` resolves Node-only host modules: `@effect/opentelemetry/NodeSdk` and
|
|
76
|
+
`@opentelemetry/sdk-trace-node`, which reaches the bare `async_hooks` specifier
|
|
77
|
+
through `@opentelemetry/context-async-hooks`. Re-exporting it from the root
|
|
78
|
+
would put a module a browser bundler cannot resolve in the entry point, and
|
|
79
|
+
break the browser bundle the root guarantees. Nothing the root entry point
|
|
80
|
+
reaches resolves a `node:` built-in, so it bundles for a browser as it is.
|
|
81
|
+
|
|
82
|
+
Import `NodeOtel` only from code that runs on Node, and `BrowserOtel` only from
|
|
83
|
+
code that runs in a browser. `Otlp` is safe in both, because it exports over
|
|
84
|
+
Effect's `HttpClient` and never touches a `node:` built-in.
|
|
85
|
+
|
|
86
|
+
## What a real composition adds
|
|
87
|
+
|
|
88
|
+
The exporter alone needs nothing else. Two features have a dependency:
|
|
89
|
+
|
|
90
|
+
- `Otlp.layer` requires an Effect `HttpClient` from
|
|
91
|
+
`effect/http/HttpClient`. `Otlp.layerFetch` supplies the host's
|
|
92
|
+
global `fetch` and is the usual choice; a Node host that wants Undici instead
|
|
93
|
+
provides `NodeHttpClient.layerUndici`, re-exported by
|
|
94
|
+
[`@smthrs/platform-node`](/api/platform-node) as `NodeHost.NodeHttpClient`.
|
|
95
|
+
- `JournalLogger.layerJournalForwarding` requires a `Journal.Journal` service
|
|
96
|
+
from [`@smthrs/journal`](/api/journal). A durable engine composition already
|
|
97
|
+
provides one.
|
|
98
|
+
|
|
99
|
+
## Next step
|
|
100
|
+
|
|
101
|
+
Export your first trace, log record, and metric series in the
|
|
102
|
+
[Quickstart](./quickstart.md).
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Quickstart"
|
|
3
|
+
description: "Export a trace, a log record, and a metric series to a collector you can watch, using a thirty-line collector stand-in and one Effect layer."
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 2
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
This quickstart exports all three signals from one small program and shows you
|
|
9
|
+
what arrived. It needs no Docker, no vendor account, and no OpenTelemetry SDK:
|
|
10
|
+
a twelve-line Node server stands in for the collector so you can read the
|
|
11
|
+
delivery with your own eyes, and swapping in a real collector is a one-line
|
|
12
|
+
change at the end.
|
|
13
|
+
|
|
14
|
+
## Prerequisites
|
|
15
|
+
|
|
16
|
+
- Node.js 26.4.0 or later, which runs a `.ts` file directly by stripping its
|
|
17
|
+
types.
|
|
18
|
+
- A package whose `package.json` sets `"type": "module"`, with the
|
|
19
|
+
dependencies installed: `npm install @smthrs/observability@next effect`.
|
|
20
|
+
|
|
21
|
+
## Start a collector stand-in
|
|
22
|
+
|
|
23
|
+
An OTLP/HTTP collector is an HTTP server that accepts JSON on three paths.
|
|
24
|
+
Create `collector.mjs`:
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
import { createServer } from "node:http"
|
|
28
|
+
|
|
29
|
+
createServer((request, response) => {
|
|
30
|
+
const chunks = []
|
|
31
|
+
request.on("data", (chunk) => chunks.push(chunk))
|
|
32
|
+
request.on("end", () => {
|
|
33
|
+
const body = JSON.parse(Buffer.concat(chunks).toString("utf8"))
|
|
34
|
+
console.log(request.url, Object.keys(body).join(" "))
|
|
35
|
+
response.writeHead(200, { "content-type": "application/json" })
|
|
36
|
+
response.end("{}")
|
|
37
|
+
})
|
|
38
|
+
}).listen(4318, "127.0.0.1")
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Run it in its own terminal, and leave it running:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
node collector.mjs
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Write the program
|
|
48
|
+
|
|
49
|
+
Create `quickstart.ts`. The program opens a span, writes a log record, and
|
|
50
|
+
advances a counter. None of those three lines mentions telemetry: they are
|
|
51
|
+
ordinary Effect, and they are exactly what the Smithers packages already do on
|
|
52
|
+
their own hot paths.
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import * as Otlp from "@smthrs/observability/Otlp"
|
|
56
|
+
import * as Effect from "effect/Effect"
|
|
57
|
+
import * as Metric from "effect/Metric"
|
|
58
|
+
|
|
59
|
+
const deployments = Metric.counter("quickstart/deployments", {
|
|
60
|
+
description: "Deployments this program recorded"
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
const deploy = Effect.gen(function*() {
|
|
64
|
+
yield* Effect.logInfo("deploying the API")
|
|
65
|
+
yield* Metric.update(deployments, 1)
|
|
66
|
+
}).pipe(Effect.withSpan("deploy"))
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Now add the exporter and run it. `layerFetch` posts over the host's global
|
|
70
|
+
`fetch`, so there is nothing else to provide:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
const telemetry = Otlp.layerFetch({
|
|
74
|
+
baseUrl: "http://localhost:4318",
|
|
75
|
+
serviceName: "quickstart",
|
|
76
|
+
serviceVersion: "1.0.0"
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
await Effect.runPromise(
|
|
80
|
+
deploy.pipe(Effect.provide(telemetry), Effect.orDie, Effect.scoped)
|
|
81
|
+
)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`Effect.orDie` says that an unusable collector endpoint is a startup defect
|
|
85
|
+
rather than something this program recovers from. The layer is scoped, so
|
|
86
|
+
`Effect.scoped` gives it a lifetime, and closing that scope is what flushes.
|
|
87
|
+
|
|
88
|
+
## Run it
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
node quickstart.ts
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The collector terminal prints one line per signal. The three arrive in
|
|
95
|
+
whatever order the exporters flush:
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
/v1/traces resourceSpans
|
|
99
|
+
/v1/logs resourceLogs
|
|
100
|
+
/v1/metrics resourceMetrics
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
If you print a whole body instead of its top-level key, every payload carries
|
|
104
|
+
the resource you named: `service.name` is `quickstart` and `service.version` is
|
|
105
|
+
`1.0.0`.
|
|
106
|
+
|
|
107
|
+
## What just happened
|
|
108
|
+
|
|
109
|
+
One layer installed three of Effect's own OTLP exporters against your base URL.
|
|
110
|
+
Each signal posts below it on its own path, so `http://localhost:4318` becomes
|
|
111
|
+
`/v1/traces`, `/v1/logs`, and `/v1/metrics`. The exporters batch, so nothing
|
|
112
|
+
was delivered while the program ran; closing the layer's scope force-flushed
|
|
113
|
+
all three, which is why `Effect.scoped` is where the output comes from.
|
|
114
|
+
|
|
115
|
+
The program never mentioned a tracer, a meter, or a logger provider. Effect's
|
|
116
|
+
own tracer and metric registry were already collecting; the layer is only the
|
|
117
|
+
delivery.
|
|
118
|
+
|
|
119
|
+
## Point it at a real collector
|
|
120
|
+
|
|
121
|
+
Change one field:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
const telemetry = Otlp.layerFetch({
|
|
125
|
+
baseUrl: "https://otlp.example.com",
|
|
126
|
+
serviceName: "quickstart",
|
|
127
|
+
headers: { authorization: "Bearer YOUR_TOKEN" }
|
|
128
|
+
})
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Replace `YOUR_TOKEN` with the token your vendor issued. The endpoint must be an
|
|
132
|
+
absolute `http:` or `https:` URL carrying no credentials; anything else refuses
|
|
133
|
+
the layer at acquisition rather than failing silently later. See
|
|
134
|
+
[Export to an OTLP collector](./guides/export-to-a-collector.md) for the rest
|
|
135
|
+
of the options.
|
|
136
|
+
|
|
137
|
+
## Next steps
|
|
138
|
+
|
|
139
|
+
- [Export to an OTLP collector](./guides/export-to-a-collector.md): service
|
|
140
|
+
identity, authentication headers, export cadence, and what happens when a
|
|
141
|
+
collector is down.
|
|
142
|
+
- [Instrumentation and export](./concepts/instrumentation-and-export.md): why
|
|
143
|
+
the exporter is one layer and what is already instrumented without it.
|
|
144
|
+
- [Forward logs to the run journal](./guides/forward-logs-to-the-journal.md):
|
|
145
|
+
the second delivery path, durable and readable in process.
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Troubleshooting"
|
|
3
|
+
description: "The typed refusals @smthrs/observability reports and the silent failures it cannot: nothing arriving at a collector, a bundler that cannot resolve async_hooks, logs that stopped, and a counter that reads zero."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Two kinds of problem show up here. A typed refusal names its own cause and
|
|
7
|
+
happens while the layer is built. A silent failure is the harder one, because
|
|
8
|
+
export is absorbed by design and a broken exporter looks exactly like a working
|
|
9
|
+
one.
|
|
10
|
+
|
|
11
|
+
## InvalidExporterEndpoint
|
|
12
|
+
|
|
13
|
+
**What happened.** A builder was handed a collector endpoint it will not
|
|
14
|
+
export to. The failure carries `code` `invalid_exporter_endpoint`, the `path`
|
|
15
|
+
of the option it arrived on (`baseUrl` for `Otlp`, `endpoint` for `NodeOtel`),
|
|
16
|
+
and a message. The rejected value is not in the error.
|
|
17
|
+
|
|
18
|
+
**What to change.** Pass an absolute `http:` or `https:` URL of at most 2,048
|
|
19
|
+
characters that carries no username or password and no spaces or control
|
|
20
|
+
characters. The usual culprits, in order:
|
|
21
|
+
|
|
22
|
+
- A trailing newline off a config file, or a leading space off a paste. Trim
|
|
23
|
+
the value. This one is refused rather than repaired precisely because the
|
|
24
|
+
URL parser would repair it into a URL nobody typed.
|
|
25
|
+
- A missing scheme: `collector:4318` instead of `http://collector:4318`.
|
|
26
|
+
- A signal path already appended. Pass the base URL; the builder adds
|
|
27
|
+
`/v1/traces`, `/v1/metrics`, and `/v1/logs`.
|
|
28
|
+
- Credentials in the URL. Move them to the `headers` option.
|
|
29
|
+
|
|
30
|
+
## InvalidResourceConfiguration
|
|
31
|
+
|
|
32
|
+
**What happened.** The service identity or the resource attributes failed
|
|
33
|
+
decoding. The failure carries `code` `invalid_resource_configuration` and a
|
|
34
|
+
`path` naming the offending field, such as `serviceName` or an attribute key.
|
|
35
|
+
|
|
36
|
+
**What to change.** Check the value against the bounds: a non-empty service
|
|
37
|
+
name and version of at most 1,024 UTF-16 code units, at most 256 attributes,
|
|
38
|
+
attribute values that are strings of at most 65,536 code units, finite numbers,
|
|
39
|
+
booleans, or homogeneous arrays of those with at most 256 elements. The complete
|
|
40
|
+
resource must fit 128 KiB of OTLP JSON, including keys, identity, the SDK-added
|
|
41
|
+
`telemetry.sdk.name`, `telemetry.sdk.language` and `telemetry.sdk.version`
|
|
42
|
+
fields, wrappers, escaping and UTF-8. Many individually valid values can exceed this budget;
|
|
43
|
+
remove or shorten attributes. The refusal names `attributes`, the measured
|
|
44
|
+
lower bound in bytes and the limit, without retaining values. NUL and unpaired surrogates are
|
|
45
|
+
refused anywhere. An empty `serviceName` is the common one, usually an
|
|
46
|
+
environment variable that was not set.
|
|
47
|
+
|
|
48
|
+
## InvalidJournalLoggerOptions
|
|
49
|
+
|
|
50
|
+
**What happened.** `JournalLogger.layerJournalForwarding` was given a run id or
|
|
51
|
+
a capacity it will not start a worker for. The failure carries `code`
|
|
52
|
+
`invalid_journal_logger_options` and the offending `path`.
|
|
53
|
+
|
|
54
|
+
**What to change.** Decode the run id through `JournalEvent.RunId` rather than
|
|
55
|
+
casting a string, and keep `capacity` between 1 and 65,536. The refusal happens
|
|
56
|
+
before any record is queued, so nothing was silently dropped.
|
|
57
|
+
|
|
58
|
+
## Nothing arrives at the collector
|
|
59
|
+
|
|
60
|
+
**What happened.** The layer built, the program ran, and the collector holds
|
|
61
|
+
nothing. Export failure is absorbed by design, so this is not reported anywhere
|
|
62
|
+
by default.
|
|
63
|
+
|
|
64
|
+
**What to check, in order.**
|
|
65
|
+
|
|
66
|
+
1. **Did the scope close?** Exporters batch. A short program delivers on the
|
|
67
|
+
shutdown flush, so read the collector only after `Effect.scoped` completes.
|
|
68
|
+
A program killed with `SIGKILL` never flushes.
|
|
69
|
+
2. **Is the base URL what you think?** Signals post below it, so
|
|
70
|
+
`http://collector:4318` posts to `http://collector:4318/v1/traces`. If your
|
|
71
|
+
collector expects a prefix, include it in `baseUrl`.
|
|
72
|
+
3. **Is the collector answering?** Run at `Debug`. The exporter logs
|
|
73
|
+
`Disabling exporter for 60 seconds` when it gives up after three retries.
|
|
74
|
+
4. **Is the layer actually provided?** A composition that swapped in
|
|
75
|
+
`Otlp.layerNoop` behaves exactly like a healthy one that delivers nothing.
|
|
76
|
+
|
|
77
|
+
## An OTLP export batch was discarded
|
|
78
|
+
|
|
79
|
+
A `Warn` record with code `otlp_export_discarded` means the transport discarded
|
|
80
|
+
a batch because it exceeded 1 MiB (`oversized`), stalled for ten seconds
|
|
81
|
+
(`stalled`), or found all four request slots occupied (`saturated`). The
|
|
82
|
+
`flows_observability_otlp_dropped` counter records every discarded batch. The
|
|
83
|
+
warning carries the request byte size, limit and running drop count, and is
|
|
84
|
+
limited to one per minute per transport.
|
|
85
|
+
|
|
86
|
+
Warnings go only to the loggers captured before exporter acquisition, without
|
|
87
|
+
logging into this exporter. Keep an ambient sink outside OTLP at level `Warn`
|
|
88
|
+
or lower: it remains useful even when the drop counter cannot reach the
|
|
89
|
+
collector. An empty ambient logger set or a higher minimum level suppresses
|
|
90
|
+
warnings. Reduce unusually large records or metric cardinality for oversized
|
|
91
|
+
batches; check collector availability and latency for stalls and saturation.
|
|
92
|
+
The resource budget reserves 888 KiB for ordinary batches, but cannot bound
|
|
93
|
+
application record sizes.
|
|
94
|
+
|
|
95
|
+
## Log records stopped reaching the collector
|
|
96
|
+
|
|
97
|
+
**What happened.** Spans and metric series still arrive, and logs do not, after
|
|
98
|
+
a `Logger` layer was added.
|
|
99
|
+
|
|
100
|
+
**What to change.** `Otlp` adds its log exporter to the ambient logger set, and
|
|
101
|
+
a `Logger` layer with the default `mergeWithExisting: false` replaces that set.
|
|
102
|
+
Whether the exporter survives depends on which layer the composition builds
|
|
103
|
+
last, which is not worth reasoning about: pass `mergeWithExisting: true` on the
|
|
104
|
+
`Logger` layer whenever another sink is installed. See
|
|
105
|
+
[Install a logger](./guides/install-a-logger.md).
|
|
106
|
+
|
|
107
|
+
## A bundler cannot resolve `async_hooks`
|
|
108
|
+
|
|
109
|
+
**What happened.** A browser or edge build failed on `async_hooks`,
|
|
110
|
+
`node:async_hooks`, or a module under `@opentelemetry/context-async-hooks`.
|
|
111
|
+
|
|
112
|
+
**What to change.** Something in the graph imports
|
|
113
|
+
`@smthrs/observability/NodeOtel`, directly or through a module that does.
|
|
114
|
+
`NodeOtel` binds the Node OpenTelemetry SDK and cannot bundle for a browser.
|
|
115
|
+
Use `Otlp.layerFetch` for browser delivery, or `BrowserOtel` when you need the
|
|
116
|
+
web SDK, and keep the `NodeOtel` import behind a Node-only entry point. The
|
|
117
|
+
root entry point itself resolves no `node:` built-in and bundles for a browser
|
|
118
|
+
as it is.
|
|
119
|
+
|
|
120
|
+
## `Metric.value` reads zero
|
|
121
|
+
|
|
122
|
+
**What happened.** The system is clearly doing work, and a counter reads zero.
|
|
123
|
+
|
|
124
|
+
**What to check.**
|
|
125
|
+
|
|
126
|
+
- **A dimensioned counter read through its bare handle.** Some packages update
|
|
127
|
+
only an attribute-tagged series, so the attribute-less parent stays at zero.
|
|
128
|
+
Read the exported view, for example `EngineStoreMetrics.dispatch.Success`
|
|
129
|
+
from [`@smthrs/engine-store`](/api/engine-store).
|
|
130
|
+
- **A registry provided per test.** Providing `Metric.MetricRegistry` with a
|
|
131
|
+
fresh map isolates state; a read outside that scope sees a different
|
|
132
|
+
registry.
|
|
133
|
+
- **A gauge read as a counter.** A gauge's value is `value`, a counter's is
|
|
134
|
+
`count`.
|
|
135
|
+
|
|
136
|
+
## `droppedLogRecords` keeps climbing
|
|
137
|
+
|
|
138
|
+
**What happened.** `flows_observability_log_dropped` advances during a run,
|
|
139
|
+
which means log records are not reaching the journal.
|
|
140
|
+
|
|
141
|
+
**What to change.** The counter advances for three distinct losses, and the
|
|
142
|
+
last two also emit a warning annotated with the run id:
|
|
143
|
+
|
|
144
|
+
- **Queue overflow**, with no warning. Raise `capacity` above the default 256,
|
|
145
|
+
up to 65,536.
|
|
146
|
+
- **A journal delivery failure.** Read the warning; the journal itself is the
|
|
147
|
+
problem, not the logger.
|
|
148
|
+
- **A defect from the journal implementation.** Read the warning. The worker
|
|
149
|
+
keeps draining, so the run continues with gaps.
|
|
150
|
+
|
|
151
|
+
Records queued behind an in-flight write can also be dropped when the layer's
|
|
152
|
+
scope closes, and the forwarder exposes no drain barrier. `journal.flush`
|
|
153
|
+
settles only the entries the journal has already admitted, so it returns while
|
|
154
|
+
a record is still waiting in the forwarder's queue. Poll the journal for the
|
|
155
|
+
records you expect while the layer's scope is open, as
|
|
156
|
+
[Forward logs to the run journal](./guides/forward-logs-to-the-journal.md#assert-on-a-short-lived-run)
|
|
157
|
+
shows.
|
|
158
|
+
|
|
159
|
+
## Reading a refusal's path
|
|
160
|
+
|
|
161
|
+
`path` is a dotted route into the value that failed, built from the decoder's
|
|
162
|
+
own issue path. `serviceName` names the identity field;
|
|
163
|
+
`attributes.region` names the `region` entry inside the `attributes` record. When the decoder cannot attribute the failure to a field,
|
|
164
|
+
the path is the bare `resource`, which means the whole configuration was
|
|
165
|
+
malformed: `null`, or not an object at all.
|
|
166
|
+
|
|
167
|
+
Neither refusal carries the rejected value, so pair the path with the value you
|
|
168
|
+
passed on that field.
|