Logger & OpenTelemetry
Structured logging, error capture and OpenTelemetry tracing for a Next.js app, in one registry item.
Drop-in observability. Once installed, server errors, client errors, unhandled
rejections and console output are captured as structured JSON, correlated with
an OpenTelemetry trace, and written to stdout for the platform collector to
pick up.
Unlike the other items in this registry this one is a kit: it installs a library, a provider, both instrumentation hooks and an API route.
Installation
npx shadcn@latest add @uqudo/loggerinstrumentation.ts, instrumentation-client.ts and
app/api/telemetry/route.ts are written at the project root, not under
components/. If your project already has any of them, back them up before
installing and merge afterwards.
Usage
import { logger } from "@/lib/observability"Wrap your root layout once. Global capture is installed before hydration by
instrumentation-client.ts; the provider adds the things that need React —
identity, Web Vitals and an error boundary.
import { ObservabilityProvider } from "@/components/uqudo/observability-provider"
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<ObservabilityProvider>{children}</ObservabilityProvider>
</body>
</html>
)
}Then log from anywhere — server components, route handlers, client components:
logger.info("Checkout completed", { orderId, amount })
logger.error(error, { event: "checkout-failed", orderId })
// Bind context once and reuse it
const scoped = logger.child({ tenant: "acme" })
scoped.warn("Rate limit approaching", { remaining: 12 })Configuration
Set these on the deployment. The defaults are safe: logs already reach the backend over stdout, so OTLP log and trace export stay off until the platform has those pipelines.
Prop
Type
Browser events are batched and relayed through POST /api/telemetry, which the
item installs for you.
API Reference
logger
import { logger } from "@/lib/observability"
Prop
Type
LogOptions
Anything extra you pass becomes a structured attribute, alongside these recognised fields.
Prop
Type
ObservabilityProvider
Prop
Type
useObservability
Imperative access from client components. Throws outside the provider.
"use client"
import { useObservability } from "@/components/uqudo/observability-provider"
export function SaveButton() {
const { captureException, identify, flush } = useObservability()
// ...
}Prop
Type
ObservabilityErrorReporter
Drop into error.tsx or global-error.tsx to report an error that Next.js has
already caught and rendered a fallback for. Reports are deduplicated by the
error digest, so Strict Mode does not double-report.
"use client"
import { ObservabilityErrorReporter } from "@/components/uqudo/observability-provider"
export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<html>
<body>
<ObservabilityErrorReporter error={error} source="global-error" />
<button onClick={reset}>Try again</button>
</body>
</html>
)
}Prop
Type
Tracing helpers
Prop
Type
redact, redactHeaders, serializeError, getTracer, addSpanEvent and
recordSpanException are exported from the same entry point.
Entry points
lib/observability/index.ts is the full export surface — safe to import from
server components, client components, route handlers and the Edge runtime.
instrumentation.ts is where the Node-only OpenTelemetry SDK boots.
/**
* Public surface of the observability kit.
*
* Safe to import from server components, client components, route handlers
* and the Edge runtime. Node-only OpenTelemetry setup lives in
* `./otel-node`, which `instrumentation.ts` imports directly.
*/
export {
logger,
captureException,
addLogSink,
setGlobalAttributes,
setUser,
setBrowserContext,
getBrowserContext,
setTraceContextProvider,
setBrowserSink,
nativeConsole,
type Logger,
type LogOptions,
} from "./logger"
export {
getClientConfig,
getServerConfig,
overrideClientConfig,
currentRuntime,
type ClientConfig,
type ServerConfig,
type OtlpProtocol,
} from "./config"
export {
installConsolePatch,
installConsolePatchIfEnabled,
isConsolePatched,
} from "./console-patch"
export {
installClientTransport,
isTransportInstalled,
flush as flushClientTelemetry,
} from "./client-transport"
export {
getTracer,
getTraceId,
getSpanId,
setSpanAttributes,
addSpanEvent,
recordSpanException,
withSpan,
getPropagationHeaders,
} from "./tracing"
export {
initPrometheus,
recordHttpDuration,
recordAppError,
renderPrometheusMetrics,
isScrapeAuthorised,
getPrometheusRegistry,
} from "./prometheus"
export { startPrometheusOtlpBridge } from "./prometheus-bridge"
export { redact, redactHeaders, redactString, redactUrl, REDACTED } from "./redact"
export { serializeError, safeStringify, flattenAttributes } from "./serialize"
export {
LOG_LEVELS,
SEVERITY_NUMBER,
isLogLevel,
type BrowserContext,
type HttpContext,
type LogAttributes,
type LogLevel,
type LogRecord,
type LogSink,
type RuntimeName,
type SerializedError,
type ServiceContext,
type TelemetryBatch,
type TelemetryEvent,
type TraceContext,
} from "./types"Reference
The kit ships its own reference at lib/observability/README.md, covering the
record schema, the redaction rules and the collector contract.