@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.
Files changed (363) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/LICENSE +1 -1
  3. package/README.md +87 -0
  4. package/dist/cjs/BrowserOtel.d.ts +37 -0
  5. package/dist/cjs/BrowserOtel.d.ts.map +1 -0
  6. package/dist/cjs/BrowserOtel.js +47 -0
  7. package/dist/cjs/BrowserOtel.js.map +7 -0
  8. package/dist/cjs/Endpoint.d.ts +70 -0
  9. package/dist/cjs/Endpoint.d.ts.map +1 -0
  10. package/dist/cjs/Endpoint.js +85 -0
  11. package/dist/cjs/Endpoint.js.map +7 -0
  12. package/dist/cjs/JournalLogger.d.ts +232 -0
  13. package/dist/cjs/JournalLogger.d.ts.map +1 -0
  14. package/dist/cjs/JournalLogger.js +415 -0
  15. package/dist/cjs/JournalLogger.js.map +7 -0
  16. package/dist/cjs/Logger.d.ts +56 -0
  17. package/dist/cjs/Logger.d.ts.map +1 -0
  18. package/dist/cjs/Logger.js +72 -0
  19. package/dist/cjs/Logger.js.map +7 -0
  20. package/dist/cjs/Metric.d.ts +67 -0
  21. package/dist/cjs/Metric.d.ts.map +1 -0
  22. package/dist/cjs/Metric.js +68 -0
  23. package/dist/cjs/Metric.js.map +7 -0
  24. package/dist/cjs/NodeOtel.d.ts +42 -0
  25. package/dist/cjs/NodeOtel.d.ts.map +1 -0
  26. package/dist/cjs/NodeOtel.js +78 -0
  27. package/dist/cjs/NodeOtel.js.map +7 -0
  28. package/dist/cjs/Otel.d.ts +63 -0
  29. package/dist/cjs/Otel.d.ts.map +1 -0
  30. package/dist/cjs/Otel.js +75 -0
  31. package/dist/cjs/Otel.js.map +7 -0
  32. package/dist/cjs/Otlp.d.ts +203 -0
  33. package/dist/cjs/Otlp.d.ts.map +1 -0
  34. package/dist/cjs/Otlp.js +251 -0
  35. package/dist/cjs/Otlp.js.map +7 -0
  36. package/dist/cjs/Resource.d.ts +163 -0
  37. package/dist/cjs/Resource.d.ts.map +1 -0
  38. package/dist/cjs/Resource.js +186 -0
  39. package/dist/cjs/Resource.js.map +7 -0
  40. package/dist/cjs/index.d.ts +61 -0
  41. package/dist/cjs/index.d.ts.map +1 -0
  42. package/dist/cjs/index.js +47 -0
  43. package/dist/cjs/index.js.map +7 -0
  44. package/dist/cjs/internal/schemaIssuePath.d.ts +31 -0
  45. package/dist/cjs/internal/schemaIssuePath.d.ts.map +1 -0
  46. package/dist/cjs/internal/schemaIssuePath.js +49 -0
  47. package/dist/cjs/internal/schemaIssuePath.js.map +7 -0
  48. package/dist/cjs/package.json +1 -0
  49. package/dist/esm/BrowserOtel.d.ts +37 -0
  50. package/dist/esm/BrowserOtel.d.ts.map +1 -0
  51. package/dist/esm/BrowserOtel.js +24 -0
  52. package/dist/esm/BrowserOtel.js.map +1 -0
  53. package/dist/esm/Endpoint.d.ts +70 -0
  54. package/dist/esm/Endpoint.d.ts.map +1 -0
  55. package/dist/esm/Endpoint.js +110 -0
  56. package/dist/esm/Endpoint.js.map +1 -0
  57. package/dist/esm/JournalLogger.d.ts +232 -0
  58. package/dist/esm/JournalLogger.d.ts.map +1 -0
  59. package/dist/esm/JournalLogger.js +520 -0
  60. package/dist/esm/JournalLogger.js.map +1 -0
  61. package/dist/esm/Logger.d.ts +56 -0
  62. package/dist/esm/Logger.d.ts.map +1 -0
  63. package/dist/esm/Logger.js +61 -0
  64. package/dist/esm/Logger.js.map +1 -0
  65. package/dist/esm/Metric.d.ts +67 -0
  66. package/dist/esm/Metric.d.ts.map +1 -0
  67. package/dist/esm/Metric.js +80 -0
  68. package/dist/esm/Metric.js.map +1 -0
  69. package/dist/esm/NodeOtel.d.ts +42 -0
  70. package/dist/esm/NodeOtel.d.ts.map +1 -0
  71. package/dist/esm/NodeOtel.js +56 -0
  72. package/dist/esm/NodeOtel.js.map +1 -0
  73. package/dist/esm/Otel.d.ts +63 -0
  74. package/dist/esm/Otel.d.ts.map +1 -0
  75. package/dist/esm/Otel.js +52 -0
  76. package/dist/esm/Otel.js.map +1 -0
  77. package/dist/esm/Otlp.d.ts +203 -0
  78. package/dist/esm/Otlp.d.ts.map +1 -0
  79. package/dist/esm/Otlp.js +384 -0
  80. package/dist/esm/Otlp.js.map +1 -0
  81. package/dist/esm/Resource.d.ts +163 -0
  82. package/dist/esm/Resource.d.ts.map +1 -0
  83. package/dist/esm/Resource.js +238 -0
  84. package/dist/esm/Resource.js.map +1 -0
  85. package/dist/esm/index.d.ts +61 -0
  86. package/dist/esm/index.d.ts.map +1 -0
  87. package/dist/esm/index.js +61 -0
  88. package/dist/esm/index.js.map +1 -0
  89. package/dist/esm/internal/schemaIssuePath.d.ts +31 -0
  90. package/dist/esm/internal/schemaIssuePath.d.ts.map +1 -0
  91. package/dist/esm/internal/schemaIssuePath.js +59 -0
  92. package/dist/esm/internal/schemaIssuePath.js.map +1 -0
  93. package/docs/README.md +164 -0
  94. package/docs/api.md +780 -0
  95. package/docs/concepts/instrumentation-and-export.md +98 -0
  96. package/docs/concepts/layer-map.md +73 -0
  97. package/docs/concepts/validated-acquisition.md +106 -0
  98. package/docs/guides/export-to-a-collector.md +190 -0
  99. package/docs/guides/forward-logs-to-the-journal.md +226 -0
  100. package/docs/guides/install-a-logger.md +111 -0
  101. package/docs/guides/read-runtime-metrics.md +83 -0
  102. package/docs/guides/testing.md +134 -0
  103. package/docs/guides/wire-an-opentelemetry-sdk.md +226 -0
  104. package/docs/installation.md +102 -0
  105. package/docs/quickstart.md +145 -0
  106. package/docs/troubleshooting.md +168 -0
  107. package/package.json +221 -32
  108. package/src/BrowserOtel.ts +51 -0
  109. package/src/Endpoint.ts +131 -0
  110. package/src/JournalLogger.ts +635 -0
  111. package/src/Logger.ts +107 -0
  112. package/src/Metric.ts +88 -0
  113. package/src/NodeOtel.ts +90 -0
  114. package/src/Otel.ts +118 -0
  115. package/src/Otlp.ts +514 -0
  116. package/src/Resource.ts +325 -0
  117. package/src/index.ts +67 -0
  118. package/src/internal/schemaIssuePath.ts +65 -0
  119. package/docker-compose.otel.yml +0 -104
  120. package/grafana/dashboards/smithers-dashboard.json +0 -453
  121. package/grafana/provisioning/dashboards/dashboards.yml +0 -12
  122. package/grafana/provisioning/datasources/datasources.yml +0 -49
  123. package/loki/loki-config.yaml +0 -35
  124. package/otel-collector-config.yml +0 -38
  125. package/prometheus/prometheus.yml +0 -8
  126. package/src/MetricName.ts +0 -1
  127. package/src/MetricsServiceLive.js +0 -9
  128. package/src/README.md +0 -28
  129. package/src/ResolvedSmithersObservabilityOptions.ts +0 -12
  130. package/src/SmithersEvent.ts +0 -843
  131. package/src/SmithersLogFormat.ts +0 -1
  132. package/src/SmithersMetricDefinition.ts +0 -15
  133. package/src/SmithersMetricType.ts +0 -1
  134. package/src/SmithersMetricUnit.ts +0 -1
  135. package/src/SmithersObservability.js +0 -15
  136. package/src/SmithersObservabilityOptions.ts +0 -12
  137. package/src/SmithersObservabilityService.ts +0 -13
  138. package/src/_coreCorrelation/CorrelationContext.ts +0 -10
  139. package/src/_coreCorrelation/CorrelationContextLive.js +0 -11
  140. package/src/_coreCorrelation/CorrelationContextService.js +0 -15
  141. package/src/_coreCorrelation/CorrelationContextServiceShape.ts +0 -12
  142. package/src/_coreCorrelation/CorrelationPatch.ts +0 -3
  143. package/src/_coreCorrelation/README.md +0 -20
  144. package/src/_coreCorrelation/_correlationStorage.js +0 -4
  145. package/src/_coreCorrelation/correlationContextFiberRef.js +0 -5
  146. package/src/_coreCorrelation/correlationContextToLogAnnotations.js +0 -18
  147. package/src/_coreCorrelation/getCurrentCorrelationContext.js +0 -9
  148. package/src/_coreCorrelation/getCurrentCorrelationContextEffect.js +0 -11
  149. package/src/_coreCorrelation/index.js +0 -14
  150. package/src/_coreCorrelation/mergeCorrelationContext.js +0 -51
  151. package/src/_coreCorrelation/runWithCorrelationContext.js +0 -14
  152. package/src/_coreCorrelation/updateCurrentCorrelationContext.js +0 -17
  153. package/src/_coreCorrelation/withCorrelationContext.js +0 -38
  154. package/src/_coreCorrelation/withCurrentCorrelationContext.js +0 -11
  155. package/src/_coreMetrics.js +0 -14
  156. package/src/_coreMetricsShape.ts +0 -44
  157. package/src/_corePrometheus.js +0 -91
  158. package/src/_corePrometheusShape.ts +0 -11
  159. package/src/_coreTracing.js +0 -132
  160. package/src/_coreTracingShape.ts +0 -17
  161. package/src/_otelLogBuilders.js +0 -102
  162. package/src/_sessionFileResolvers.js +0 -164
  163. package/src/_smithersSpanAttributeAliases.js +0 -19
  164. package/src/_smithersTraceSpanStorage.js +0 -3
  165. package/src/_traceEventNormalizers.js +0 -499
  166. package/src/_traceRedaction.js +0 -162
  167. package/src/agentSessionEventToOtelLogRecord.js +0 -55
  168. package/src/agentTrace.ts +0 -144
  169. package/src/agentTraceCapabilities.js +0 -151
  170. package/src/annotateSmithersTrace.js +0 -11
  171. package/src/canonicalTraceEventToOtelLogRecord.js +0 -55
  172. package/src/correlation.js +0 -56
  173. package/src/createSmithersObservabilityLayer.js +0 -59
  174. package/src/createSmithersOtelLayer.js +0 -23
  175. package/src/createSmithersRuntimeLayer.js +0 -2
  176. package/src/detectAgentFamily.js +0 -26
  177. package/src/detectCaptureMode.js +0 -29
  178. package/src/emitOtelLogRecord.js +0 -19
  179. package/src/getCurrentSmithersTraceAnnotations.js +0 -14
  180. package/src/getCurrentSmithersTraceSpan.js +0 -7
  181. package/src/index.d.ts +0 -1392
  182. package/src/index.js +0 -137
  183. package/src/kindPhase.js +0 -17
  184. package/src/logging.js +0 -264
  185. package/src/makeSmithersSpanAttributes.js +0 -20
  186. package/src/metrics/README.md +0 -22
  187. package/src/metrics/SmithersMetricDefinition.ts +0 -17
  188. package/src/metrics/SmithersMetricType.ts +0 -1
  189. package/src/metrics/SmithersMetricUnit.ts +0 -1
  190. package/src/metrics/_asyncExternalWaitCounts.js +0 -4
  191. package/src/metrics/_buckets.js +0 -37
  192. package/src/metrics/_incrementGauge.js +0 -11
  193. package/src/metrics/_processStartMs.js +0 -1
  194. package/src/metrics/activeNodes.js +0 -2
  195. package/src/metrics/activeRuns.js +0 -2
  196. package/src/metrics/agentActionsTotal.js +0 -2
  197. package/src/metrics/agentDurationMs.js +0 -3
  198. package/src/metrics/agentErrorsTotal.js +0 -2
  199. package/src/metrics/agentEventsTotal.js +0 -2
  200. package/src/metrics/agentInvocationsTotal.js +0 -2
  201. package/src/metrics/agentRetriesTotal.js +0 -2
  202. package/src/metrics/agentSessionsTotal.js +0 -2
  203. package/src/metrics/agentTokensTotal.js +0 -2
  204. package/src/metrics/alertDeliveriesAttempted.js +0 -2
  205. package/src/metrics/alertDeliveriesSuppressed.js +0 -2
  206. package/src/metrics/alertsAcknowledgedTotal.js +0 -2
  207. package/src/metrics/alertsActive.js +0 -2
  208. package/src/metrics/alertsEscalatedTotal.js +0 -2
  209. package/src/metrics/alertsFiredTotal.js +0 -2
  210. package/src/metrics/alertsReopenedTotal.js +0 -2
  211. package/src/metrics/alertsResolvedTotal.js +0 -2
  212. package/src/metrics/alertsSilencedTotal.js +0 -2
  213. package/src/metrics/approvalPending.js +0 -2
  214. package/src/metrics/approvalWaitDuration.js +0 -5
  215. package/src/metrics/approvalsDenied.js +0 -2
  216. package/src/metrics/approvalsGranted.js +0 -2
  217. package/src/metrics/approvalsRequested.js +0 -2
  218. package/src/metrics/attemptDuration.js +0 -3
  219. package/src/metrics/attentionBacklog.js +0 -2
  220. package/src/metrics/cacheHits.js +0 -2
  221. package/src/metrics/cacheMisses.js +0 -2
  222. package/src/metrics/dbQueryDuration.js +0 -3
  223. package/src/metrics/dbRetries.js +0 -2
  224. package/src/metrics/dbTransactionDuration.js +0 -3
  225. package/src/metrics/dbTransactionRetries.js +0 -2
  226. package/src/metrics/dbTransactionRollbacks.js +0 -2
  227. package/src/metrics/devtoolsActiveSubscribers.js +0 -2
  228. package/src/metrics/devtoolsBackpressureDisconnectTotal.js +0 -2
  229. package/src/metrics/devtoolsDeltaBuildMs.js +0 -3
  230. package/src/metrics/devtoolsEventBytes.js +0 -3
  231. package/src/metrics/devtoolsEventTotal.js +0 -2
  232. package/src/metrics/devtoolsSnapshotBuildMs.js +0 -5
  233. package/src/metrics/devtoolsSubscribeTotal.js +0 -2
  234. package/src/metrics/errorsTotal.js +0 -2
  235. package/src/metrics/eventsEmittedTotal.js +0 -2
  236. package/src/metrics/externalWaitAsyncPending.js +0 -2
  237. package/src/metrics/gatewayApprovalDecisionsTotal.js +0 -2
  238. package/src/metrics/gatewayAuthEventsTotal.js +0 -2
  239. package/src/metrics/gatewayConnectionsActive.js +0 -2
  240. package/src/metrics/gatewayConnectionsClosedTotal.js +0 -2
  241. package/src/metrics/gatewayConnectionsTotal.js +0 -2
  242. package/src/metrics/gatewayCronTriggersTotal.js +0 -2
  243. package/src/metrics/gatewayErrorsTotal.js +0 -2
  244. package/src/metrics/gatewayHeartbeatTicksTotal.js +0 -2
  245. package/src/metrics/gatewayMessagesReceivedTotal.js +0 -2
  246. package/src/metrics/gatewayMessagesSentTotal.js +0 -2
  247. package/src/metrics/gatewayRpcCallsTotal.js +0 -2
  248. package/src/metrics/gatewayRpcDuration.js +0 -3
  249. package/src/metrics/gatewayRunEventBackpressureDisconnectTotal.js +0 -4
  250. package/src/metrics/gatewayRunsCompletedTotal.js +0 -2
  251. package/src/metrics/gatewayRunsStartedTotal.js +0 -2
  252. package/src/metrics/gatewaySignalsTotal.js +0 -2
  253. package/src/metrics/gatewayWebhooksReceivedTotal.js +0 -2
  254. package/src/metrics/gatewayWebhooksRejectedTotal.js +0 -2
  255. package/src/metrics/gatewayWebhooksVerifiedTotal.js +0 -2
  256. package/src/metrics/heartbeatDataSizeBytes.js +0 -5
  257. package/src/metrics/heartbeatIntervalMs.js +0 -3
  258. package/src/metrics/hotReloadDuration.js +0 -3
  259. package/src/metrics/hotReloadFailures.js +0 -2
  260. package/src/metrics/hotReloads.js +0 -2
  261. package/src/metrics/httpRequestDuration.js +0 -3
  262. package/src/metrics/httpRequests.js +0 -2
  263. package/src/metrics/index.d.ts +0 -1222
  264. package/src/metrics/index.js +0 -168
  265. package/src/metrics/memoryFactReads.js +0 -3
  266. package/src/metrics/memoryFactWrites.js +0 -3
  267. package/src/metrics/memoryMessageSaves.js +0 -3
  268. package/src/metrics/memoryRecallDuration.js +0 -11
  269. package/src/metrics/memoryRecallQueries.js +0 -3
  270. package/src/metrics/metricsServiceAdapter.js +0 -181
  271. package/src/metrics/nodeDuration.js +0 -3
  272. package/src/metrics/nodeRetriesTotal.js +0 -2
  273. package/src/metrics/nodesFailed.js +0 -2
  274. package/src/metrics/nodesFinished.js +0 -2
  275. package/src/metrics/nodesStarted.js +0 -2
  276. package/src/metrics/openApiToolCallErrorsTotal.js +0 -3
  277. package/src/metrics/openApiToolCallsTotal.js +0 -3
  278. package/src/metrics/openApiToolDuration.js +0 -9
  279. package/src/metrics/processHeapUsedBytes.js +0 -2
  280. package/src/metrics/processMemoryRssBytes.js +0 -2
  281. package/src/metrics/processUptimeSeconds.js +0 -2
  282. package/src/metrics/promptSizeBytes.js +0 -3
  283. package/src/metrics/replaysStarted.js +0 -3
  284. package/src/metrics/responseSizeBytes.js +0 -3
  285. package/src/metrics/rewindDurationMs.js +0 -3
  286. package/src/metrics/rewindFramesDeleted.js +0 -3
  287. package/src/metrics/rewindRollbackTotal.js +0 -2
  288. package/src/metrics/rewindSandboxesReverted.js +0 -3
  289. package/src/metrics/rewindTotal.js +0 -2
  290. package/src/metrics/runDuration.js +0 -3
  291. package/src/metrics/runForksCreated.js +0 -3
  292. package/src/metrics/runsAncestryDepth.js +0 -3
  293. package/src/metrics/runsCancelledTotal.js +0 -2
  294. package/src/metrics/runsCarriedStateBytes.js +0 -5
  295. package/src/metrics/runsContinuedTotal.js +0 -2
  296. package/src/metrics/runsFailedTotal.js +0 -2
  297. package/src/metrics/runsFinishedTotal.js +0 -2
  298. package/src/metrics/runsResumedTotal.js +0 -2
  299. package/src/metrics/runsTotal.js +0 -2
  300. package/src/metrics/sandboxActive.js +0 -2
  301. package/src/metrics/sandboxBundleSizeBytes.js +0 -5
  302. package/src/metrics/sandboxCompletedTotal.js +0 -2
  303. package/src/metrics/sandboxCreatedTotal.js +0 -2
  304. package/src/metrics/sandboxDurationMs.js +0 -3
  305. package/src/metrics/sandboxPatchCount.js +0 -3
  306. package/src/metrics/sandboxTransportDurationMs.js +0 -5
  307. package/src/metrics/schedulerConcurrencyUtilization.js +0 -2
  308. package/src/metrics/schedulerQueueDepth.js +0 -2
  309. package/src/metrics/schedulerWaitDuration.js +0 -5
  310. package/src/metrics/scorerDuration.js +0 -9
  311. package/src/metrics/scorerEventsFailed.js +0 -2
  312. package/src/metrics/scorerEventsFinished.js +0 -2
  313. package/src/metrics/scorerEventsStarted.js +0 -2
  314. package/src/metrics/scorersFailed.js +0 -3
  315. package/src/metrics/scorersFinished.js +0 -3
  316. package/src/metrics/scorersStarted.js +0 -3
  317. package/src/metrics/smithersMetricCatalog.js +0 -893
  318. package/src/metrics/smithersMetricCatalogByKey.js +0 -2
  319. package/src/metrics/smithersMetricCatalogByName.js +0 -2
  320. package/src/metrics/smithersMetricCatalogByPrometheusName.js +0 -4
  321. package/src/metrics/snapshotDuration.js +0 -9
  322. package/src/metrics/snapshotsCaptured.js +0 -3
  323. package/src/metrics/supervisorPollDuration.js +0 -5
  324. package/src/metrics/supervisorPollsTotal.js +0 -2
  325. package/src/metrics/supervisorResumeLag.js +0 -5
  326. package/src/metrics/supervisorResumedTotal.js +0 -2
  327. package/src/metrics/supervisorSkippedTotal.js +0 -2
  328. package/src/metrics/supervisorStaleDetected.js +0 -2
  329. package/src/metrics/taskHeartbeatTimeoutTotal.js +0 -2
  330. package/src/metrics/taskHeartbeatsTotal.js +0 -2
  331. package/src/metrics/timerDelayDuration.js +0 -3
  332. package/src/metrics/timersCancelled.js +0 -2
  333. package/src/metrics/timersCreated.js +0 -2
  334. package/src/metrics/timersFired.js +0 -2
  335. package/src/metrics/timersPending.js +0 -2
  336. package/src/metrics/toPrometheusMetricName.js +0 -8
  337. package/src/metrics/tokensCacheReadTotal.js +0 -2
  338. package/src/metrics/tokensCacheWriteTotal.js +0 -2
  339. package/src/metrics/tokensContextWindowBucketTotal.js +0 -2
  340. package/src/metrics/tokensContextWindowPerCall.js +0 -5
  341. package/src/metrics/tokensInputPerCall.js +0 -3
  342. package/src/metrics/tokensInputTotal.js +0 -2
  343. package/src/metrics/tokensOutputPerCall.js +0 -3
  344. package/src/metrics/tokensOutputTotal.js +0 -2
  345. package/src/metrics/tokensReasoningTotal.js +0 -2
  346. package/src/metrics/toolCallErrorsTotal.js +0 -2
  347. package/src/metrics/toolCallsTotal.js +0 -2
  348. package/src/metrics/toolDuration.js +0 -3
  349. package/src/metrics/toolOutputTruncatedTotal.js +0 -2
  350. package/src/metrics/trackEvent.js +0 -608
  351. package/src/metrics/updateAsyncExternalWaitPending.js +0 -18
  352. package/src/metrics/updateProcessMetrics.js +0 -20
  353. package/src/metrics/vcsDuration.js +0 -3
  354. package/src/normalizeStructuredEvent.js +0 -15
  355. package/src/prometheusContentType.js +0 -1
  356. package/src/renderPrometheusMetrics.js +0 -212
  357. package/src/resolveAgentTraceCapabilities.js +0 -59
  358. package/src/resolveSmithersObservabilityOptions.js +0 -109
  359. package/src/smithersMetrics.js +0 -2
  360. package/src/smithersSpanNames.js +0 -6
  361. package/src/unsupportedKindsForCapabilities.js +0 -32
  362. package/src/withSmithersSpan.js +0 -17
  363. package/tempo/tempo.yml +0 -17
