11import "./polyfills/compression" ;
22import { auth } from "@databuddy/auth" ;
3- import { appRouter , createRPCContext } from "@databuddy/rpc" ;
3+ import {
4+ appRouter ,
5+ createAbortSignalInterceptor ,
6+ createRPCContext ,
7+ recordORPCError ,
8+ setupUncaughtErrorHandlers ,
9+ } from "@databuddy/rpc" ;
410import { logger } from "@databuddy/shared/logger" ;
511import cors from "@elysiajs/cors" ;
6- import { onError } from "@orpc/server" ;
12+ import { context } from "@opentelemetry/api" ;
13+ import { ORPCError , onError } from "@orpc/server" ;
714import { RPCHandler } from "@orpc/server/fetch" ;
815import { autumnHandler } from "autumn-js/elysia" ;
916import { Elysia } from "elysia" ;
17+ import {
18+ endRequestSpan ,
19+ initTracing ,
20+ shutdownTracing ,
21+ startRequestSpan ,
22+ } from "./lib/tracing" ;
1023import { assistant } from "./routes/assistant" ;
1124import { exportRoute } from "./routes/export" ;
1225import { health } from "./routes/health" ;
1326import { publicApi } from "./routes/public" ;
1427import { query } from "./routes/query" ;
1528
29+ initTracing ( ) ;
30+ setupUncaughtErrorHandlers ( ) ;
31+
1632const rpcHandler = new RPCHandler ( appRouter , {
1733 interceptors : [
34+ createAbortSignalInterceptor ( ) ,
1835 onError ( ( error ) => {
1936 logger . error ( error ) ;
2037 } ) ,
2138 ] ,
2239} ) ;
2340
2441const app = new Elysia ( )
42+ . state ( "tracing" , {
43+ span : null as ReturnType < typeof startRequestSpan > [ "span" ] | null ,
44+ activeContext : null as ReturnType < typeof context . active > | null | undefined ,
45+ startTime : 0 ,
46+ } )
2547 . use (
2648 cors ( {
2749 credentials : true ,
@@ -35,6 +57,31 @@ const app = new Elysia()
3557 )
3658 . use ( publicApi )
3759 . use ( health )
60+ . onBeforeHandle ( function startTrace ( { request, path, store } ) {
61+ const method = request . method ;
62+ const startTime = Date . now ( ) ;
63+
64+ // Extract route from path (e.g., "/rpc/websites.list" -> "websites.list")
65+ const route = path . startsWith ( "/rpc/" ) ? path . slice ( 5 ) : path ;
66+ const { span, activeContext } = startRequestSpan (
67+ method ,
68+ request . url ,
69+ route
70+ ) ;
71+
72+ // Store span, context, and start time in Elysia store
73+ store . tracing = {
74+ span,
75+ activeContext,
76+ startTime,
77+ } ;
78+ } )
79+ . onAfterHandle ( function endTrace ( { response, store } ) {
80+ if ( store . tracing ?. span && store . tracing . startTime ) {
81+ const statusCode = response instanceof Response ? response . status : 200 ;
82+ endRequestSpan ( store . tracing . span , statusCode , store . tracing . startTime ) ;
83+ }
84+ } )
3885 . use (
3986 autumnHandler ( {
4087 identify : async ( { request } ) => {
@@ -62,17 +109,35 @@ const app = new Elysia()
62109 . use ( exportRoute )
63110 . all (
64111 "/rpc/*" ,
65- async ( { request } ) => {
112+ async ( { request, store } ) => {
66113 try {
67114 const rpcContext = await createRPCContext ( {
68115 headers : request . headers ,
69116 } ) ;
70- const { response } = await rpcHandler . handle ( request , {
71- prefix : "/rpc" ,
72- context : rpcContext ,
73- } ) ;
117+
118+ const handler = async ( ) => {
119+ const { response } = await rpcHandler . handle ( request , {
120+ prefix : "/rpc" ,
121+ context : rpcContext ,
122+ } ) ;
123+ return response ;
124+ } ;
125+
126+ const activeContext = store . tracing ?. activeContext ;
127+ const response = activeContext
128+ ? await context . with ( activeContext , handler )
129+ : await handler ( ) ;
130+
74131 return response ?? new Response ( "Not Found" , { status : 404 } ) ;
75132 } catch ( error ) {
133+ // Record ORPC errors in OpenTelemetry
134+ if ( error instanceof ORPCError ) {
135+ recordORPCError ( {
136+ code : error . code ,
137+ message : error . message ,
138+ } ) ;
139+ }
140+
76141 logger . error ( { error } , "RPC handler failed" ) ;
77142 return new Response ( "Internal Server Error" , { status : 500 } ) ;
78143 }
@@ -81,7 +146,12 @@ const app = new Elysia()
81146 parse : "none" ,
82147 }
83148 )
84- . onError ( function handleError ( { error, code } ) {
149+ . onError ( function handleError ( { error, code, store } ) {
150+ if ( store . tracing ?. span && store . tracing . startTime ) {
151+ const statusCode = code === "NOT_FOUND" ? 404 : 500 ;
152+ endRequestSpan ( store . tracing . span , statusCode , store . tracing . startTime ) ;
153+ }
154+
85155 const errorMessage = error instanceof Error ? error . message : String ( error ) ;
86156 logger . error ( { error, code } , errorMessage ) ;
87157
@@ -100,12 +170,18 @@ export default {
100170 port : 3001 ,
101171} ;
102172
103- process . on ( "SIGINT" , ( ) => {
173+ process . on ( "SIGINT" , async ( ) => {
104174 logger . info ( "SIGINT received, shutting down gracefully..." ) ;
175+ await shutdownTracing ( ) . catch ( ( error ) =>
176+ logger . error ( { error } , "Shutdown error" )
177+ ) ;
105178 process . exit ( 0 ) ;
106179} ) ;
107180
108- process . on ( "SIGTERM" , ( ) => {
181+ process . on ( "SIGTERM" , async ( ) => {
109182 logger . info ( "SIGTERM received, shutting down gracefully..." ) ;
183+ await shutdownTracing ( ) . catch ( ( error ) =>
184+ logger . error ( { error } , "Shutdown error" )
185+ ) ;
110186 process . exit ( 0 ) ;
111187} ) ;
0 commit comments