{"_id":"@abheektripathy/internal-utils","name":"@abheektripathy/internal-utils","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@abheektripathy/internal-utils","version":"0.1.0","description":"Internal utilities for logging and tracing with OpenTelemetry","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"},"./tracing":{"import":"./dist/tracing/index.js","types":"./dist/tracing/index.d.ts"}},"scripts":{"build":"tsc","clean":"rm -rf dist","test":"node --import tsx --test src/**/*.test.ts","typecheck":"tsc --noEmit"},"keywords":["logging","tracing","opentelemetry"],"license":"MIT","engines":{"node":">=20.0.0"},"dependencies":{"@opentelemetry/api":"^1.9.0","@opentelemetry/core":"^2.5.1","@opentelemetry/api-logs":"^0.212.0","@opentelemetry/sdk-node":"^0.212.0","@opentelemetry/sdk-trace-base":"^2.5.1","@opentelemetry/sdk-logs":"^0.212.0","@opentelemetry/sdk-metrics":"^2.5.1","@opentelemetry/exporter-trace-otlp-proto":"^0.212.0","@opentelemetry/exporter-metrics-otlp-proto":"^0.212.0","@opentelemetry/exporter-logs-otlp-proto":"^0.212.0"},"devDependencies":{"@types/node":"^25.3.0","tsx":"^4.19.2","typescript":"^5.7.3"},"_id":"@abheektripathy/internal-utils@0.1.0","gitHead":"6a1b47e9c3a510cd9d1736da1b45645b3e06f9d0","_nodeVersion":"23.3.0","_npmVersion":"11.5.2","dist":{"integrity":"sha512-8OtefZ5q7raRdDnMPfIk+3e4vm33VsXumznYFQx28ky3buDx01236UA2iw1m5juYK+fpHQeddHODRG+jYPl+QQ==","shasum":"2b6c1cd65dce03b3098d8e2aa8e0a703f966eaa7","tarball":"https://registry.npmjs.org/@abheektripathy/internal-utils/-/internal-utils-0.1.0.tgz","fileCount":22,"unpackedSize":38042,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDVV9ziEKOKzNqVaeN68CA/vhR9auIcIjaw+pLbsiyXAwIhAOpBwiNr20tqNSvbUdZ0y/BE4v06F74yb3MvB7fpdGJA"}]},"_npmUser":{"name":"abheektripathy","email":"abheek.tripathy@gmail.com"},"directories":{},"maintainers":[{"name":"abheektripathy","email":"abheek.tripathy@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/internal-utils_0.1.0_1771922648742_0.8681664252986576"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-24T08:44:08.674Z","0.1.0":"2026-02-24T08:44:08.890Z","modified":"2026-02-24T08:44:09.062Z"},"maintainers":[{"name":"abheektripathy","email":"abheek.tripathy@gmail.com"}],"description":"Internal utilities for logging and tracing with OpenTelemetry","keywords":["logging","tracing","opentelemetry"],"license":"MIT","readme":"# internal-utils\n\nStructured logging and tracing for TypeScript services, built on OpenTelemetry.\n\n## Run Example\n```bash\nbun run ./src/example.ts \n```\n\n## Quick Start\n\n```typescript\nimport { TracingBuilder } from 'internal-utils';\n\nconst { logger, shutdown } = await new TracingBuilder()\n  .withJson(false)\n  .build();\n\nlogger.info(\"App started\", { version: \"1.0.0\" });\n\nawait shutdown();\n```\n\n## Initialization\n\n`TracingBuilder` configures the OpenTelemetry SDK and returns a logger and shutdown function.\n\n```typescript\nimport { TracingBuilder } from 'internal-utils';\n\nconst { logger, shutdown } = await new TracingBuilder()\n  .withJson(false)           // pretty output (colored). true = JSON lines (default)\n  .withOtel({\n    serviceName: 'my-service',\n    serviceVersion: '1.0.0',\n    endpointTraces: 'http://localhost:4318/v1/traces',\n    endpointMetrics: 'http://localhost:4318/v1/metrics',\n    endpointLogs: 'http://localhost:4318/v1/logs',\n  })\n  .build();\n```\n\nLogs always print to the console. When OTLP endpoints are configured, logs/traces/metrics are also exported to your collector.\n\n## Logging\n\nSix levels: `trace`, `debug`, `info`, `warn`, `error`, `fatal`. Second argument is structured fields.\n\n```typescript\nlogger.info(\"User logged in\", { userId: 123, action: \"login\" });\nlogger.error(\"Connection lost\", { host: \"db.internal\", retries: 3 });\n```\n\n### Child Loggers\n\n`child()` creates a new logger that merges persistent fields into every log.\n\n```typescript\nconst reqLogger = logger.child({ requestId: \"abc-123\", userId: \"user-42\" });\nreqLogger.info(\"Handling request\");     // includes requestId + userId\nreqLogger.warn(\"Slow query\", { ms: 480 }); // includes requestId + userId + ms\n```\n\n### Global Logger Access\n\n`getLogger(name)` works from any module after `build()` has been called. Before init, it returns a no-op logger (safe to call, does nothing).\n\n```typescript\nimport { getLogger } from 'internal-utils';\n\nconst logger = getLogger('payments');\nlogger.info(\"Payment processed\", { amount: 99.99 });\n```\n\n## Tracing\n\n### withSpan\n\nWraps an async operation in a span. Automatically ends the span, records exceptions, and sets error status. Logs inside the callback carry `trace_id` and `span_id`.\n\n```typescript\nimport { withSpan } from 'internal-utils';\n\nawait withSpan(\"order.process\", async (span) => {\n  span.setAttribute(\"orderId\", \"ord-123\");\n  await processOrder();\n});\n```\n\nWith options (`SpanKind`, initial attributes, custom tracer name):\n\n```typescript\nimport { SpanKind } from '@opentelemetry/api';\n\nawait withSpan(\"db.query\", {\n  kind: SpanKind.CLIENT,\n  attributes: { \"db.system\": \"postgres\" },\n}, async () => {\n  await db.query(sql);\n});\n```\n\n### withSpanSync\n\nSame as `withSpan` but for synchronous work.\n\n```typescript\nimport { withSpanSync } from 'internal-utils';\n\nconst isValid = withSpanSync(\"validate.input\", () => {\n  return input.length > 0;\n});\n```\n\n### Error Handling\n\nExceptions thrown inside `withSpan`/`withSpanSync` are recorded on the span, the span status is set to `ERROR`, and the error is re-thrown.\n\n```typescript\ntry {\n  await withSpan(\"order.charge\", async () => {\n    throw new Error(\"Payment declined\");\n  });\n} catch (err) {\n  logger.error(\"Charge failed\");\n}\n```\n\n### Nested Spans\n\nSpans nest automatically via context propagation.\n\n```typescript\nawait withSpan(\"http.request\", async () => {\n  // parent span\n\n  await withSpan(\"auth.validate\", async () => {\n    // child span\n  });\n\n  await withSpan(\"db.insert\", async () => {\n    // child span\n  });\n});\n```\n\n## Metrics\n\n`createMetrics(name)` returns a `Metrics` instance for creating instruments.\n\n```typescript\nimport { createMetrics } from 'internal-utils';\n\nconst m = createMetrics('my-service');\n```\n\n### Counter\n\nValues that only go up (requests, errors, orders).\n\n```typescript\nconst orderCount = m.counter(\"orders.total\");\norderCount.add(1, { type: \"new\" });\n```\n\n### UpDownCounter\n\nValues that go up and down (active connections, queue size).\n\n```typescript\nconst activeJobs = m.upDownCounter(\"jobs.active\");\nactiveJobs.add(1);   // job started\nactiveJobs.add(-1);  // job finished\n```\n\n### Histogram\n\nDistributions (latency, request size).\n\n```typescript\nconst duration = m.histogram(\"http.request.duration\", { unit: \"ms\" });\nduration.record(45.2, { method: \"GET\", route: \"/orders\" });\n```\n\nHistograms have a `.time()` helper that measures duration automatically:\n\n```typescript\n// Async — records elapsed time in ms\nawait duration.time({ method: \"POST\", route: \"/orders\" }, async () => {\n  await processOrder();\n});\n\n// Without attributes\nconst result = await duration.time(async () => {\n  return await db.query(sql);\n});\n\n// Sync\nconst parsed = duration.timeSync(() => JSON.parse(data));\n```\n\n### Gauge\n\nObserves a value via callback at export time.\n\n```typescript\nm.gauge(\"queue.size\", () => queue.length);\n```\n\n### Raw Meter Access\n\nFor advanced use cases, access the underlying OTel meter directly.\n\n```typescript\nimport { getMeter } from 'internal-utils';\n\nconst meter = getMeter('my-service');\nconst counter = meter.createCounter('custom_metric');\n```\n\n## Output Formats\n\n**JSON** (`withJson(true)`, default):\n```\n{\"timestamp\":\"2026-02-20T10:30:00.000Z\",\"level\":\"INFO\",\"message\":\"App started\",\"version\":\"1.0.0\"}\n```\n\n**Pretty** (`withJson(false)`):\n```\n2026-02-20T10:30:00.000Z  INFO   App started  version=1.0.0\n```\n\nPretty mode uses colored severity levels: TRACE (magenta), DEBUG (blue), INFO (green), WARN (yellow), ERROR/FATAL (red).\n\n## Example\n\nSee [`src/example.ts`](src/example.ts) for a runnable showcase of all features:\n\n```bash\nnpx tsx src/example.ts\n```\n\n## API Reference\n\n### TracingBuilder\n\n| Method | Description |\n|--------|-------------|\n| `.withJson(enabled)` | `true` = JSON lines (default), `false` = colored pretty output |\n| `.withOtel(params)` | Configure OTLP endpoints and service identity |\n| `.build()` | Start the SDK, returns `{ logger, shutdown }` |\n\n### OtelParams\n\n| Field | Description |\n|-------|-------------|\n| `serviceName` | Service name for the logger and resource |\n| `serviceVersion` | Service version |\n| `endpointTraces` | OTLP traces endpoint URL |\n| `endpointMetrics` | OTLP metrics endpoint URL |\n| `endpointLogs` | OTLP logs endpoint URL |\n\n### Logger\n\n| Method | Description |\n|--------|-------------|\n| `.trace(message, fields?)` | Log at TRACE level |\n| `.debug(message, fields?)` | Log at DEBUG level |\n| `.info(message, fields?)` | Log at INFO level |\n| `.warn(message, fields?)` | Log at WARN level |\n| `.error(message, fields?)` | Log at ERROR level |\n| `.fatal(message, fields?)` | Log at FATAL level |\n| `.child(fields)` | Create child logger with persistent fields |\n\n### Metrics\n\n| Method | Description |\n|--------|-------------|\n| `.counter(name, options?)` | Create a counter (monotonically increasing) |\n| `.histogram(name, options?)` | Create a `TimedHistogram` (distributions + `.time()` helper) |\n| `.upDownCounter(name, options?)` | Create an up-down counter |\n| `.gauge(name, callback, options?)` | Create an observable gauge |\n\n### TimedHistogram\n\n| Method | Description |\n|--------|-------------|\n| `.record(value, attrs?)` | Record a value manually |\n| `.time([attrs], fn)` | Run async function, record elapsed ms |\n| `.timeSync([attrs], fn)` | Run sync function, record elapsed ms |\n\n### Functions\n\n| Function | Description |\n|----------|-------------|\n| `getLogger(name)` | Get a named logger (works after `build()`) |\n| `createMetrics(name)` | Create a `Metrics` instance bound to a meter |\n| `getTracer(name)` | Get an OpenTelemetry tracer |\n| `getMeter(name)` | Get an OpenTelemetry meter (raw access) |\n| `withSpan(name, [options], fn)` | Run async function inside a traced span |\n| `withSpanSync(name, [options], fn)` | Run sync function inside a traced span |\n\n### TraceOptions\n\nExtends OpenTelemetry `SpanOptions` with:\n\n| Field | Description |\n|-------|-------------|\n| `tracer` | Custom tracer name (default: `\"app\"`) |\n| `kind` | `SpanKind.SERVER`, `CLIENT`, `INTERNAL`, etc. |\n| `attributes` | Initial span attributes |\n","readmeFilename":"README.md","_rev":"1-38155696d815aa16b6f43676b5f26f81"}