diff --git a/charts/lobu/templates/gateway-deployment.yaml b/charts/lobu/templates/gateway-deployment.yaml index 05efcca79..484a325b8 100644 --- a/charts/lobu/templates/gateway-deployment.yaml +++ b/charts/lobu/templates/gateway-deployment.yaml @@ -160,8 +160,8 @@ spec: optional: true {{- if .Values.tempo.enabled }} - # Tempo distributed tracing endpoint - - name: TEMPO_ENDPOINT + # OpenTelemetry OTLP endpoint for distributed tracing + - name: OTEL_EXPORTER_OTLP_ENDPOINT value: "http://{{ .Release.Name }}-tempo:4318/v1/traces" {{- end }} diff --git a/charts/lobu/values.yaml b/charts/lobu/values.yaml index 2e189fb9e..1312f2517 100644 --- a/charts/lobu/values.yaml +++ b/charts/lobu/values.yaml @@ -263,9 +263,10 @@ grafana: namespace: "monitoring" # Namespace where Grafana is installed lokiUrl: "http://loki:3100" # URL to Loki service -# Grafana Tempo for distributed tracing (waterfall view) +# Grafana Tempo for distributed tracing +# When enabled, sets OTEL_EXPORTER_OTLP_ENDPOINT automatically for gateway and workers tempo: - enabled: false # Enable for Chrome DevTools-style trace visualization + enabled: false # Tempo subchart configuration tempo: # Use local filesystem storage for simplicity (switch to S3/GCS in production) diff --git a/config/biome.config.json b/config/biome.config.json index 3d09b638a..031975bdd 100644 --- a/config/biome.config.json +++ b/config/biome.config.json @@ -20,7 +20,8 @@ "!**/node_modules/**", "!**/.astro/**", "!**/tmp/**", - "!**/provision-careops-watchers*" + "!**/provision-careops-watchers*", + "!**/*.css" ] }, "formatter": { @@ -98,6 +99,17 @@ } } }, + "css": { + "parser": { + "cssModules": true + }, + "formatter": { + "enabled": false + }, + "linter": { + "enabled": false + } + }, "javascript": { "formatter": { "jsxQuoteStyle": "double", diff --git a/package.json b/package.json index 1699e9c9f..fd5a48e68 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "docker:build": "docker build -f docker/Dockerfile.gateway -t lobu-gateway . && docker build -f docker/Dockerfile.worker -t lobu-worker .", "k8s:deploy": "helm upgrade --install lobu charts/lobu --dependency-update", "k8s:uninstall": "helm uninstall lobu", - "publish:packages": "cd packages/core && npm publish --access public && cd ../gateway && npm publish --access public && cd ../worker && npm publish --access public && cd ../cli && npm publish --access public", + "publish:packages": "cd packages/core && pnpm publish --access public --no-git-checks && cd ../gateway && pnpm publish --access public --no-git-checks && cd ../worker && pnpm publish --access public --no-git-checks && cd ../cli && pnpm publish --access public --no-git-checks", "stage:publish": "node scripts/stage-publish-package.mjs", "slack:manifest:print": "bun run scripts/slack-manifest.ts print", "slack:manifest:validate": "bun run scripts/slack-manifest.ts validate", diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index e90abf9c0..056478df7 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -424,6 +424,43 @@ export async function initCommand( } // "none" — no env var needed, gateway defaults to filesystem memory + // Observability — OTEL tracing endpoint + const { otelEndpoint } = await inquirer.prompt([ + { + type: "input", + name: "otelEndpoint", + message: + "OpenTelemetry collector endpoint? (leave empty to disable tracing)", + default: "", + }, + ]); + + if (otelEndpoint) { + envSecrets.push({ + envVar: "OTEL_EXPORTER_OTLP_ENDPOINT", + value: otelEndpoint, + }); + } + + // Observability — Sentry error reporting + const { enableSentry } = await inquirer.prompt([ + { + type: "confirm", + name: "enableSentry", + message: + "Help improve Lobu by sharing anonymous error reports with Sentry?", + default: true, + }, + ]); + + if (enableSentry) { + envSecrets.push({ + envVar: "SENTRY_DSN", + value: + "https://c5910e58d1a134d64ff93a95a9c535bb@o4507291398897664.ingest.us.sentry.io/4511097466781696", + }); + } + // Compute network domains from selected policy let allowedDomains: string; let disallowedDomains: string; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 4dfa593fd..6c5f9f160 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -17,6 +17,26 @@ export type { StoredConnection, } from "./agent-store"; export { findTemplateAgentId } from "./agent-store"; +// Agent Settings API response types (for UI consumers) +export type { + AgentConfigResponse, + AgentInfo, + CatalogProvider, + Connection, + McpConfig, + ModelOption, + ModelSelectionState, + PermissionGrant, + PrefillMcp, + PrefillSkill, + ProviderInfo, + ProviderState, + ProviderStatus, + Schedule, + SettingsSnapshot, + Skill, + SkillMcpServerInfo, +} from "./api-types"; export type { CommandContext, CommandDefinition } from "./command-registry"; // Command registry export { CommandRegistry } from "./command-registry"; @@ -33,7 +53,7 @@ export * from "./logger"; export type { ActionButton, ModuleSessionContext } from "./modules"; export * from "./modules"; export type { OtelConfig, Span, Tracer } from "./otel"; -// OpenTelemetry tracing (Tempo integration) +// OpenTelemetry tracing export { createChildSpan, createRootSpan, @@ -80,13 +100,13 @@ export type { InstructionContext, InstructionProvider, LogLevel, + McpOAuthConfig, McpServerConfig, NetworkConfig, NixConfig, RegistryEntry, SessionContext, SkillConfig, - McpOAuthConfig, SkillMcpServer, SkillsConfig, SuggestedPrompt, @@ -96,27 +116,6 @@ export type { UserSuggestion, } from "./types"; -// Agent Settings API response types (for UI consumers) -export type { - AgentConfigResponse, - AgentInfo, - CatalogProvider, - Connection, - McpConfig, - ModelOption, - ModelSelectionState, - PermissionGrant, - PrefillMcp, - PrefillSkill, - ProviderInfo, - ProviderState, - ProviderStatus, - Schedule, - SettingsSnapshot, - Skill, - SkillMcpServerInfo, -} from "./api-types"; - // Utilities export * from "./utils/encryption"; export * from "./utils/env"; diff --git a/packages/core/src/otel.ts b/packages/core/src/otel.ts index 3d0126b0c..c36edc765 100644 --- a/packages/core/src/otel.ts +++ b/packages/core/src/otel.ts @@ -1,6 +1,6 @@ /** - * OpenTelemetry tracing setup for distributed tracing with Grafana Tempo. - * Provides Chrome DevTools-style waterfall visualization in Grafana. + * OpenTelemetry tracing setup for distributed tracing. + * Ships traces via OTLP HTTP to any compatible collector (Tempo, Jaeger, Datadog, etc.). */ import type { Span, Tracer } from "@opentelemetry/api"; @@ -25,7 +25,7 @@ let tracer: Tracer | null = null; export interface OtelConfig { serviceName: string; serviceVersion?: string; - tempoEndpoint?: string; // e.g., "http://tempo:4318/v1/traces" + otlpEndpoint?: string; // e.g., "http://collector:4318/v1/traces" enabled?: boolean; } @@ -36,7 +36,7 @@ export interface OtelConfig { * @example * initTracing({ * serviceName: "lobu-gateway", - * tempoEndpoint: "http://lobu-tempo:4318/v1/traces", + * otlpEndpoint: "http://collector:4318/v1/traces", * }); */ export function initTracing(config: OtelConfig): void { @@ -44,9 +44,11 @@ export function initTracing(config: OtelConfig): void { return; // Already initialized } - const enabled = config.enabled ?? !!config.tempoEndpoint; + const enabled = config.enabled ?? !!config.otlpEndpoint; if (!enabled) { - logger.debug("Tracing disabled (no TEMPO_ENDPOINT configured)"); + logger.debug( + "Tracing disabled (no OTEL_EXPORTER_OTLP_ENDPOINT configured)" + ); return; } @@ -57,9 +59,8 @@ export function initTracing(config: OtelConfig): void { provider = new NodeTracerProvider({ resource }); - // Configure OTLP exporter to send traces to Tempo const exporter = new OTLPTraceExporter({ - url: config.tempoEndpoint, + url: config.otlpEndpoint, timeoutMillis: 30000, // 30 second timeout for reliability }); @@ -70,7 +71,7 @@ export function initTracing(config: OtelConfig): void { tracer = trace.getTracer(config.serviceName, config.serviceVersion); logger.info( - `Tracing initialized: ${config.serviceName} -> ${config.tempoEndpoint}` + `Tracing initialized: ${config.serviceName} -> ${config.otlpEndpoint}` ); } @@ -301,6 +302,6 @@ export function getTraceparent(span: Span | null): string | null { return `00-${ctx.traceId}-${ctx.spanId}-01`; } +export type { Span, Tracer }; // Re-export OpenTelemetry types for convenience export { SpanKind, SpanStatusCode }; -export type { Span, Tracer }; diff --git a/packages/core/src/sentry.ts b/packages/core/src/sentry.ts index e9e8c9b29..f642701c3 100644 --- a/packages/core/src/sentry.ts +++ b/packages/core/src/sentry.ts @@ -12,19 +12,21 @@ function getLogger(): Logger { let sentryInstance: typeof import("@sentry/node") | null = null; /** - * Initialize Sentry with configuration from environment variables - * Falls back to hardcoded DSN if SENTRY_DSN is not provided - * Uses dynamic import to avoid module resolution issues in dev mode + * Initialize Sentry with configuration from environment variables. + * Only initializes if SENTRY_DSN is set — no implicit error reporting. + * Uses dynamic import to avoid module resolution issues in dev mode. */ export async function initSentry() { + const sentryDsn = process.env.SENTRY_DSN; + if (!sentryDsn) { + getLogger().debug("Sentry disabled (no SENTRY_DSN configured)"); + return; + } + try { const Sentry = await import("@sentry/node"); sentryInstance = Sentry; - const sentryDsn = - process.env.SENTRY_DSN || - "https://c5910e58d1a134d64ff93a95a9c535bb@o4507291398897664.ingest.us.sentry.io/4511097466781696"; - Sentry.init({ dsn: sentryDsn, sendDefaultPii: true, @@ -39,7 +41,7 @@ export async function initSentry() { getLogger().debug("Sentry monitoring initialized"); } catch (error) { getLogger().warn( - "⚠️ Sentry initialization failed (continuing without monitoring):", + "Sentry initialization failed (continuing without monitoring):", error ); } diff --git a/packages/gateway/src/cli/index.ts b/packages/gateway/src/cli/index.ts index 0273ea790..831fc1dda 100644 --- a/packages/gateway/src/cli/index.ts +++ b/packages/gateway/src/cli/index.ts @@ -34,8 +34,8 @@ async function main() { initTracing({ serviceName: "lobu-gateway", serviceVersion: process.env.npm_package_version || "2.0.0", - tempoEndpoint: process.env.TEMPO_ENDPOINT, - enabled: !!process.env.TEMPO_ENDPOINT, + otlpEndpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT, + enabled: !!process.env.OTEL_EXPORTER_OTLP_ENDPOINT, }); const config = buildGatewayConfig(); diff --git a/packages/gateway/src/connections/message-handler-bridge.ts b/packages/gateway/src/connections/message-handler-bridge.ts index 0c04c2a21..ebccbdfc3 100644 --- a/packages/gateway/src/connections/message-handler-bridge.ts +++ b/packages/gateway/src/connections/message-handler-bridge.ts @@ -4,7 +4,12 @@ * settings links, allowlist, audio transcription, etc. */ -import { createLogger, generateTraceId } from "@lobu/core"; +import { + createLogger, + createRootSpan, + flushTracing, + generateTraceId, +} from "@lobu/core"; import type Redis from "ioredis"; import type { CommandDispatcher } from "../commands/command-dispatcher"; import { createChatReply } from "../commands/command-reply-adapters"; @@ -290,62 +295,82 @@ class MessageHandlerBridge { const traceId = generateTraceId(messageId); const agentSettingsStore = this.services.getAgentSettingsStore(); - // Check if agent has any provider credentials before enqueuing - if (!(await hasConfiguredProvider(agentId, agentSettingsStore))) { - await thread.post( - "No AI provider is configured yet. Provider setup is not available in the end-user chat flow yet. Ask an admin to connect a provider for the base agent." - ); - return; - } + // Create root span for distributed tracing + const { span: rootSpan, traceparent } = createRootSpan("message_received", { + "lobu.agent_id": agentId, + "lobu.message_id": messageId, + "lobu.platform": platform, + "lobu.connection_id": this.connection.id, + }); - const agentOptions = await resolveAgentOptions( - agentId, - {}, - agentSettingsStore - ); + try { + // Check if agent has any provider credentials before enqueuing + if (!(await hasConfiguredProvider(agentId, agentSettingsStore))) { + await thread.post( + "No AI provider is configured yet. Provider setup is not available in the end-user chat flow yet. Ask an admin to connect a provider for the base agent." + ); + return; + } - const payload = buildMessagePayload({ - platform, - userId, - botId: platform, - conversationId: isGroup ? messageId : channelId, - teamId: isGroup ? channelId : platform, - agentId, - messageId, - messageText, - channelId, - platformMetadata: { - traceId, + const agentOptions = await resolveAgentOptions( agentId, - chatId: channelId, - senderId: userId, - senderUsername: message.author?.userName, - senderDisplayName: message.author?.fullName, - isGroup, - connectionId: this.connection.id, - responseChannel: channelId, - responseId: messageId, - responseThreadId: thread.id, - conversationHistory: - conversationHistory.length > 0 ? conversationHistory : undefined, - ...(sessionReset && { sessionReset: true }), - }, - agentOptions, - }); + {}, + agentSettingsStore + ); - const queueProducer = this.services.getQueueProducer(); - await queueProducer.enqueueMessage(payload); + const payload = buildMessagePayload({ + platform, + userId, + botId: platform, + conversationId: isGroup ? messageId : channelId, + teamId: isGroup ? channelId : platform, + agentId, + messageId, + messageText, + channelId, + platformMetadata: { + traceId, + traceparent: traceparent || undefined, + agentId, + chatId: channelId, + senderId: userId, + senderUsername: message.author?.userName, + senderDisplayName: message.author?.fullName, + isGroup, + connectionId: this.connection.id, + responseChannel: channelId, + responseId: messageId, + responseThreadId: thread.id, + conversationHistory: + conversationHistory.length > 0 ? conversationHistory : undefined, + ...(sessionReset && { sessionReset: true }), + }, + agentOptions, + }); + + const queueProducer = this.services.getQueueProducer(); + await queueProducer.enqueueMessage(payload); - logger.info( - { traceId, messageId, agentId, connectionId: this.connection.id }, - "Message enqueued via Chat SDK bridge" - ); + logger.info( + { + traceId, + traceparent, + messageId, + agentId, + connectionId: this.connection.id, + }, + "Message enqueued via Chat SDK bridge" + ); - // Show typing indicator - try { - await thread.startTyping?.("Processing..."); - } catch { - // best effort + // Show typing indicator + try { + await thread.startTyping?.("Processing..."); + } catch { + // best effort + } + } finally { + rootSpan?.end(); + void flushTracing(); } } diff --git a/packages/gateway/src/orchestration/base-deployment-manager.ts b/packages/gateway/src/orchestration/base-deployment-manager.ts index c8f4afe42..24615b045 100644 --- a/packages/gateway/src/orchestration/base-deployment-manager.ts +++ b/packages/gateway/src/orchestration/base-deployment-manager.ts @@ -480,15 +480,15 @@ export abstract class BaseDeploymentManager { envVars.TRACE_ID = traceId; } - // Add Tempo endpoint for distributed tracing - const tempoEndpoint = process.env.TEMPO_ENDPOINT; - if (tempoEndpoint) { - envVars.TEMPO_ENDPOINT = tempoEndpoint; + // Add OTLP endpoint for distributed tracing + const otlpEndpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT; + if (otlpEndpoint) { + envVars.OTEL_EXPORTER_OTLP_ENDPOINT = otlpEndpoint; try { - const tempoUrl = new URL(tempoEndpoint); - envVars.NO_PROXY = `${envVars.NO_PROXY},${tempoUrl.hostname}`; + const otlpUrl = new URL(otlpEndpoint); + envVars.NO_PROXY = `${envVars.NO_PROXY},${otlpUrl.hostname}`; } catch { - envVars.NO_PROXY = `${envVars.NO_PROXY},lobu-tempo`; + envVars.NO_PROXY = `${envVars.NO_PROXY},tempo`; } } diff --git a/packages/gateway/src/platform/unified-thread-consumer.ts b/packages/gateway/src/platform/unified-thread-consumer.ts index 951129cf1..0bc9f6498 100644 --- a/packages/gateway/src/platform/unified-thread-consumer.ts +++ b/packages/gateway/src/platform/unified-thread-consumer.ts @@ -4,7 +4,7 @@ * via the PlatformRegistry, eliminating duplicate queue filtering logic. */ -import { createLogger } from "@lobu/core"; +import { createChildSpan, createLogger, flushTracing } from "@lobu/core"; import type { ChatResponseBridge } from "../connections/chat-response-bridge"; import type { IMessageQueue, @@ -77,61 +77,68 @@ export class UnifiedThreadResponseConsumer { return; } - // Check if this response belongs to a Chat SDK connection — handle before legacy routing - if (this.chatResponseBridge?.canHandle(data)) { - const sessionKey = `${data.userId}:${data.originalMessageId || data.messageId}`; - try { + // Create child span for response processing (linked to original trace) + const traceparent = data.platformMetadata?.traceparent as + | string + | undefined; + const span = createChildSpan("response_delivery", traceparent, { + "lobu.message_id": data.messageId, + "lobu.user_id": data.userId, + "lobu.platform": data.platform || data.teamId || "unknown", + }); + + try { + // Check if this response belongs to a Chat SDK connection — handle before legacy routing + if (this.chatResponseBridge?.canHandle(data)) { + const sessionKey = `${data.userId}:${data.originalMessageId || data.messageId}`; await this.routeToRenderer(this.chatResponseBridge, data, sessionKey); - } catch (error) { - logger.error("Error processing Chat SDK response:", error); - throw error; + return; } - return; - } - // Use platform field, fall back to teamId - const platformName = data.platform || data.teamId; - if (!platformName) { - logger.warn( - `Missing platform in thread response for message ${data.messageId}, skipping` - ); - return; - } + // Use platform field, fall back to teamId + const platformName = data.platform || data.teamId; + if (!platformName) { + logger.warn( + `Missing platform in thread response for message ${data.messageId}, skipping` + ); + return; + } - // Get platform adapter from registry - const platform = this.platformRegistry.get(platformName); - if (!platform) { - logger.warn( - `No platform adapter registered for: ${platformName}, skipping message ${data.messageId}` - ); - return; - } + // Get platform adapter from registry + const platform = this.platformRegistry.get(platformName); + if (!platform) { + logger.warn( + `No platform adapter registered for: ${platformName}, skipping message ${data.messageId}` + ); + return; + } - // Get renderer from platform - const renderer = platform.getResponseRenderer?.(); - if (!renderer) { - logger.warn( - `Platform ${platformName} does not provide a response renderer, skipping message ${data.messageId}` - ); - return; - } + // Get renderer from platform + const renderer = platform.getResponseRenderer?.(); + if (!renderer) { + logger.warn( + `Platform ${platformName} does not provide a response renderer, skipping message ${data.messageId}` + ); + return; + } - // Create session key for tracking - const sessionKey = `${data.userId}:${data.originalMessageId || data.messageId}`; + // Create session key for tracking + const sessionKey = `${data.userId}:${data.originalMessageId || data.messageId}`; - logger.info( - `Processing thread response for platform=${platformName}, message=${data.messageId}, session=${sessionKey}` - ); + logger.info( + `Processing thread response for platform=${platformName}, message=${data.messageId}, session=${sessionKey}` + ); - try { await this.routeToRenderer(renderer, data, sessionKey); } catch (error) { logger.error( - `Error processing thread response for ${platformName}:`, + `Error processing thread response for message ${data.messageId}:`, error ); - // Let queue handle retry logic throw error; + } finally { + span?.end(); + void flushTracing(); } } diff --git a/packages/landing/src/content/docs/guides/observability.md b/packages/landing/src/content/docs/guides/observability.md index 6eefc951e..01d1aeca7 100644 --- a/packages/landing/src/content/docs/guides/observability.md +++ b/packages/landing/src/content/docs/guides/observability.md @@ -3,24 +3,31 @@ title: Observability description: Distributed tracing, logging, and error monitoring for Lobu deployments. --- -Lobu includes built-in observability through OpenTelemetry tracing (Grafana Tempo), structured logging (Loki-compatible), and error monitoring (Sentry). +Lobu includes built-in observability through OpenTelemetry tracing, structured logging (Loki-compatible), and error monitoring (Sentry). ## Distributed tracing -Lobu uses [OpenTelemetry](https://opentelemetry.io/) to trace messages end-to-end across the gateway and worker. Traces are exported to [Grafana Tempo](https://grafana.com/oss/tempo/) and visualized as waterfall timelines in Grafana. +Lobu uses [OpenTelemetry](https://opentelemetry.io/) to trace messages end-to-end across the gateway and worker. Traces are exported via OTLP HTTP to any compatible collector (Tempo, Jaeger, Datadog, Honeycomb, etc.). ### What gets traced -Each incoming message creates a root span that propagates through the full pipeline: +Each incoming message creates a root span that propagates through the full request and response pipeline: -1. **message_received** — gateway ingests message from platform +**Request path (gateway → worker):** + +1. **message_received** — gateway ingests message from API or platform (Slack, Telegram, etc.) 2. **queue_processing** — message consumer picks up the job 3. **worker_creation** — gateway creates worker container/pod 4. **pvc_setup** — persistent volume setup (Kubernetes only) 5. **job_received** — worker receives the job -6. **agent_execution** — OpenClaw agent runs the prompt +6. **exec_execution** — sandbox command execution (if applicable) +7. **agent_execution** — agent runs the prompt + +**Response path (worker → platform):** -Spans are linked via W3C `traceparent` headers, so a single trace ID connects gateway and worker activity. +8. **response_delivery** — gateway receives worker response and routes to platform renderer + +Spans are linked via W3C `traceparent` headers propagated through the queue, so a single trace ID connects the full round-trip from message ingestion through agent execution to response delivery. All entry points (API, Slack, Telegram, Discord, etc.) create root spans. ### Trace ID format @@ -28,14 +35,14 @@ Each message gets a trace ID in the format `tr-{messageId}-{timestamp}-{random}` ### Enable tracing -Set the `TEMPO_ENDPOINT` environment variable. Tracing is automatically disabled when this variable is unset. +Point `OTEL_EXPORTER_OTLP_ENDPOINT` at any OTLP HTTP collector. Tracing is automatically disabled when this variable is unset. ```bash -# .env -TEMPO_ENDPOINT=http://tempo:4318/v1/traces +# .env — use any OTLP-compatible collector +OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318/v1/traces ``` -Both gateway and worker initialize tracing on startup when this is set. The gateway passes the endpoint to workers automatically. +Both gateway and worker initialize tracing on startup when this is set. The gateway passes the endpoint to workers automatically. You can also set this during `lobu init`. ### Docker setup @@ -91,7 +98,7 @@ metrics_generator: path: /var/tempo/metrics ``` -Then add `TEMPO_ENDPOINT=http://tempo:4318/v1/traces` to your `.env` and restart. +Then add `OTEL_EXPORTER_OTLP_ENDPOINT=http://tempo:4318/v1/traces` to your `.env` and restart. ### Kubernetes setup @@ -175,12 +182,14 @@ kubectl logs -f deployment/lobu-gateway -n lobu ## Error monitoring (Sentry) -Lobu integrates with [Sentry](https://sentry.io/) for error and warning capture. +Lobu integrates with [Sentry](https://sentry.io/) for error and warning capture. Sentry is **opt-in only** — no error data is sent unless you explicitly enable it. ### Enable Sentry +During `lobu init`, you'll be asked whether to share anonymous error reports with Lobu's community Sentry project. You can also configure your own Sentry project: + ```bash -# .env +# .env — use Lobu's community DSN or your own SENTRY_DSN=https://your-dsn@sentry.io/your-project ``` @@ -189,13 +198,13 @@ When set, errors and warnings from both gateway and worker are automatically sen - Redis integration for queue-related errors - 100% trace sample rate for full visibility -If `SENTRY_DSN` is not set, Sentry falls back to the community DSN for basic error reporting. +To disable, remove `SENTRY_DSN` from your `.env`. ## Environment variable summary | Variable | Component | Description | |----------|-----------|-------------| -| `TEMPO_ENDPOINT` | Gateway, Worker | OTLP HTTP endpoint for Tempo (e.g., `http://tempo:4318/v1/traces`) | +| `OTEL_EXPORTER_OTLP_ENDPOINT` | Gateway, Worker | OTLP HTTP endpoint for trace collector (e.g., `http://collector:4318/v1/traces`) | | `SENTRY_DSN` | Gateway, Worker | Sentry DSN for error monitoring | | `LOG_LEVEL` | Gateway, Worker | Minimum log level (`error`, `warn`, `info`, `debug`) | | `LOG_FORMAT` | Gateway, Worker | Log output format (`json` or `text`) | diff --git a/packages/worker/src/index.ts b/packages/worker/src/index.ts index 9b2d15ce1..69f08db41 100644 --- a/packages/worker/src/index.ts +++ b/packages/worker/src/index.ts @@ -34,15 +34,13 @@ async function main() { await initSentry(); // Initialize OpenTelemetry tracing for distributed tracing - // Worker traces are sent to Tempo via gateway proxy - const tempoEndpoint = process.env.TEMPO_ENDPOINT; - logger.debug(`TEMPO_ENDPOINT: ${tempoEndpoint}`); - if (tempoEndpoint) { + const otlpEndpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT; + if (otlpEndpoint) { initTracing({ serviceName: "lobu-worker", - tempoEndpoint, + otlpEndpoint, }); - logger.info(`Tracing initialized: lobu-worker -> ${tempoEndpoint}`); + logger.info(`Tracing initialized: lobu-worker -> ${otlpEndpoint}`); } // Discover and register available modules diff --git a/tsconfig.json b/tsconfig.json index bace49272..d8ed71819 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { // Environment setup & latest features "lib": ["ESNext"], + "types": ["bun-types"], "target": "ESNext", "module": "ESNext", "moduleDetection": "force", @@ -40,6 +41,7 @@ "exclude": [ "node_modules", "packages/*/src/__tests__/**/*", + "packages/cli/**/*", "packages/landing/**/*", "packages/gateway/src/routes/public/agent-page/**/*", "packages/gateway/src/routes/public/history-page/**/*",