@guvnor/ts
v2.0.0
Published
TypeScript SDK for Guvnor. Instruments your application with traces and spans, sending events to the Guvnor ingest API in the background without blocking responses.
Readme
@guvnor/ts
TypeScript SDK for Guvnor. Instruments your application with traces and spans, sending events to the Guvnor ingest API in the background without blocking responses.
Installation
bun add @guvnor/tsRemix / React Router 7
Setup
Create a single Guvnor instance and export it from a shared module:
// app/guvnor.server.ts
import { createRemixGuvnor } from '@guvnor/ts'
export const guvnor = createRemixGuvnor({
environment: process.env.NODE_ENV,
identify: async (request) => {
const user = await getUser(request)
return { userId: user.id, plan: user.plan }
},
release: process.env.COMMIT_SHA
})wrap
Wrap your request handler to trace the full request lifecycle. Call it at the
entry point of each route — typically in a root loader or a middleware-style
utility.
// app/routes/dashboard.tsx
import { guvnor } from '~/guvnor.server'
export async function loader({ request }: LoaderFunctionArgs) {
const responseHeaders = new Headers()
return guvnor.wrap(request, responseHeaders, async () => {
const data = await getDashboardData()
return json(data, { headers: responseHeaders })
})
}A root span is created for the request. Its duration and status (ok/error) are
recorded automatically. A session cookie (__guvnor_sid) is set on the first
visit and read on subsequent requests.
loader and action
Instrument individual loaders and actions to record them as child spans:
export const loader = guvnor.loader(async function loader({
request
}: LoaderFunctionArgs) {
const data = await getDashboardData()
return json(data)
})
export const action = guvnor.action(async function action({
request
}: ActionFunctionArgs) {
const formData = await request.formData()
await processForm(formData)
return redirect('/dashboard')
})Loader spans are named "loader" and action spans "action". The function name
is recorded in the loader.name / action.name attribute, so naming your
functions is recommended.
span
Instrument arbitrary async work inside a loader, action, or wrap handler:
export const loader = guvnor.loader(async function loader({ request }) {
const rows = await guvnor.span('db.fetchUsers', () =>
db.query('SELECT * FROM users')
)
return json(rows)
})Spans nest correctly — a span called inside a loader will have the loader's
span as its parent.
ignore
Skip instrumentation for specific requests:
const guvnor = createRemixGuvnor({
ignore: (request) => new URL(request.url).pathname.startsWith('/healthcheck')
})captureError
Mark the current span as errored from anywhere within an active trace:
export const loader = guvnor.loader(async function loader({ request }) {
try {
return json(await riskyOperation())
} catch (error) {
guvnor.captureError(error)
return json({ error: 'Something went wrong' }, { status: 500 })
}
})TypeScript (framework-agnostic)
Setup
import { createGuvnor } from '@guvnor/ts'
const guvnor = createGuvnor({
environment: 'production',
release: '1.2.3'
})span
Wrap any async operation in a named span. If called outside of an active trace context it passes through transparently with no overhead.
const result = await guvnor.span('payment.charge', async () => {
return stripe.charges.create({ amount, currency })
})captureError
Mark the current span as errored:
try {
await guvnor.span('sync.run', () => runSync())
} catch (error) {
guvnor.captureError(error)
throw error
}Options
| Option | Type | Description |
| ------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| environment | string | e.g. "production", "development". When set to "development", spans are also logged to the console. |
| release | string | Release identifier, e.g. a commit SHA or version string. |
| identify | (request: Request) => Promise<Record<string, string \| number \| boolean>> | Attach user or session attributes to all spans for a request. Return a userId key to associate spans with a user. |
| ignore | (request: Request) => boolean | Return true to skip instrumentation for a request entirely. |
