# Remote logging

SDKs send logs to [Hypertune Edge](https://docs.hypertune.com/concepts/hypertune-edge) in the background to report:

* Flag evaluations
* Experiment exposures
* Analytics events
* SDK error messages

## Change the flush interval

By default, SDKs flush logs to [Hypertune Edge](https://docs.hypertune.com/concepts/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`.

{% tabs %}
{% tab title="React" %}
{% code title="src/components/AppHypertuneProvider.tsx" %}

```tsx
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: 'user@example.com',
          },
        },
      }}
    >
      {children}
    </HypertuneProvider>
  )
}
```

{% endcode %}
{% endtab %}

{% tab title="Node.js" %}
{% code title="src/lib/getHypertune.ts" %}

```typescript
import { createSource } from '../generated/hypertune'

const hypertuneSource = createSource({
  token: process.env.HYPERTUNE_TOKEN!,
  remoteLogging: { flushIntervalMs: 10_000 },
})

export default async function getHypertune() {
  // Get flag updates in serverless environments
  // await hypertuneSource.initIfNeeded();

  return hypertuneSource.root({
    args: {
      context: {
        environment:
          process.env.NODE_ENV === 'development'
            ? 'development'
            : 'production',
        user: {
          id: 'e23cc9a8-0287-40aa-8500-6802df91e56a',
          name: 'Example User',
          email: 'user@example.com',
        },
      },
    },
  })
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Manually flush logs

To manually trigger and wait for logs to be flushed, use the `flushLogs` method:

{% code title="app/api/route.ts" %}

```typescript
import { waitUntil } from '@vercel/functions'
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 })

  waitUntil(hypertune.flushLogs())

  return NextResponse.json({ exampleFlag })
}
```

{% endcode %}

This is a requirement in [serverless and edge environments](https://docs.hypertune.com/sdk-reference/serverless-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](https://docs.hypertune.com/concepts/hypertune-edge) via the following endpoint: `https://gcp.fasthorse.workers.dev/logs`

To change this endpoint, set `remoteLogging.endpointUrl` in your `createSource` options:

{% tabs %}
{% tab title="React" %}
{% code title="src/components/AppHypertuneProvider.tsx" %}

```tsx
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: 'user@example.com',
          },
        },
      }}
    >
      {children}
    </HypertuneProvider>
  )
}
```

{% endcode %}
{% endtab %}

{% tab title="Node.js" %}
{% code title="src/lib/getHypertune.ts" %}

```typescript
import { createSource } from '../generated/hypertune'

const hypertuneSource = createSource({
  token: process.env.HYPERTUNE_TOKEN!,
  remoteLogging: {
    endpointUrl: 'https://yourdomain.com/api/hypertune-logs',
  },
})

export default async function getHypertune() {
  // Get flag updates in serverless environments
  // await hypertuneSource.initIfNeeded();

  return hypertuneSource.root({
    args: {
      context: {
        environment:
          process.env.NODE_ENV === 'development'
            ? 'development'
            : 'production',
        user: {
          id: 'e23cc9a8-0287-40aa-8500-6802df91e56a',
          name: 'Example User',
          email: 'user@example.com',
        },
      },
    },
  })
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

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:

{% code title="/app/api/hypertune-logs/route.ts" %}

```typescript
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 })
}
```

{% endcode %}

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](https://docs.hypertune.com/concepts/hypertune-edge) or your own endpoint. There are three remote logging modes:

| Remote logging mode | Behaviour                                                                                                  |
| ------------------- | ---------------------------------------------------------------------------------------------------------- |
| `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:

| Environment    | Default   | Why                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| -------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Server         | `normal`  | No need for special treatment of logs.                                                                                                                                                                                                                                                                                                                                                                                                               |
| Browser        | `session` | Ensures that logs aren't generated in every render.                                                                                                                                                                                                                                                                                                                                                                                                  |
| Next.js server | `off`     | <p>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.</p><p></p><p>However, since Route Handlers aren't subject to the same prefetching and caching patterns, remote logging can be enabled for them.</p> |

To change the remote logging mode, set `remoteLogging.mode` in your `createSource` options:

{% tabs %}
{% tab title="React" %}
{% code title="src/components/AppHypertuneProvider.tsx" %}

```tsx
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: 'user@example.com',
          },
        },
      }}
    >
      {children}
    </HypertuneProvider>
  )
}
```

{% endcode %}
{% endtab %}

{% tab title="Node.js" %}
{% code title="src/lib/getHypertune.ts" %}

```typescript
import { createSource } from '../generated/hypertune'

const hypertuneSource = createSource({
  token: process.env.HYPERTUNE_TOKEN!,
  remoteLogging: { mode: 'off' },
})

export default async function getHypertune() {
  // Get flag updates in serverless environments
  // await hypertuneSource.initIfNeeded();

  return hypertuneSource.root({
    args: {
      context: {
        environment:
          process.env.NODE_ENV === 'development'
            ? 'development'
            : 'production',
        user: {
          id: 'e23cc9a8-0287-40aa-8500-6802df91e56a',
          name: 'Example User',
          email: 'user@example.com',
        },
      },
    },
  })
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

Or change the remote logging mode with the `setRemoteLoggingMode` method:

{% code title="lib/getHypertune.ts" %}

```typescript
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: 'user@example.com',
        },
      },
    },
  })
}
```

{% endcode %}
