Minimal browser-only OpenTelemetry bootstrap for Glimt.dev
npm install @glimt.dev/otel-browserOpenTelemetry for the browser. Works out of the box, highly configurable.
``bash`
npm install @glimt.dev/otel-browser @opentelemetry/api @opentelemetry/api-logs
`ts
import { registerOTelBrowser } from '@glimt.dev/otel-browser'
registerOTelBrowser({ serviceName: 'my-web-app' })
`
Done. Traces go to https://ingest.glimt.dev/v1/traces, logs to /v1/logs.
For local development with a local collector:
`ts`
registerOTelBrowser({
serviceName: 'my-web-app',
exporterUrl: 'http://localhost:4318',
})
For production with Glimt authentication:
`ts`
registerOTelBrowser({
serviceName: 'my-web-app',
// exporterUrl defaults to https://ingest.glimt.dev
organisationId: 'your-org-id',
publishableKey: 'your-publishable-key',
})
---
`ts
import { registerOTelBrowser } from '@glimt.dev/otel-browser'
const sdk = registerOTelBrowser({
// Identity
serviceName: 'my-web-app',
// Glimt auth
organisationId: 'org_xxx',
publishableKey: 'pk_xxx',
// Release metadata (CRITICAL for source mapping)
release: {
commit: process.env.NEXT_PUBLIC_COMMIT_SHA, // or 'abc1234'
branch: process.env.NEXT_PUBLIC_COMMIT_REF, // or 'main'
version: '1.0.0',
environment: 'production',
},
// Export - base URL, SDK derives /v1/traces and /v1/logs
exporterUrl: 'https://ingest.glimt.dev',
exporter: 'http/protobuf', // or 'http/json'
exporterHeaders: { 'x-tenant-id': 'acme' },
credentials: 'include', // for CORS
// Logs (auto-derived to /v1/logs)
logs: {
exporter: 'http/protobuf',
},
// Instrumentations
instrumentations: ['auto'], // document-load, user-interaction, xhr, fetch
fetch: {
ignoreUrls: [/\/healthz?$/, /analytics\.js$/],
propagateContextUrls: [/^https:\/\/api\.myapp\.com/],
},
// Capture
captureUnhandledErrors: true, // window.onerror, unhandledrejection (default: true)
captureConsoleLogs: true, // console.* as OTLP logs (default: false)
includeUserAgent: false, // privacy default
// Initial user context
user: { userId: 'user_123', role: 'admin' },
// Debug
logLevel: 'DEBUG',
})
// Update user on login/logout
sdk.setUserContext({ userId: 'user_456' })
sdk.setUserContext(null)
`
---
Browsers don't have runtime environment variables, but you can inject them at build time using your bundler:
Next.js (next.config.mjs):
`js`
// Automatically available as process.env.NEXT_PUBLIC_*
// Set in .env or CI environment
Vite (vite.config.ts):
`ts`
export default defineConfig({
define: {
'import.meta.env.VITE_OTEL_ENDPOINT': JSON.stringify(process.env.OTEL_EXPORTER_OTLP_ENDPOINT),
'import.meta.env.VITE_COMMIT_SHA': JSON.stringify(process.env.COMMIT_SHA),
}
})
Webpack:
`js`
new webpack.DefinePlugin({
'process.env.OTEL_ENDPOINT': JSON.stringify(process.env.OTEL_EXPORTER_OTLP_ENDPOINT),
})
---
`tsx
'use client'
import { useEffect, useRef } from 'react'
import { registerOTelBrowser, type BrowserSDK } from '@glimt.dev/otel-browser'
export function TelemetryProvider({ userId }: { userId?: string }) {
const sdk = useRef
useEffect(() => {
sdk.current = registerOTelBrowser({
serviceName: process.env.NEXT_PUBLIC_OTEL_SERVICE_NAME!,
exporterUrl: process.env.NEXT_PUBLIC_OTEL_EXPORTER_OTLP_ENDPOINT,
user: userId ? { userId } : undefined,
release: {
commit: process.env.NEXT_PUBLIC_COMMIT_SHA || process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA,
branch: process.env.NEXT_PUBLIC_COMMIT_REF || process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF,
environment: process.env.NEXT_PUBLIC_VERCEL_ENV,
}
})
}, [])
useEffect(() => {
sdk.current?.setUserContext(userId ? { userId } : null)
}, [userId])
return null
}
`
`tsx`
// app/layout.tsx
export default async function RootLayout({ children }) {
const user = await getCurrentUser()
return (
{children}
)
}
---
Inject in your HTML to correlate browser ↔ server:
`html`
The SDK picks this up automatically as the parent span.
---
Your collector must allow:
``
Access-Control-Allow-Origin: * (or your origin)
Access-Control-Allow-Headers: content-type, traceparent, baggage, authorization
Access-Control-Allow-Credentials: true (if using credentials)
---
| Feature | Default |
|---------|---------|
| Trace exporter | http/protobuf → https://ingest.glimt.dev/v1/traces |http/protobuf
| Log exporter | → https://ingest.glimt.dev/v1/logs |window.onerror
| Instrumentations | document-load, user-interaction, xhr, fetch |
| Error capture | , unhandledrejection |
| User agent | NOT included (privacy) |
| Console capture | OFF |
---
When you call sdk.setUserContext(), these attributes are added to all spans:
| Attribute | Description |
|-----------|-------------|
| enduser.id | Primary user identifier |enduser.pseudo.id
| | Privacy-preserving hash |enduser.email
| | PII, use with caution |enduser.role` | Permission scope |
|
---
MIT