@@ -0,0 +1,226 @@
1
+ ---
2
+ title: "Forward logs to the run journal"
3
+ description: "Install JournalLogger.layerJournalForwarding so a run's Effect log records become durable telemetry.log entries, and read them back: capacity, bounds, redaction, observed losses, and admission limits."
4
+ sidebar:
5
+ order: 3
6
+ ---
7
+
8
+ `JournalLogger` turns a run's Effect log records into durable, structured
9
+ `telemetry.log` entries on that run's journal. It is the second delivery path
10
+ beside [OTLP export](./export-to-a-collector.md), and it is the one an operator
11
+ reads without a collector: the records land next to the run's lifecycle events
12
+ and are read back with the same `Journal.entries` call.
13
+
14
+ ## Install the layer
15
+
16
+ The layer names one run, and requires a `Journal.Journal` service from
17
+ [`@smthrs/journal`](/api/journal):
18
+
19
+ ```ts
20
+ import * as JournalEvent from "@smthrs/journal/JournalEvent"
21
+ import * as JournalLogger from "@smthrs/observability/JournalLogger"
22
+ import * as Schema from "effect/Schema"
23
+
24
+ const runId = Schema.decodeUnknownSync(JournalEvent.RunId)("deploy-status-1")
25
+
26
+ const journalLogs = JournalLogger.layerJournalForwarding({ runId })
27
+ ```
28
+
29
+ `runId` is branded, so it comes from decoding a string rather than from a bare
30
+ literal. Decoding it here means a malformed id is reported where it was
31
+ written.
32
+
33
+ Provide the layer over your journal. In a test, the deterministic bundle is
34
+ enough:
35
+
36
+ ```ts
37
+ import * as TestJournal from "@smthrs/journal/test/TestJournal"
38
+ import * as Layer from "effect/Layer"
39
+
40
+ const layer = Layer.provideMerge(journalLogs, TestJournal.layer())
41
+ ```
42
+
43
+ In production, the journal is the one your durable engine already composed;
44
+ provide the forwarder over it the same way.
45
+
46
+ ## Read the records back
47
+
48
+ Each record is one journal entry with `eventType` `telemetry.log`, written on
49
+ the journal's lossy channel by source `flows/observability/logger`. The payload
50
+ decodes with the exported schema:
51
+
52
+ ```ts
53
+ import * as Journal from "@smthrs/journal/Journal"
54
+ import * as Effect from "effect/Effect"
55
+
56
+ const read = Effect.gen(function*() {
57
+ const journal = yield* Journal.Journal
58
+ const page = yield* journal.entries({ runId, limit: 200 })
59
+ return page.entries
60
+ .filter((entry) => entry.eventType === "telemetry.log")
61
+ .map((entry) => Schema.decodeUnknownSync(JournalLogger.TelemetryLog)(entry.payload))
62
+ })
63
+ ```
64
+
65
+ A decoded record carries the level, the logged message, the log annotations,
66
+ the fiber id, an ISO-8601 timestamp, the active span's `traceId` and `spanId`
67
+ when there was one, and a structural `cause` that preserves the order and the
68
+ kind of every failure reason:
69
+
70
+ ```text
71
+ {
72
+ version: 1,
73
+ level: "Error",
74
+ message: [ "deploy failed" ],
75
+ annotations: { lane: "api", attempt: 2 },
76
+ cause: {
77
+ version: 1,
78
+ reasons: [
79
+ { _tag: "Fail", error: { code: "timeout" } },
80
+ { _tag: "Interrupt", fiberId: 7 }
81
+ ]
82
+ },
83
+ fiberId: 12,
84
+ traceId: "0af7651916cd43dd8448eb211c80319c",
85
+ spanId: "b7ad6b7169203331",
86
+ timestamp: "2026-09-03T12:00:00.000Z"
87
+ }
88
+ ```
89
+
90
+ The durable journal allocates the per-source sequence, so restarting the layer
91
+ or running two of them for one run cannot make two records collide on one
92
+ identity.
93
+
94
+ ## Choose the queue depth
95
+
96
+ The logger callback never blocks. It snapshots the record synchronously when the
97
+ bounded queue has room, then offers it to that queue, which a forked worker
98
+ drains into the journal.
99
+
100
+ ```ts
101
+ const journalLogs = JournalLogger.layerJournalForwarding({
102
+ runId,
103
+ capacity: 4096,
104
+ minimumLogLevel: "Debug",
105
+ mergeWithExisting: true
106
+ })
107
+ ```
108
+
109
+ - `capacity` defaults to 256 and accepts 1 through
110
+ `JournalLogger.maximumCapacity` (65,536). Raise it for a chatty run on a slow
111
+ journal; a full queue drops the incoming record before snapshotting it, so
112
+ saturation costs no traversal of the logged value.
113
+ - `minimumLogLevel` pins `References.MinimumLogLevel` for the whole
114
+ application. Omitted, the layer leaves that reference alone and Effect's own
115
+ `Info` default applies.
116
+ - `mergeWithExisting` defaults to `false`, which replaces the ambient logger
117
+ set. Pass `true` to keep a console logger alongside the forwarder. See
118
+ [Install a logger](./install-a-logger.md).
119
+
120
+ An invalid run id or capacity fails layer acquisition with
121
+ `InvalidJournalLoggerOptions`, before any worker starts.
122
+
123
+ ## What the snapshot does to a value
124
+
125
+ The record is detached, bounded, and redacted before it joins the queue, so
126
+ mutating a logged object after the log call cannot change what is persisted,
127
+ and a hostile value cannot stall the logger.
128
+
129
+ | Ceiling | Value | What happens past it |
130
+ | ------------------------ | ----- | ------------------------------------------------ |
131
+ | `maximumSnapshotBytes` | 1 MiB | Text is cut and marked `[Truncated]` |
132
+ | `maximumSnapshotMembers` | 4,096 | Remaining members become one `[Truncated]` entry |
133
+ | `maximumSnapshotDepth` | 64 | Deeper values become `[Deep]` |
134
+
135
+ Values that cannot be read at all, such as a throwing accessor or a revoked
136
+ proxy, become `[Unrenderable]`. Functions, symbols, and binary data are named
137
+ rather than serialized (`[Function]`, `[Symbol]`, `[Binary]`), and a cycle
138
+ becomes `[Circular]`. Text is bounded in one pass that never cuts a surrogate
139
+ pair, so a truncated string is still well formed.
140
+
141
+ An `Error` is projected to `name`, `message`, `stack`, and `cause` first, then
142
+ to every other own data property it carries, under the same member and byte
143
+ budgets. A `Schema.TaggedError` therefore keeps its `_tag` and its declared
144
+ fields, and a Node system error keeps `code`, `syscall`, and `path`. Accessor
145
+ properties become `[Unrenderable]`, and members past the budget become one
146
+ `[Truncated]` entry.
147
+
148
+ Container-shaped fields keep their shape. Annotations whose snapshot spent the
149
+ whole budget arrive as `{ "[Truncated]": "[Truncated]" }` rather than as a
150
+ scalar the schema would refuse. A projection that still fails `TelemetryLog`
151
+ degrades to a total record with an empty cause, so every durable
152
+ `telemetry.log` row decodes.
153
+
154
+ The journal's own redaction rules run before queue admission, the same scrub
155
+ durable events get, so a credential in a logged object never reaches a
156
+ permanent row.
157
+
158
+ ## Watch for lost records
159
+
160
+ Forwarding is lossy on purpose: a telemetry backlog must not become application
161
+ backpressure. Each of these observed losses advances `Metric.droppedLogRecords`
162
+ (`flows_observability_log_dropped`) once:
163
+
164
+ 1. **Forwarder queue overflow.** The queue was full when the record arrived.
165
+ Counted only.
166
+ 2. **Journal `Dropped` receipt.** The journal refused admission under its
167
+ `drop-newest` policy. Counted only; `Accepted` and `Duplicate` receipts do
168
+ not advance the counter.
169
+ 3. **Journal delivery failure.** Admission failed. Counted, and reported as a
170
+ warning with the run id and code `journal_forwarding_failed`.
171
+ 4. **A defect from the journal implementation.** Counted and warned with the
172
+ run id and code `journal_forwarding_defect`. The worker keeps draining.
173
+
174
+ Warnings carry fixed diagnostic codes instead of raw errors or causes, so
175
+ failure credentials do not reach ambient warning sinks. The worker is forked
176
+ before the forwarding logger is installed, so a warning cannot enqueue itself.
177
+
178
+ `emitLossy` reports admission, not durable delivery. The counter cannot observe
179
+ later `drop-oldest` evictions of admitted telemetry, asynchronous persistence
180
+ failures, or journal shutdown losses. An `Accepted.evicted` summary does not
181
+ identify the evicted record, so it cannot attribute that loss to telemetry.
182
+
183
+ Interruption stays fatal: closing the layer's scope ends the worker and can
184
+ drop records queued behind an in-flight write without advancing this counter.
185
+ See [Assert on a short-lived run](#assert-on-a-short-lived-run) for the shape a
186
+ test needs because of that.
187
+
188
+ A nonzero `droppedLogRecords` with a healthy journal usually means the capacity
189
+ is too small for the run's log volume. See
190
+ [Read the runtime metrics](./read-runtime-metrics.md) for how to read the
191
+ counter.
192
+
193
+ ## Assert on a short-lived run
194
+
195
+ The forwarder exposes no drain barrier. `journal.flush` settles the entries the
196
+ journal has already admitted, so it returns while a record is still waiting in
197
+ the forwarder's queue or inside an admission the worker has not finished.
198
+ Closing the layer's scope then interrupts that worker and discards what is
199
+ left, so flushing before an assertion proves nothing about a record the journal
200
+ never saw.
201
+
202
+ Poll the journal for the records you expect while the layer's scope is still
203
+ open, and let the scope close after the assertion:
204
+
205
+ ```ts
206
+ const forwarded = Effect.gen(function*() {
207
+ const journal = yield* Journal.Journal
208
+ yield* Effect.logInfo("first")
209
+ yield* Effect.logInfo("second")
210
+ for (let attempt = 0; attempt < 200; attempt++) {
211
+ const page = yield* journal.entries({ runId, limit: 100 })
212
+ const records = page.entries.filter((entry) => entry.eventType === "telemetry.log")
213
+ if (records.length >= 2) return records
214
+ yield* Effect.sleep("5 millis")
215
+ }
216
+ return []
217
+ }).pipe(
218
+ Effect.provide(Layer.provideMerge(JournalLogger.layerJournalForwarding({ runId }), TestJournal.layer())),
219
+ Effect.scoped
220
+ )
221
+ ```
222
+
223
+ A bounded poll fails the assertion when a record never arrives, where a fixed
224
+ sleep only makes that failure intermittent. The same pattern is in
225
+ [Test telemetry without a collector](./testing.md), and the loss it guards
226
+ against is cataloged in [Troubleshooting](../troubleshooting.md).
@@ -0,0 +1,111 @@
1
+ ---
2
+ title: "Install a logger"
3
+ description: "Pick a Logger layer for a development shell, a production process, or a silent test, and understand the two rules every one of them follows: the ambient set is replaced, and the minimum level is left alone."
4
+ sidebar:
5
+ order: 4
6
+ ---
7
+
8
+ `Logger` ships four layers over Effect's own logger machinery. They differ in
9
+ the sink; they agree on the two rules that decide what installing one does to
10
+ the rest of the application.
11
+
12
+ ## Pick a layer
13
+
14
+ ```ts
15
+ import * as Logger from "@smthrs/observability/Logger"
16
+
17
+ // A development shell: colored, human-readable lines.
18
+ const development = Logger.layerPrettyDev()
19
+
20
+ // A production process: one structured JSON object per line.
21
+ const production = Logger.layerStructuredJson()
22
+
23
+ // A test that should print nothing.
24
+ const silent = Logger.layerNoop()
25
+ ```
26
+
27
+ `Logger.layerPrettyDev` chooses colors and mode from the terminal it finds, so
28
+ it degrades to plain text when the output is not a TTY.
29
+ `Logger.layerStructuredJson` is the one to ship: a log shipper can parse it, and
30
+ it is what a collector's log pipeline expects.
31
+
32
+ To install a logger you wrote, or one from another package, use `Logger.layer`:
33
+
34
+ ```ts
35
+ import * as EffectLogger from "effect/Logger"
36
+
37
+ const custom = Logger.layer(
38
+ EffectLogger.make((options) => {
39
+ /* your sink */
40
+ })
41
+ )
42
+ ```
43
+
44
+ ## Rule one: the ambient set is replaced
45
+
46
+ `mergeWithExisting` defaults to `false`, matching Effect's own `Logger.layer`,
47
+ so installing one of these layers replaces the ambient logger set rather than
48
+ adding to it. That is what you want for a process that has exactly one sink,
49
+ and it is the usual explanation for logs disappearing after a layer was added.
50
+
51
+ Pass `true` to keep the loggers already installed:
52
+
53
+ ```ts
54
+ const both = Logger.layerStructuredJson({ mergeWithExisting: true })
55
+ ```
56
+
57
+ The same option is on
58
+ [`JournalLogger.layerJournalForwarding`](./forward-logs-to-the-journal.md), and
59
+ merging is how a control-plane run gets console output and durable
60
+ `telemetry.log` rows from one log call.
61
+
62
+ ## Rule two: the minimum level is left alone
63
+
64
+ A layer that adds a sink has no business overriding the level an application
65
+ chose elsewhere, so `References.MinimumLogLevel` is provided only when you name
66
+ `minimumLogLevel`. Omitted, whatever the application set stands, and Effect's
67
+ own `Info` default applies when nothing set it.
68
+
69
+ ```ts
70
+ // Leaves the application's chosen level in place.
71
+ const inherits = Logger.layerStructuredJson()
72
+
73
+ // Pins Warn for the whole application.
74
+ const pinned = Logger.layerStructuredJson({ minimumLogLevel: "Warn" })
75
+ ```
76
+
77
+ `Logger.layerNoop` is the deliberate exception. Silence is its purpose, so it
78
+ pins `None` unless you name another level. Passing one is how a test silences
79
+ output while keeping a level assertion meaningful:
80
+
81
+ ```ts
82
+ const quietButFiltered = Logger.layerNoop({ minimumLogLevel: "Debug" })
83
+ ```
84
+
85
+ ## Merge when you install more than one sink
86
+
87
+ Installing a logger decides what is rendered locally. It exports nothing on its
88
+ own: log records reach a collector because [`Otlp`](./export-to-a-collector.md)
89
+ adds its own log exporter to the same ambient set, and they reach a run's
90
+ history because
91
+ [`JournalLogger`](./forward-logs-to-the-journal.md) adds a forwarder to it.
92
+
93
+ That shared set is why rule one matters here. A `Logger` layer with the default
94
+ `mergeWithExisting: false` discards every logger the set already held, and
95
+ whether that includes the OTLP exporter depends on which layer the composition
96
+ builds last. Pass `mergeWithExisting: true` on the `Logger` layer whenever
97
+ another sink is installed, and the ordering question does not arise:
98
+
99
+ ```ts
100
+ import * as Effect from "effect/Effect"
101
+
102
+ const outcome = program.pipe(
103
+ Effect.provide(Logger.layerStructuredJson({ mergeWithExisting: true })),
104
+ Effect.provide(telemetry),
105
+ Effect.scoped
106
+ )
107
+ ```
108
+
109
+ One `Effect.logInfo` call then renders locally, posts to the collector's
110
+ `/v1/logs` path, and, with the journal forwarder installed, lands on the run's
111
+ durable history.
@@ -0,0 +1,83 @@
1
+ ---
2
+ title: "Read the runtime metrics"
3
+ description: "The four runtime metric handles this package declares, the exact series names a dashboard binds to, which package advances each one, and how to read a counter in process without an exporter."
4
+ sidebar:
5
+ order: 5
6
+ ---
7
+
8
+ `Metric` declares the runtime signals that cross package boundaries: the
9
+ handles a producer updates and a dashboard reads. Every other metric in
10
+ Smithers belongs to the package that owns the behavior it measures.
11
+
12
+ ## The six handles
13
+
14
+ | Handle | Series name | Kind | Advances when |
15
+ | ---------------------------- | ------------------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
16
+ | `runThroughput` | `flows_run_throughput` | counter | A run's terminal transition commits in [`@smthrs/run-store`](/api/run-store). |
17
+ | `activeSeats` | `flows_seat_active` | gauge | Held for the lifetime of a production `Agent.run` stream in [`@smthrs/agent`](/api/agent), and released on success, failure, or interruption. |
18
+ | `quotaParks` | `flows_quota_park` | counter | A sealed quota decision is first executed, and not when that decision is replayed after a wake or a process restart. |
19
+ | `structuredOutputRejections` | `flows_agent_structured_output_rejections` | counter | An agent answer is refused by its declared output schema, in the same session or a fresh one. |
20
+ | `structuredOutputExhausted` | `flows_agent_structured_output_exhausted` | counter | An agent step fails after spending every output correction and its repair. |
21
+ | `droppedLogRecords` | `flows_observability_log_dropped` | counter | A log record is lost before durable delivery: once per queue overflow, once per journal delivery failure, and once per defect the forwarding worker recovers from. |
22
+
23
+ `Metric.registry` is the same six as one object, so a host can enumerate them.
24
+ The series names are the dashboard contract; treat them as public API.
25
+
26
+ Step-cache lookup and write counters belong to
27
+ [`@smthrs/step-cache`](/api/step-cache), and the store packages keep their own
28
+ handles. This package does not duplicate any of them.
29
+
30
+ ## Read a counter in process
31
+
32
+ A metric handle is readable without an exporter, which makes it a fine
33
+ assertion target in a test and a fine health check in a host:
34
+
35
+ ```ts
36
+ import * as ObservabilityMetric from "@smthrs/observability/Metric"
37
+ import * as Effect from "effect/Effect"
38
+ import * as Metric from "effect/Metric"
39
+
40
+ const dropped = Effect.gen(function*() {
41
+ const value = yield* Metric.value(ObservabilityMetric.droppedLogRecords)
42
+ return value.count
43
+ })
44
+ ```
45
+
46
+ A counter's value carries `count`; a gauge's carries `value`.
47
+
48
+ ## Read a dimensioned counter through its attribute view
49
+
50
+ Some packages update a counter through a tagged view rather than the bare
51
+ handle. The dispatch counter in [`@smthrs/engine-store`](/api/engine-store) is
52
+ the common example: it updates only the series tagged with the outcome, so
53
+ reading the bare handle reads the attribute-less series and always sees zero.
54
+
55
+ ```ts
56
+ import { EngineStoreMetrics } from "@smthrs/engine-store"
57
+
58
+ const dispatches = Effect.gen(function*() {
59
+ const value = yield* Metric.value(EngineStoreMetrics.dispatch.Success)
60
+ return value.count
61
+ })
62
+ ```
63
+
64
+ Read the exported view, not the parent handle, whenever the owning package
65
+ exports one.
66
+
67
+ ## Isolate the registry in a test
68
+
69
+ Effect keeps metric state in a `MetricRegistry` reference with a
70
+ process-default value, so counters accumulate across tests in one process.
71
+ Provide a fresh map to isolate one run:
72
+
73
+ ```ts
74
+ Effect.provideService(Metric.MetricRegistry, new Map())
75
+ ```
76
+
77
+ ## Where the series go
78
+
79
+ Every registered series exports with the rest of the telemetry: with
80
+ [`Otlp` provided](./export-to-a-collector.md), they post to the collector's
81
+ `/v1/metrics` path under the `service.name` you configured. With no exporter
82
+ installed, they are still collected and still readable in process, and nothing
83
+ leaves the machine.
@@ -0,0 +1,134 @@
1
+ ---
2
+ title: "Test telemetry without a collector"
3
+ description: "Assert on what would have been exported: a recording fetch for OTLP payloads, TestJournal for forwarded log records, a fresh metric registry, and the noop layers that stand in for absent telemetry."
4
+ sidebar:
5
+ order: 6
6
+ ---
7
+
8
+ Telemetry is testable because every outbound edge is a service. Replace the
9
+ edge, keep the wiring, and the assertions are about the payloads a collector
10
+ would have received.
11
+
12
+ ## Record what the exporter posts
13
+
14
+ `Otlp.layerFetch` delivers over the `FetchHttpClient.Fetch` service, so
15
+ providing your own function captures every export request and touches no
16
+ network:
17
+
18
+ ```ts
19
+ import * as Otlp from "@smthrs/observability/Otlp"
20
+ import * as Effect from "effect/Effect"
21
+ import * as FetchHttpClient from "effect/http/FetchHttpClient"
22
+ import * as Metric from "effect/Metric"
23
+
24
+ const recordingFetch = () => {
25
+ const requests: Array<{ url: string; body: string }> = []
26
+ const fetch: typeof globalThis.fetch = (input, init) => {
27
+ const body = typeof init?.body === "string"
28
+ ? init.body
29
+ : new TextDecoder().decode(init?.body as Uint8Array)
30
+ requests.push({ url: String(input), body })
31
+ return Promise.resolve(new Response("{}", { status: 200 }))
32
+ }
33
+ return { requests, fetch }
34
+ }
35
+
36
+ const collector = recordingFetch()
37
+
38
+ await Effect.runPromise(
39
+ program.pipe(
40
+ Effect.provide(Otlp.layerFetch({ baseUrl: "http://collector.invalid:4318" })),
41
+ Effect.provideService(FetchHttpClient.Fetch, collector.fetch),
42
+ Effect.provideService(Metric.MetricRegistry, new Map()),
43
+ Effect.orDie,
44
+ Effect.scoped
45
+ )
46
+ )
47
+ ```
48
+
49
+ Assert after the scope closes, because the shutdown flush is what delivers a
50
+ short program's signals. Filter `collector.requests` by URL suffix to separate
51
+ the three signals, and parse a body to read span or series names. An
52
+ unresolvable host such as `collector.invalid` is a deliberate choice: if the
53
+ service is ever missed, the test fails rather than reaching the network.
54
+
55
+ The fresh `Metric.MetricRegistry` map keeps one test's counters out of the
56
+ next test's export.
57
+
58
+ ## Test the journal forwarder
59
+
60
+ `TestJournal.layer()` from [`@smthrs/journal`](/api/journal) provides the
61
+ production SQLite journal over an in-memory database, so a forwarding test
62
+ exercises the real write path:
63
+
64
+ ```ts
65
+ import * as Journal from "@smthrs/journal/Journal"
66
+ import * as TestJournal from "@smthrs/journal/test/TestJournal"
67
+ import * as JournalLogger from "@smthrs/observability/JournalLogger"
68
+ import * as Effect from "effect/Effect"
69
+ import * as Layer from "effect/Layer"
70
+
71
+ // `runId` is a decoded JournalEvent.RunId, as in the forwarding guide.
72
+ const entries = Effect.gen(function*() {
73
+ const journal = yield* Journal.Journal
74
+ yield* Effect.logInfo("hello")
75
+ const page = yield* journal.entries({ runId, limit: 100 })
76
+ return page.entries
77
+ }).pipe(
78
+ Effect.provide(Layer.provideMerge(JournalLogger.layerJournalForwarding({ runId }), TestJournal.layer())),
79
+ Effect.scoped
80
+ )
81
+ ```
82
+
83
+ Forwarding is asynchronous, so poll until the expected number of entries has
84
+ landed rather than sleeping a fixed interval. To assert on a loss instead,
85
+ read `Metric.value(ObservabilityMetric.droppedLogRecords)`.
86
+
87
+ ## Control time for the retry window
88
+
89
+ Export retries and the 60-second disable window run on Effect's clock, so
90
+ `TestClock` makes them deterministic:
91
+
92
+ ```ts
93
+ import { TestClock } from "effect/testing"
94
+
95
+ const exported = Effect.gen(function*() {
96
+ yield* Effect.void.pipe(Effect.withSpan("exported"))
97
+ // One initial request plus three transient retries completes within four seconds.
98
+ yield* TestClock.adjust("5 seconds")
99
+ })
100
+ ```
101
+
102
+ Provide `TestClock.layer()` alongside the exporter, then advance the clock and
103
+ count the recorded requests.
104
+
105
+ ## Stand telemetry down explicitly
106
+
107
+ A test that is not about telemetry should say so rather than leave the layer
108
+ out and let a type error decide:
109
+
110
+ | Layer | What it does |
111
+ | -------------------- | --------------------------------------------------------------- |
112
+ | `Otlp.layerNoop` | Provides nothing and exports nothing. |
113
+ | `Otel.layerNoop` | The empty provider-neutral OTEL slot. |
114
+ | `Logger.layerNoop()` | Removes the logger set and pins `None` unless you name a level. |
115
+
116
+ `Journal.layerNoop` from [`@smthrs/journal`](/api/journal) is the matching stub
117
+ for the forwarder when the assertion is about queue behavior rather than about
118
+ persistence.
119
+
120
+ ## Assert on refusals at acquisition
121
+
122
+ Both configuration refusals happen while the layer is built, so a test builds
123
+ the layer and inspects the exit rather than running a program:
124
+
125
+ ```ts
126
+ import * as Layer from "effect/Layer"
127
+
128
+ const exit = await Effect.runPromiseExit(
129
+ Effect.scoped(Layer.build(Otlp.layerFetch({ baseUrl: "collector:4318" })))
130
+ )
131
+ ```
132
+
133
+ The failure is an `Endpoint.InvalidExporterEndpoint` whose `path` is `baseUrl`.
134
+ See [Troubleshooting](../troubleshooting.md) for the catalog.