Remote logging
SDKs send logs to Hypertune Edge in the background. This enables you to:
See how often different parts of your flag logic are evaluated, e.g. to see how often different targeting rules are evaluated and passed in realtime
Log analytics events
Log split exposures, e.g. for A/B tests, multivariate tests, and AI loops
See any SDK errors from the Hypertune UI
Change the flush interval
By default, SDKs flush logs to Hypertune Edge every 2 seconds. To change this interval, set remoteLogging.flushIntervalMs
in your createSource
options. For example, to flush logs every 10 seconds, set it to 10_000
. To disable automatic log flushing, set it to null
.
import { HypertuneProvider } from '../generated/hypertune.react'
export default function AppHypertuneProvider({
children,
}: {
children: React.ReactNode
}) {
return (
<HypertuneProvider
createSourceOptions={{
token: import.meta.env.VITE_HYPERTUNE_TOKEN!,
remoteLogging: { flushIntervalMs: 10_000 },
}}
rootArgs={{
context: {
environment:
process.env.NODE_ENV === 'development'
? 'development'
: 'production',
user: {
id: 'e23cc9a8-0287-40aa-8500-6802df91e56a',
name: 'Example User',
email: '[email protected]',
},
},
}}
>
{children}
</HypertuneProvider>
)
}
Manually flush logs
To manually trigger and wait for logs to be flushed, use the flushLogs
method:
import { NextResponse } from 'next/server'
import getHypertune from '@/lib/getHypertune'
export const runtime = 'edge'
export async function GET() {
const hypertune = await getHypertune({ isRouteHandler: true })
const exampleFlag = hypertune.exampleFlag({ fallback: false })
await hypertune.flushLogs()
return NextResponse.json({ exampleFlag })
}
This is a requirement in serverless and edge environments like Vercel deployments, Cloudflare Workers, AWS Lambdas, etc, where background SDK tasks like flushing logs aren't guaranteed to execute.
Change the remote logging endpoint
By default, SDKs send all logs to Hypertune Edge via the following endpoint: https://gcp.fasthorse.workers.dev/logs
To change this endpoint, set remoteLogging.endpointUrl
in your createSource
options:
import { HypertuneProvider } from '../generated/hypertune.react'
export default function AppHypertuneProvider({
children,
}: {
children: React.ReactNode
}) {
return (
<HypertuneProvider
createSourceOptions={{
token: import.meta.env.VITE_HYPERTUNE_TOKEN!,
remoteLogging: {
endpointUrl:
'https://yourdomain.com/api/hypertune-logs',
},
}}
rootArgs={{
context: {
environment:
process.env.NODE_ENV === 'development'
? 'development'
: 'production',
user: {
id: 'e23cc9a8-0287-40aa-8500-6802df91e56a',
name: 'Example User',
email: '[email protected]',
},
},
}}
>
{children}
</HypertuneProvider>
)
}
This enables you to send logs to your own server, where you can forward them to your own data warehouse or analytics system, and optionally forward them to Hypertune Edge too:
import {
CreateLogsInput,
prodLogsEndpointUrl,
} from 'hypertune/dist/shared'
import { NextResponse } from 'next/server'
export const runtime = 'edge'
export const dynamic = 'force-dynamic'
export async function POST(request: Request) {
const body = (await request.json()) as CreateLogsInput
console.log('events', body.events)
console.log('exposures', body.exposures)
// Forward logs to Hypertune
try {
await fetch(prodLogsEndpointUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
cache: 'no-store',
})
} catch (error) {
console.error('Error forwarding logs to Hypertune:', error)
return NextResponse.json(
{ error: 'Failed to forward logs to Hypertune' },
{ status: 500 }
)
}
// TODO: If successful, forward logs to own data warehouse too.
// Return an error response on failure.
return NextResponse.json({ success: true }, { status: 200 })
}
This setup also helps ensure you don't lose any data due to ad blockers or network restrictions in the browser.
Change the remote logging mode
The remote logging mode determines how the SDK sends flag evaluations, experiment exposures, analytics events, and error logs to Hypertune Edge or your own endpoint. There are three remote logging modes:
normal
All logs are sent.
session
Logs are deduplicated per session based on the provided Context
. However, all analytics events are sent.
off
No logs are sent.
The default remote logging mode is based on the environment:
Server
normal
No need for special treatment of logs.
Browser
session
Ensures that logs aren't generated in every render.
Next.js server
off
Prefetching and caching of pages and layouts can cause logs for flag evaluations, experiment exposures, and analytics events generated on Next.js servers to differ from actual user behaviour. To ensure accuracy, remote logging is enabled by default on the client (in the browser) only.
However, since Route Handlers aren't subject to the same prefetching and caching patterns, remote logging can be enabled for them.
To change the remote logging mode, set remoteLogging.mode
in your createSource
options:
import { HypertuneProvider } from '../generated/hypertune.react'
export default function AppHypertuneProvider({
children,
}: {
children: React.ReactNode
}) {
return (
<HypertuneProvider
createSourceOptions={{
token: import.meta.env.VITE_HYPERTUNE_TOKEN!,
remoteLogging: { mode: 'off' },
}}
rootArgs={{
context: {
environment:
process.env.NODE_ENV === 'development'
? 'development'
: 'production',
user: {
id: 'e23cc9a8-0287-40aa-8500-6802df91e56a',
name: 'Example User',
email: '[email protected]',
},
},
}}
>
{children}
</HypertuneProvider>
)
}
Or change the remote logging mode with the setRemoteLoggingMode
method:
import 'server-only'
import { unstable_noStore as noStore } from 'next/cache'
import { createSource } from '@/generated/hypertune'
const hypertuneSource = createSource({
token: process.env.NEXT_PUBLIC_HYPERTUNE_TOKEN!,
})
export default async function getHypertune({
isRouteHandler = false,
}: {
isRouteHandler?: boolean
} = {}) {
noStore()
await hypertuneSource.initIfNeeded()
hypertuneSource.setRemoteLoggingMode(
isRouteHandler ? 'normal' : 'off'
)
return hypertuneSource.root({
args: {
context: {
environment: process.env.NODE_ENV,
user: {
id: 'e23cc9a8-0287-40aa-8500-6802df91e56a',
name: 'Example User',
email: '[email protected]',
},
},
},
})
}
Last updated