The @packages/observability package provides comprehensive observability tools for Answer Overflow, including error tracking with Sentry, telemetry with Axiom, and OpenTelemetry integration.
Installation
npm install @packages/observability
Overview
This package provides:
- Sentry integration for error tracking
- Axiom integration for logs, metrics, and traces
- OpenTelemetry for distributed tracing
- Effect integration for functional error handling
- Convex-specific observability utilities
Sentry Error Tracking
Initialization
import { initSentry } from "@packages/observability/sentry";
initSentry({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV || "development",
release: process.env.VERCEL_GIT_COMMIT_SHA,
sampleRate: 0.25,
tracesSampleRate: 0.1,
profilesSampleRate: 0.1,
});
Sentry configurationdsn
string | undefined
required
Sentry DSN (Data Source Name)
Environment name (e.g., “production”, “development”)
Release version or commit SHA
Error sampling rate (0-1, default: 0.25)
Traces sampling rate (0-1, default: 0.1)
Profiles sampling rate (0-1, default: 0.1)
Capturing Exceptions
import { captureException } from "@packages/observability/sentry";
try {
// Risky operation
await processUserData(userId);
} catch (error) {
captureException(error, {
tags: {
module: "user-processing",
userId,
},
extra: {
userData: { id: userId },
},
user: {
id: userId,
username: "john_doe",
},
});
}
Capture an exception to SentryTags for filtering/grouping errors
Capturing Messages
import { captureMessage } from "@packages/observability/sentry";
captureMessage("User completed onboarding", "info");
captureMessage("Rate limit exceeded", "warning");
captureMessage("Critical system failure", "error");
User Context
import { setUser, setContext, setTag } from "@packages/observability/sentry";
// Set user context
setUser({
id: "user_123",
username: "john_doe",
});
// Clear user context
setUser(null);
// Set custom context
setContext("server", {
serverId: "123456789",
serverName: "My Server",
});
// Set tags
setTag("feature", "chat");
Effect Integration
import {
tapErrorCauseToSentry,
captureEffectCause,
} from "@packages/observability/sentry-effect";
import { Effect } from "effect";
const riskyOperation = Effect.gen(function* () {
// Operation that might fail
}).pipe(
tapErrorCauseToSentry({
tags: { operation: "risky" },
})
);
// Or manually capture
const result = await Effect.runPromise(
riskyOperation.pipe(
Effect.catchAllCause((cause) => {
captureEffectCause(cause, {
tags: { severity: "high" },
});
return Effect.fail(cause);
})
)
);
Sentry Layer (Effect)
import { createSentryLayer } from "@packages/observability/sentry";
import { Layer } from "effect";
const SentryLayer = createSentryLayer({
dsn: process.env.SENTRY_DSN,
environment: "production",
});
const program = Effect.gen(function* () {
// Your program
}).pipe(Effect.provide(SentryLayer));
Axiom Telemetry
Initialization
import { createAxiomLayer } from "@packages/observability/axiom";
import { Effect, Layer } from "effect";
import * as Duration from "effect/Duration";
const AxiomLayer = createAxiomLayer({
apiToken: process.env.AXIOM_API_TOKEN!,
tracesDataset: "traces",
logsDataset: "logs",
metricsDataset: "metrics",
serviceName: "answer-overflow",
serviceVersion: "1.0.0",
environment: "production",
tracesExportInterval: Duration.seconds(5),
logsExportInterval: Duration.seconds(1),
metricsExportInterval: Duration.seconds(10),
});
Axiom configurationService name for telemetry
Environment (default: “production”)
Axiom domain (default: “api.axiom.co”)
How often to export traces
How often to export metrics
Using Axiom Layer
import { Effect } from "effect";
const program = Effect.gen(function* () {
yield* Effect.log("Starting operation");
// Your application logic
const result = yield* processData();
yield* Effect.log("Operation completed");
return result;
}).pipe(Effect.provide(AxiomLayer));
await Effect.runPromise(program);
OpenTelemetry
Effect OpenTelemetry
import { createOtelLayer } from "@packages/observability/effect-otel";
import * as Duration from "effect/Duration";
const OtelLayer = createOtelLayer({
url: "https://api.axiom.co/v1/traces",
headers: {
Authorization: `Bearer ${process.env.AXIOM_API_TOKEN}`,
"X-Axiom-Dataset": "traces",
},
serviceName: "answer-overflow",
exportInterval: Duration.seconds(5),
});
const program = Effect.gen(function* () {
// Automatically traced
yield* Effect.log("Processing request");
const result = yield* fetchData();
return result;
}).pipe(Effect.provide(OtelLayer));
Convex OpenTelemetry
For Convex-specific telemetry:
import { createConvexOtelLayer } from "@packages/observability/convex-effect-otel";
const ConvexOtelLayer = createConvexOtelLayer({
endpoint: process.env.OTEL_ENDPOINT!,
headers: {
Authorization: `Bearer ${process.env.OTEL_TOKEN}`,
},
});
Complete Setup Example
import { initSentry, createSentryLayer } from "@packages/observability/sentry";
import { createAxiomLayer } from "@packages/observability/axiom";
import { Effect, Layer } from "effect";
// Initialize Sentry (non-Effect code)
initSentry({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV!,
});
// Create Effect layers
const SentryLayer = createSentryLayer({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV!,
});
const AxiomLayer = createAxiomLayer({
apiToken: process.env.AXIOM_API_TOKEN!,
tracesDataset: "traces",
logsDataset: "logs",
metricsDataset: "metrics",
serviceName: "answer-overflow",
environment: process.env.NODE_ENV!,
});
// Combine layers
const ObservabilityLayer = Layer.mergeAll(
SentryLayer,
AxiomLayer
);
// Use in your application
const app = Effect.gen(function* () {
yield* Effect.log("Application started");
try {
yield* runServer();
} catch (error) {
yield* Effect.logError("Server error", error);
}
}).pipe(Effect.provide(ObservabilityLayer));
await Effect.runPromise(app);
Best Practices
- Initialize early: Set up observability at app startup
- Use structured logging: Include context in logs
- Tag appropriately: Use tags for filtering and grouping
- Sample strategically: Adjust sample rates for production
- Capture user context: Add user info to error reports
- Flush on shutdown: Call
flush() before process exits
Exports
Sentry error tracking utilities
Sentry integration for Effect
Axiom telemetry integration
Effect OpenTelemetry integration
Convex-specific OpenTelemetry
Dependencies
@sentry/node - Sentry SDK for Node.js
@sentry/opentelemetry - Sentry OpenTelemetry integration
@effect/opentelemetry - Effect OpenTelemetry integration
@opentelemetry/sdk-node - OpenTelemetry SDK
effect - Effect library