Repository navigation
feat: shared Node telemetry package, extracted from Backbone - #1
Merged
Merged
Conversation
pnpm, Node 22, TypeScript built to dist/, vitest, ESM. Dependency pins match Backbone's packages/telemetry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Backbone's SDK boot, two-layer scrub's layer 1, OTLP logger and trace helpers move from packages/telemetry. Its constants become options: logScope, metricViews, instrumentations (Prisma leaves the default list), redactKeys and onError. PostHog error tracking and Backbone's metric attribute list stay in Backbone. url-credentials has its own browser-safe entry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
captureConsole sends console.warn and console.error lines through the logger's scrub to OTLP logs under the configured scope, and still writes them to the console. register starts telemetry under node --import from OTEL_SERVICE_NAME alone. Tests pin the defaults: no SDK module without an endpoint, a dead collector costs a request at most 50 ms, no metricViews keeps every attribute, and the scope filter keeps the default scope's lines and drops every other scope. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Apps install the repo as a git dependency pinned to a commit, so the built output ships in the commit. The build drops source maps, whose sources are not in the installed package. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Arguments were sanitized one by one before formatting, so a value that takes its shape only once formatted (split across %s arguments, a custom inspect or toString) reached the exporter unscrubbed, and a Map or Set printed its entries past the key redaction. Collections are now expanded before the field sanitizer runs, and the formatted message is scrubbed as the last step, with long digit runs included. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI runs the type check, the tests and the dist/ check on pull requests and pushes to main, read-only and with pinned actions. The README covers install by commit, the options, the two-layer scrub and the rules for an app using the package. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ntry Console arguments are now reduced to the logger's JSON form before formatting, so a function's properties and source, and Map, Set or byte view contents (including inside a toJSON result) are never exported, and a huge array is read only up to the sanitizer's breadth. A test pins that the SDK's console diag logger bypasses the capture. bullmqTelemetry moves to the ./bullmq entry, so the main entry's types never name bullmq; the optional peer is ^6.3.8, as bullmq-otel needs. A test typechecks the built main entry without bullmq, skipLibCheck off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The sanitizer's output is now inert in both log and console capture. A
function is dropped (its own toJSON never runs), what any toJSON
returns is walked like any value, a Map, Set or RegExp becomes {} and a
byte view { bytes } whatever toJSON or own properties it carries, and
a date is read with the built-in methods. An error's message and
Prisma's meta target are copied only as strings, and a non-string
message never reaches the stack header. A table test covers each hook
at the top level, nested, in an array, Map, Set and toJSON result.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A primitive toJSON result (a Decimal's string) costs no depth. - Console capture puts an Error's safe message where %s takes it. - redactKeys also redacts span attributes, by whole name or last segment; the agent's layer 2 does not know them. - startTelemetry: the first call's options win whether or not it exports, with a telemetry_restart_ignored warning, and options it cannot read log telemetry_start_failed instead of rejecting. - Tests pin the inert format-arg rendering and assert the hooks table never passes by failing closed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README lists every exported type, says how an app checks an instrumentation it passes in, and states that layer 2 does not repeat redactKeys. CI cancels superseded runs on pull requests only, so every main commit gets a verdict. dist/ is marked generated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… keys startTelemetry validates and copies its options before it changes any state, then applies the log scope, error hook and redact keys in one step, so a failed call leaves nothing half-applied. A redact key that is not a string is dropped with one invalid_redact_key warning naming only its type; the valid keys still apply. Unreadable options on a later call return the running config. An app key matches a span attribute's last segment only outside OpenTelemetry's namespaces, so `name` never redacts db.system.name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An app key now matches a span attribute's last segment everywhere except a fixed list of attributes the instrumentations, BullMQ and Prisma emit (db.system.name, http.route, bullmq.job.id, ...), so an attribute the app sets under an OpenTelemetry namespace is still redacted. Content-bearing attributes stay off the list. Prisma's bare name, method and model are matched by whole name, as documented. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Member
Author
QA Check Report — codelittinc/otel-node
|
victor-carv
marked this pull request as ready for review
October 13, 2026 02:18
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The first version of
@codelittinc/otel-node: one in-process OpenTelemetry SDK for every Carbon Node app. It is extracted from Backbone'spackages/telemetry, so Backbone's tested behaviour is the starting point; its app-specific constants became options. Part of codelittinc/carbon-backbone#825 (Phase 6, the shared package).OTEL_EXPORTER_OTLP_ENDPOINTis set. With no endpoint, no SDK module loads.startTelemetrynever throws or rejects, andshutdownTelemetryis bounded.src/scrub-contract.json) now lives here; the agent's layer 2 incarbon-observabilityfollows it.captureConsole()sendsconsole.warnandconsole.errorthrough the same scrub, so an app that logs withconsoleneeds no rewrite. Console capture never exports anythinglogwould not..is the main API../registeris fornode --importwith no options../bullmqholds the BullMQ telemetry.bullmq^6.3.8 is the only optional peer../url-credentialsis browser-safe: no Node and no OTel imports../testingholds the test helpers.serviceNamelogScopemetricViews(instrument prefix plus an allowed-label list)instrumentations: app-supplied, so Prisma matches the app's versionredactKeys: adds keys to the default list and never removes one. It also applies to span attribute keys: a whole name anywhere, or a last segment unless the attribute is one of 54 listed attributes the package's instrumentations emit (such ashttp.routeordb.system.name). Anything unlisted fails closedonErrorignoredPathsdist/is committed and marked as generated. CI fails when it differs from a fresh build. There is no license, by decision: the repo is public only so apps can install it without a token.Changes from Backbone at 6a948f21 that its move (next PR) will declare
@codelittinc/otel-node, and Prisma instrumentation registers after pg.logScope: "backbone".lognow scrubs values it used to leak: functions, and Maps, Sets, RegExps or Date subclasses with their owntoJSON.meta.targetitems, are scrubbed too.%dwith a Date).{ bytes }.Ordinary values log byte for byte as before.
Testing
pnpm typecheckpasses.pnpm test: 38 files, 947 tests, all passing. Moved Backbone tests keep their assertions, with Backbone's values passed as options. Two assertion lines changed: one now imports from./bullmq, and the lockstep check now covers this package's own manifest. The new tests cover:./url-credentialsimport graph./bullmqtypes without bullmq installedlogand console capturepnpm buildmatches the committeddist/.git+file:clone works with no build step: every entry imports, andregisterboots the SDK.Not yet verified: the token-free
github:install, which needs this branch on GitHub, and Backbone's own suite against the package, which is the next PR.🤖 Generated with Claude Code