@zephyrcloud/telemetry
v0.1.1
Published
Internal OpenTelemetry package for Zephyr packages
Readme
@zephyrcloud/telemetry
Internal OpenTelemetry package for Zephyr JS packages (Node runtimes). Provides a unified, configurable interface for sending traces, metrics, and logs to OTEL collectors with secure authentication.
For edge runtimes (Cloudflare Workers, Fastly, Akamai, Netlify, Lambda@Edge), use @zephyrcloud/telemetry-edge.
Installation
pnpm add @zephyrcloud/telemetryUsage
Basic Setup
import { TelemetryProvider } from "@zephyrcloud/telemetry";
const telemetry = new TelemetryProvider({
service: {
name: "my-service",
version: "1.0.0",
environment: "production",
},
collectorEndpoint: "https://otel-collector.example.com",
auth: {
bearerToken: process.env.OTEL_AUTH_TOKEN,
},
});
// Initialize before using
telemetry.initialize();
// Get a tracer
const tracer = telemetry.getTracer("my-component");
// Create spans
tracer.startActiveSpan("my-operation", (span) => {
// Do work...
span.end();
});
// Shutdown on exit
process.on("SIGTERM", async () => {
await telemetry.shutdown();
});Authentication Options
The package supports multiple authentication methods:
// Bearer token
auth: {
bearerToken: "your-token",
}
// API key
auth: {
apiKey: "your-api-key",
apiKeyHeader: "x-api-key", // optional, defaults to "x-api-key"
}
// Custom headers
auth: {
headers: {
"X-Custom-Auth": "value",
},
}Tracing Functions (Signature-Preserving)
Wrap existing functions with tracing while preserving their type signatures:
import { traced, tracedSync, createTracingHelpers } from "@zephyrcloud/telemetry";
// Wrap an async function - signature is preserved
const fetchUser = traced(
tracer,
"fetchUser",
async (userId: string, includeProfile: boolean) => {
const response = await fetch(`/api/users/${userId}?profile=${includeProfile}`);
return response.json();
},
{
// Extract attributes from function arguments
extractAttributes: (userId) => ({ "user.id": userId }),
},
);
// Call with original signature - fully typed!
const user = await fetchUser("123", true);
// Wrap a sync function
const parseConfig = tracedSync(
tracer,
"parseConfig",
(path: string) => JSON.parse(fs.readFileSync(path, "utf-8")),
{
extractAttributes: (path) => ({ "config.path": path }),
},
);
const config = parseConfig("/etc/app/config.json");Create bound helpers for cleaner code:
// Create helpers bound to a tracer
const { traced, tracedSync } = createTracingHelpers(telemetry.getTracer("user-service"));
// No need to pass tracer each time
const fetchUser = traced("fetchUser", async (id: string) => {
return db.users.find(id);
});
const validateUser = tracedSync("validateUser", (user: User) => {
return schema.parse(user);
});Inline Span Helpers
For ad-hoc spans within existing code:
import { withSpan, addSpanAttributes, recordError } from "@zephyrcloud/telemetry";
// Automatically handles span lifecycle and errors
const result = await withSpan(tracer, "fetch-user", async (span) => {
addSpanAttributes({ "user.id": userId });
const user = await fetchUser(userId);
return user;
});
// Record errors on current span
try {
await riskyOperation();
} catch (error) {
recordError(error);
throw error;
}Semantic Conventions
Use the provided semantic conventions for consistent attribute naming:
import {
ATTR_SERVICE_NAME,
ATTR_ZEPHYR_CUSTOMER_ID,
ATTR_ZEPHYR_REQUEST_ID,
OperationType,
} from "@zephyrcloud/telemetry";
span.setAttributes({
[ATTR_ZEPHYR_CUSTOMER_ID]: customerId,
[ATTR_ZEPHYR_REQUEST_ID]: requestId,
});Metrics
const meter = telemetry.getMeter("my-service");
const requestCounter = meter.createCounter("requests", {
description: "Number of requests",
});
const latencyHistogram = meter.createHistogram("request_latency", {
description: "Request latency in milliseconds",
unit: "ms",
});
// Record metrics
requestCounter.add(1, { endpoint: "/api/users" });
latencyHistogram.record(42, { endpoint: "/api/users" });Logs
const logger = telemetry.getLogger("my-service");
logger.emit({
severityText: "INFO",
body: "User logged in",
attributes: { userId: "123" },
});Configuration
| Option | Type | Required | Description |
| --------------------- | ------- | -------- | --------------------------------------------------- |
| service.name | string | Yes | Service name |
| service.version | string | No | Service version |
| service.environment | string | No | Deployment environment |
| service.instanceId | string | No | Unique instance ID (auto-generated if not provided) |
| service.attributes | object | No | Additional resource attributes |
| collectorEndpoint | string | Yes | Base URL of the OTEL collector |
| auth | object | No | Authentication configuration |
| traces.enabled | boolean | No | Enable traces (default: true) |
| traces.endpoint | string | No | Custom traces endpoint |
| metrics.enabled | boolean | No | Enable metrics (default: true) |
| metrics.endpoint | string | No | Custom metrics endpoint |
| logs.enabled | boolean | No | Enable logs (default: true) |
| logs.endpoint | string | No | Custom logs endpoint |
| debug | boolean | No | Enable debug logging |
Security
- Auth credentials are never logged
- Use environment variables for sensitive values
- The
maskConfig()helper redacts sensitive fields for safe logging
Development
# Install dependencies
pnpm install
# Build
pnpm build
# Type check
pnpm typecheck
# Run tests
pnpm test