@theholocron/observability
v0.4.0
Published
Logging, error tracking, and analytics — one interface, swappable backends.
Downloads
20,122
Maintainers
Readme
@theholocron/observability
Logging, error tracking, and analytics — one interface, swappable backends.
Installation
pnpm install @theholocron/observabilityUsage
See the documentation for the API.
Installation
pnpm add @theholocron/observabilityThen add the peer dependencies for the subpaths you use — they are all optional:
| Subpath | Peers |
| -------------------------------------- | -------------------------------------- |
| @theholocron/observability/core | none |
| @theholocron/observability/logger | pino, pino-pretty, @axiomhq/pino |
| @theholocron/observability/errors | @sentry/node |
| @theholocron/observability/analytics | posthog-node |
| @theholocron/observability/testing | vitest |
Usage
Application modules depend on the interfaces from /core, never a concrete
adapter:
import type { Logger, ErrorSink } from "@theholocron/observability/core";
export function deploy(deps: { logger: Logger; errors: ErrorSink }) {
deps.logger.info({ target: "production" }, "deploying");
}The process entry point wires the adapters and is the only place that reads credentials:
import { createLogger } from "@theholocron/observability/logger";
import { NoopErrorSink } from "@theholocron/observability/core";
import { SentrySink } from "@theholocron/observability/errors";
const { logger, runId } = createLogger({ level: "info" });
const errors = process.env.SENTRY_DSN ? new SentrySink() : new NoopErrorSink();
errors.init({
dsn: process.env.SENTRY_DSN ?? "",
release: "[email protected]",
environment: process.env.CI ? "ci" : "local",
tags: { runId },
});
deploy({ logger, errors });For the browser, an edge runtime, or React Native, import
@theholocron/observability/core (zero dependencies) and use ConsoleLogger
from /logger in place of Pino.
Testing code that depends on Logger / ErrorSink / AnalyticsSink? Use the
spy doubles from /testing instead of hand-rolling one per repo:
import { fakeLogger, fakeErrorSink } from "@theholocron/observability/testing";
const log = fakeLogger();
const errors = fakeErrorSink();
await deploy({ logger: log, errors, target: "production" });
expect(log.info).toHaveBeenCalledWith(expect.objectContaining({ target: "production" }), "deploying");
expect(errors.captureException).not.toHaveBeenCalled();For a non-vitest context that just needs a Logger with no output (an
example, a non-test opt-out path), use NoopLogger from /core — it
discards everything, child() returns itself.
See the documentation for the full API.
Development
| Script | Command |
| -------------------- | ------------------------ |
| pnpm build | tsdown |
| pnpm lint | holocron run lint |
| pnpm test | holocron run test |
| pnpm test:coverage | vitest run --coverage |
| pnpm typecheck | holocron run typecheck |
| pnpm audit | knip |
Releases
Automated via semantic-release. See the releases page and CHANGELOG.md.
