@cobre-npm/library-nodejs-telemetry
v1.4.0
Published
Nodejs Cobre telemetry library with OpenTelemetry
Keywords
Readme
Telemetry library Node.js
A specialized Node.js library for OpenTelemetry-based telemetry and observability. This library provides comprehensive tracing and logging capabilities using OpenTelemetry, designed to streamline observability across your Node.js applications.
Features
- Distributed Tracing - Complete OpenTelemetry tracing with AWS X-Ray, W3C Trace Context, and Baggage propagation
- Structured Logging - OpenTelemetry-based logging with automatic context propagation
- FaaS Metrics - Opt-in
faas.*golden signals for AWS Lambda handlers, matching the Python telemetry library - AWS Integration - Built-in support for AWS Lambda, AWS SDK, Express, and HTTP instrumentation
- OTLP Export - Native support for OTLP (OpenTelemetry Protocol) exporters for traces and logs
Installation
npm install @cobre-npm/library-nodejs-telemetry
# or
pnpm install @cobre-npm/library-nodejs-telemetryExamples
Tracing
Initialize OpenTelemetry tracing in your application to enable distributed tracing across your services.
import { TracingAdapter } from '@cobre-npm/library-nodejs-telemetry/dist'
import packageJson from './package.json'
const serviceName = 'my-service'
const serviceVersion = packageJson.version
// Initialize tracing with service configuration
TracingAdapter.init({ serviceName, serviceVersion })
// Graceful shutdown
process.on('SIGTERM', async () => {
await TracingAdapter.shutdown()
process.exit(0)
})Features:
- Automatic instrumentation for HTTP, Express, AWS Lambda, and AWS SDK
- AWS X-Ray propagation support
- W3C Trace Context and Baggage propagation
- OTLP HTTP exporter for traces
Supported Instrumentations:
@opentelemetry/instrumentation-http- HTTP requests@opentelemetry/instrumentation-express- Express.js framework@opentelemetry/instrumentation-aws-lambda- AWS Lambda functions@opentelemetry/instrumentation-aws-sdk- AWS SDK calls
FaaS Metrics
TracingAdapter.init wires an OTLP metric exporter, but instruments have to be recorded
explicitly. withFaasMetrics wraps an AWS Lambda handler and records the faas.* golden
signals the New Relic "Golden Signals - FaaS (Lambda)" dashboard queries by name.
Opt-in and additive: an unwrapped handler behaves exactly as before, and tracing is
untouched — AwsLambdaInstrumentation still owns the invocation span.
import { withFaasMetrics } from '@cobre-npm/library-nodejs-telemetry/dist'
const handler = async (event, context) => {
return { statusCode: 200, body: 'ok' }
}
// Apply outermost, on the function AWS invokes
exports.handler = withFaasMetrics(handler)Metrics recorded per invocation:
| Metric | Type | Unit | When |
| ------ | ---- | ---- | ---- |
| faas.invoke_duration | histogram | s | always |
| faas.invocations | counter | — | handler succeeded |
| faas.errors | counter | — | handler threw, or returned a 5xx statusCode |
| faas.coldstarts | counter | — | first invocation in the container |
Attributes on every data point:
faas.trigger (http, pubsub, datasource, timer or other), inferred from the event
shape. HTTP triggers — API Gateway v1 and v2, Function URLs and Application Load Balancer —
add http.request.method and http.route; SQS and SNS triggers add the messaging.*
attributes.
http.route is the route template (/v1/items/{id}), taken from resource on v1 or
routeKey on v2, and is omitted when the event carries no template. The resolved path is
never used: as a metric dimension it produces one time series per path parameter value. Put
the resolved path on the span instead, where cardinality is free.
Every invocation also adds http.response.status_code when the handler returned one, and
error.type when it failed. These land on faas.invoke_duration as well as on the
counters, so the histogram is partitioned by status code and error class — useful for
reading the latency of 5xx responses separately, at the cost of extra series on the
highest-volume instrument.
Metrics are force-flushed before the handler returns, since a Lambda container freezes immediately afterwards and the periodic reader would never export them. That flush is a request to the collector, so it adds to the invocation's billed duration.
Requirements:
TracingAdapter.initmust have run before the first invocation. Instruments are created lazily for this reason — creating them earlier would bind them to the API's no-op provider.- Set
COBRE_ENVon the function, ordeployment.environment.nameis reported asUNKNOWNand the dashboard's environment filter will not list the service.
Metric temporality needs no configuration on Lambda: TracingAdapter.init defaults
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE to delta when
AWS_LAMBDA_FUNCTION_NAME is present, because New Relic expects delta and cumulative
counters reset unpredictably as Lambda recycles containers. Setting the variable explicitly
still wins, and long-lived services outside Lambda keep the exporter's own default.
flushFaasMetrics() and getFaasInstruments() are also exported, for handlers that record
their own data points onto the same series outside the wrapper.
Logging
The logging utility provides structured logging capabilities with OpenTelemetry support. It follows RFC5424 severity levels:
emerg: 0 - Emergencyalert: 1 - Alertcrit: 2 - Criticalerror: 3 - Errorwarning: 4 - Warningnotice: 5 - Noticeinfo: 6 - Informationaldebug: 7 - Debug
Basic Usage
import { log } from '@cobre-npm/library-nodejs-telemetry/dist'
// Log different severity levels
log.info({ message: 'Application started', content: { port: 3000 } })
log.error({ message: 'Failed to connect', content: { error: 'Connection timeout' } })
log.warning({ message: 'Rate limit approaching', content: { remaining: 10 } })
log.debug({ message: 'Processing request', content: { requestId: '123' } })Initialize OpenTelemetry Logging
Initialize OpenTelemetry logging with your service name and version:
import { log } from '@cobre-npm/library-nodejs-telemetry/dist'
import packageJson from './package.json'
const serviceName = 'my-service'
const serviceVersion = packageJson.version
// Initialize OpenTelemetry logging
log.initOpentelemetry(serviceName, serviceVersion)
// Or use automatic initialization with default values
log.initializeLogger()
// Or specify custom service name and version
log.initializeLogger('my-service', '1.0.0')Express Middleware
Use the built-in Express middleware to automatically extract and store user context from JWT tokens:
import express from 'express'
import { log } from '@cobre-npm/library-nodejs-telemetry/dist'
const app = express()
// Add logging middleware before your routes
app.use(log.loggerMiddleware)
app.get('/api/users', (req, res) => {
// User context (email, userId, clientId) is automatically available in logs
log.info({ message: 'Fetching users' })
res.json({ users: [] })
})The middleware automatically extracts:
email- User email from JWT tokenname(userId) - User name/ID from JWT tokenclient_id- Client ID from request headers
All subsequent logs will include this context automatically.
Logging with Attributes
Add custom attributes to your logs:
import { log } from '@cobre-npm/library-nodejs-telemetry/dist'
log.info({
message: 'User action performed',
content: { action: 'purchase', amount: 100 },
attrs: {
userId: 'user123',
sessionId: 'session456',
feature: 'checkout'
}
})Complete Setup Example
Here's a complete example of setting up both tracing and logging in an Express application:
import express from 'express'
import dotenv from 'dotenv'
import { TracingAdapter, log } from '@cobre-npm/library-nodejs-telemetry/dist'
import packageJson from './package.json'
// Load environment variables
dotenv.config()
const app = express()
const serviceName = 'my-service'
const serviceVersion = packageJson.version
// Initialize OpenTelemetry Tracing
TracingAdapter.init({ serviceName, serviceVersion })
// Initialize OpenTelemetry Logging
log.initOpentelemetry(serviceName, serviceVersion)
// Or use automatic initialization
log.initializeLogger(serviceName, serviceVersion)
// Add logging middleware
app.use(log.loggerMiddleware)
// Your routes
app.get('/health', (req, res) => {
log.info({ message: 'Health check requested' })
res.json({ status: 'ok' })
})
// Graceful shutdown
process.on('SIGTERM', async () => {
log.info({ message: 'Shutting down gracefully' })
await TracingAdapter.shutdown()
process.exit(0)
})
app.listen(3000, () => {
log.info({ message: 'Server started', content: { port: 3000 } })
})logMaskedObject
Utility function to mask sensitive data in logs while keeping a portion of the original value for identification purposes.
import { logMaskedObject } from '@cobre-npm/library-nodejs-telemetry/dist'
logMaskedObject(
{
username: 'DanielHernandez',
email: '[email protected]',
creditCard: '1234567890123456',
phone: '+1234567890'
},
5 // Keep last 5 characters
)Output (logged as info):
{
"username": "*****-*****-*****-*****andez",
"email": "*****-*****-*****-*****@example.com",
"creditCard": "*****-*****-*****-3456",
"phone": "*****-*****-*****-67890"
}Environment Variables
This library uses OpenTelemetry for all telemetry operations. None of the variables below are required — the library falls back to a default service configuration when they are absent.
| Variable | Read by | Effect |
| -------- | ------- | ------ |
| COBRE_ENV | tracing, logging | Value of the deployment.environment.name resource attribute. Defaults to UNKNOWN. |
| OTEL_EXPORTER_OTLP_ENDPOINT | OTLP exporters | Collector to export traces, logs and metrics to. |
| OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE | metric exporter | Defaults to delta on AWS Lambda, set by TracingAdapter.init. See FaaS Metrics. |
| OTEL_SEMCONV_STABILITY_OPT_IN | HTTP instrumentation | Defaults to http when unset. |
| NODE_ENV | logging | When it contains local, logs are also mirrored to the console. |
Architecture
Tracing Module
src/tracing/
├── adapter/
│ ├── tracing.adapter.ts # Main tracing adapter
│ └── config.ts # Service configuration
├── domain/
│ └── port/
│ └── out/
│ └── tracing.port # Port interface
└── entrypoints/
└── index.ts # Public exportsMetrics Module
src/metrics/
├── adapter/
│ ├── faasMeters.ts # faas.* instruments and force flush
│ └── faasMetrics.adapter.ts # withFaasMetrics handler wrapper
├── utils/
│ └── faasAttributes.utils.ts # Trigger classification and attribute keys
└── entrypoints/
└── index.ts # Public exportsLogging Module
src/logging/
├── adapter/
│ └── logging.adapter.ts # Main logging adapter
├── domain/
│ ├── interfaces/
│ │ └── tokenDecoded.interface.ts # Token interface
│ └── loggerProps.ts # Logger properties
└── entrypoints/
└── index.ts # Public exportsDependencies
OpenTelemetry Packages
@opentelemetry/api- Core OpenTelemetry API@opentelemetry/api-logs- Logging API@opentelemetry/sdk-node- Node.js SDK@opentelemetry/sdk-logs- Logging SDK@opentelemetry/exporter-trace-otlp-http- OTLP HTTP trace exporter@opentelemetry/exporter-logs-otlp-proto- OTLP Proto log exporter@opentelemetry/instrumentation-*- Various instrumentations@opentelemetry/propagator-aws-xray-lambda- AWS X-Ray propagation@opentelemetry/resources- Resource detection@opentelemetry/semantic-conventions- Semantic conventions
Other Dependencies
jwt-decode- JWT token decoding
Build
npm run buildThis will create the compiled output in the dist/ directory.
License
ISC
