Evlog Integration
Evlog integration for oRPC adds structured logging so you can trace requests, monitor errors, and inspect application behavior.
Installation
npm install @orpc/evlog@beta evlog@betapnpm add @orpc/evlog@beta evlog@betayarn add @orpc/evlog@beta evlog@betabun add @orpc/evlog@beta evlog@betaSetup
Use EvlogHandlerPlugin to instrument your handler with structured logs, request tracking, and error monitoring.
import { class EvlogHandlerPlugin<T extends Context>Instruments an oRPC handler with Evlog structured logging, request tracking,
and error monitoring.EvlogHandlerPlugin } from '@orpc/evlog'
const const handler: RPCHandler<{
headers?: IncomingHttpHeaders;
} & object>
handler = new new RPCHandler<{
headers?: IncomingHttpHeaders;
} & object>(router: Router<{
headers?: IncomingHttpHeaders;
} & object>, options?: NoInfer<RPCHandlerOptions<{
headers?: IncomingHttpHeaders;
} & object>>): RPCHandler<{
headers?: IncomingHttpHeaders;
} & object>
Serves an oRPC router over the RPC protocol using the Fetch API
(Request/Response), supported by modern runtimes like Deno, Bun,
Cloudflare Workers, and browsers.RPCHandler(const router: {
planet: {
list: ImplementedProcedure<{
headers?: IncomingHttpHeaders;
} & object, object, ZodObject<{
limit: ZodOptional<ZodNumber>;
cursor: ZodDefault<ZodNumber>;
}, $strip>, ZodArray<ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>>, object>;
find: ImplementedProcedure<{
headers?: IncomingHttpHeaders;
} & object, object, ZodObject<{
id: ZodNumber;
}, $strip>, ZodObject<...>, object>;
create: ImplementedProcedure<...>;
};
}
router, {
FetchHandlerOptions<{ headers?: IncomingHttpHeaders; } & object>.plugins?: FetchHandlerPlugin<{
headers?: IncomingHttpHeaders;
} & object>[] | undefined
plugins: [
new new EvlogHandlerPlugin<{
headers?: IncomingHttpHeaders;
} & object>({ storage, logAbort, ...evlogOptions }?: EvlogHandlerPluginOptions<{
headers?: IncomingHttpHeaders;
} & object>): EvlogHandlerPlugin<{
headers?: IncomingHttpHeaders;
} & object>
Instruments an oRPC handler with Evlog structured logging, request tracking,
and error monitoring.EvlogHandlerPlugin({
BaseEvlogOptions.drain?: ((ctx: DrainContext) => void | Promise<void>) | undefinedDrain callback invoked with every emitted event.drain: var undefinedundefined, // <- custom Evlog drain (optional)
BaseEvlogOptions.plugins?: EvlogPlugin[] | undefinedPlugins for this middleware, merged with globally-registered ones.plugins: [], // <- additional Evlog plugins (optional)
EvlogHandlerPluginOptions<_T extends Context>.logAbort?: boolean | undefinedIf true, this plugin will log when a request signal is aborted.logAbort: true, // <- log when requests are aborted (disabled by default)
}),
],
})
Using the Logger in Your Code
This plugin supports using AsyncLocalStorage to access the logger throughout a request and enrich the final wide event. It is the most convenient way to use Evlog’s full feature set. If your runtime does not support AsyncLocalStorage, you can still access the logger from the context.
import { createLoggerStorage } from '@orpc/evlog/node'
/**
* Pass `storage` to the plugin configuration.
* Call `useLogger` inside a procedure to access the request logger.
*/
export const { storage, useLogger } = createLoggerStorage()
const procedure = os
.handler(async () => {
const logger = useLogger()
logger?.set({ user: { id: 123, name: 'John Doe' } })
await logger.fork('child-procedure', () => {
const logger = useLogger()
})
return { success: true }
})const handler = new RPCHandler(router, {
plugins: [
new EvlogHandlerPlugin({
storage, // <- pass the storage to the plugin
}),
],
})Without AsyncLocalStorage
If you do not want to use AsyncLocalStorage, or your runtime does not support it, you can still read the logger from the context.
import { getLogger, LoggerContext } from '@orpc/evlog'
interface ServerContext extends LoggerContext {}
const procedure = os
.$context<ServerContext>()
.handler(({ context }) => {
const logger = getLogger(context)
logger?.set({ user: { id: 123, name: 'John Doe' } })
return { success: true }
})