diff --git a/examples/logger/.env-example b/examples/logger/.env-example index a611f7b..cec5f0f 100644 --- a/examples/logger/.env-example +++ b/examples/logger/.env-example @@ -1,2 +1,3 @@ NEXT_PUBLIC_BETTER_STACK_INGESTING_URL="" -NEXT_PUBLIC_BETTER_STACK_SOURCE_TOKEN="" \ No newline at end of file +NEXT_PUBLIC_BETTER_STACK_SOURCE_TOKEN="" +NEXT_PUBLIC_BETTER_STACK_LOG_LEVEL="info" diff --git a/examples/logger/README.md b/examples/logger/README.md index f4da3c4..36b648a 100644 --- a/examples/logger/README.md +++ b/examples/logger/README.md @@ -1,34 +1,44 @@ -This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). +# Better Stack Next.js Logger Example -## Getting Started +This example demonstrates how to integrate Better Stack logging into a Next.js application. -First, run the development server: +## Setup + +### Environment Variables + +Copy `.env-example` to `.env` and fill in your Better Stack credentials: ```bash -npm run dev -# or -yarn dev -# or -pnpm dev +cp .env-example .env ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +Then edit `.env`: -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +```bash +# Your Better Stack source token +NEXT_PUBLIC_BETTER_STACK_SOURCE_TOKEN="your_source_token_here" -This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. +# Your Better Stack ingesting URL +NEXT_PUBLIC_BETTER_STACK_INGESTING_URL="https://your-cluster.betterstackdata.com" -## Learn More +# Log level (optional - debug/info/warn/error/off) +NEXT_PUBLIC_BETTER_STACK_LOG_LEVEL="info" +``` -To learn more about Next.js, take a look at the following resources: +You can create your source in Better Stack -> Telemetry -> [Sources](https://telemetry.betterstack.com/team/0/sources). -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +### Run the Example + +```bash +npm install +npm run dev +``` -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! +Open [http://localhost:3000](http://localhost:3000) to see the demo. -## Deploy on Vercel +Click the buttons to generate logs and see them appear in your Better Stack dashboard in real-time. -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +## Need Help? -Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. +- Check the [Better Stack Next.js documentation](https://betterstack.com/docs/logs/javascript/nextjs/) +- Visit the [Better Stack dashboard](https://telemetry.betterstack.com/team/0/tail) to view your logs diff --git a/examples/logger/app/globals.css b/examples/logger/app/globals.css index aaff6b2..df8fb88 100644 --- a/examples/logger/app/globals.css +++ b/examples/logger/app/globals.css @@ -39,6 +39,22 @@ --callout-border-rgb: 172, 175, 176; --card-rgb: 180, 185, 188; --card-border-rgb: 131, 134, 135; + + /* Example theme tokens (used by app/page.module.css) */ + --bs-bg: #f6f7fb; + --bs-bg-2: #ffffff; + --bs-text: #0b1220; + --bs-muted: #5b6577; + --bs-surface: #ffffffcc; + --bs-border: #e6eaf2; + --bs-shadow: 0 10px 30px rgba(16, 24, 40, 0.08); + --bs-link: #2563eb; + --bs-primary: #2563eb; + --bs-primary-hover: #1d4ed8; + --bs-danger: #dc2626; + --bs-danger-hover: #b91c1c; + --bs-success: #059669; + --bs-success-hover: #047857; } @media (prefers-color-scheme: dark) { @@ -71,6 +87,22 @@ --callout-border-rgb: 108, 108, 108; --card-rgb: 100, 100, 100; --card-border-rgb: 200, 200, 200; + + /* Example theme tokens (dark) */ + --bs-bg: #070b14; + --bs-bg-2: #0b1220; + --bs-text: #e5e7eb; + --bs-muted: #a4adbd; + --bs-surface: rgba(17, 24, 39, 0.72); + --bs-border: rgba(148, 163, 184, 0.18); + --bs-shadow: 0 16px 40px rgba(0, 0, 0, 0.45); + --bs-link: #60a5fa; + --bs-primary: #3b82f6; + --bs-primary-hover: #2563eb; + --bs-danger: #f87171; + --bs-danger-hover: #ef4444; + --bs-success: #34d399; + --bs-success-hover: #10b981; } } @@ -84,22 +116,33 @@ html, body { max-width: 100vw; overflow-x: hidden; + min-height: 100vh; } body { color: rgb(var(--foreground-rgb)); - background: linear-gradient( - to bottom, - transparent, - rgb(var(--background-end-rgb)) - ) - rgb(var(--background-start-rgb)); + background: + radial-gradient( + 1200px 500px at 30% -10%, + rgba(37, 99, 235, 0.22), + transparent 55% + ), + radial-gradient( + 900px 450px at 80% 0%, + rgba(16, 185, 129, 0.14), + transparent 60% + ), + linear-gradient(180deg, var(--bs-bg), var(--bs-bg-2)); + background-attachment: fixed; } a { - color: inherit; + color: var(--bs-link); text-decoration: none; } +a:hover { + text-decoration: underline; +} @media (prefers-color-scheme: dark) { html { diff --git a/examples/logger/app/page.module.css b/examples/logger/app/page.module.css index 06faba8..11850fa 100644 --- a/examples/logger/app/page.module.css +++ b/examples/logger/app/page.module.css @@ -1,231 +1,163 @@ -.main { - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - padding: 6rem; - min-height: 100vh; -} - -.description { - display: inherit; - justify-content: inherit; - align-items: inherit; - font-size: 0.85rem; - max-width: var(--max-width); - width: 100%; - z-index: 2; - font-family: var(--font-mono); +.container { + max-width: 1080px; + margin: 0 auto; + padding: 3rem 1.25rem 5rem; } -.description a { - display: flex; - justify-content: center; - align-items: center; - gap: 0.5rem; +.header { + text-align: center; + margin-bottom: 2rem; } -.description p { - position: relative; +.title { margin: 0; - padding: 1rem; - background-color: rgba(var(--callout-rgb), 0.5); - border: 1px solid rgba(var(--callout-border-rgb), 0.3); - border-radius: var(--border-radius); + font-size: 2.25rem; + letter-spacing: -0.02em; + color: var(--bs-text); } -.code { - font-weight: 700; - font-family: var(--font-mono); +.subtitle { + margin: 0.75rem auto 0; + max-width: 52ch; + color: var(--bs-muted); + line-height: 1.55; } .grid { display: grid; - grid-template-columns: repeat(4, minmax(25%, auto)); - width: var(--max-width); - max-width: 100%; + grid-template-columns: repeat(12, minmax(0, 1fr)); + gap: 1.25rem; + margin: 2rem 0 1.25rem; } .card { - padding: 1rem 1.2rem; - border-radius: var(--border-radius); - background: rgba(var(--card-rgb), 0); - border: 1px solid rgba(var(--card-border-rgb), 0); - transition: - background 200ms, - border 200ms; + grid-column: span 6; + background: var(--bs-surface); + border: 1px solid var(--bs-border); + border-radius: 14px; + padding: 1.25rem 1.25rem 1.1rem; + box-shadow: var(--bs-shadow); + backdrop-filter: blur(10px); } -.card span { - display: inline-block; - transition: transform 200ms; +.cardTitle { + margin: 0 0 0.4rem 0; + font-size: 1.1rem; + color: var(--bs-text); } -.card h2 { - font-weight: 600; - margin-bottom: 0.7rem; +.cardBody { + margin: 0 0 1rem 0; + color: var(--bs-muted); + line-height: 1.55; } -.card p { - margin: 0; - opacity: 0.6; - font-size: 0.9rem; - line-height: 1.5; - max-width: 30ch; -} - -.center { +.buttonRow { display: flex; - justify-content: center; - align-items: center; - position: relative; - padding: 4rem 0; + gap: 0.75rem; + flex-wrap: wrap; } -.center::before { - background: var(--secondary-glow); - border-radius: 50%; - width: 480px; - height: 360px; - margin-left: -400px; +.button { + appearance: none; + border: 1px solid transparent; + border-radius: 10px; + padding: 0.7rem 0.95rem; + font-weight: 600; + cursor: pointer; + transition: + transform 0.05s ease, + background-color 0.15s ease, + border-color 0.15s ease; + background: var(--bs-primary); + color: white; } -.center::after { - background: var(--primary-glow); - width: 240px; - height: 180px; - z-index: -1; +.button:hover { + background: var(--bs-primary-hover); } -.center::before, -.center::after { - content: ''; - left: 50%; - position: absolute; - filter: blur(45px); - transform: translateZ(0); +.button:active { + transform: translateY(1px); } -.logo { - position: relative; +.debug { + background: #64748b; } -/* Enable hover only on non-touch devices */ -@media (hover: hover) and (pointer: fine) { - .card:hover { - background: rgba(var(--card-rgb), 0.1); - border: 1px solid rgba(var(--card-border-rgb), 0.15); - } - .card:hover span { - transform: translateX(4px); - } +.debug:hover { + background: #475569; } -@media (prefers-reduced-motion) { - .card:hover span { - transform: none; - } +.danger { + background: var(--bs-danger); } -/* Mobile */ -@media (max-width: 700px) { - .content { - padding: 4rem; - } - - .grid { - grid-template-columns: 1fr; - margin-bottom: 120px; - max-width: 320px; - text-align: center; - } - - .card { - padding: 1rem 2.5rem; - } - - .card h2 { - margin-bottom: 0.5rem; - } - - .center { - padding: 8rem 0 6rem; - } - - .center::before { - transform: none; - height: 300px; - } +.danger:hover { + background: var(--bs-danger-hover); +} - .description { - font-size: 0.8rem; - } +.success { + background: var(--bs-success); +} - .description a { - padding: 1rem; - } +.success:hover { + background: var(--bs-success-hover); +} - .description p, - .description div { - display: flex; - justify-content: center; - position: fixed; - width: 100%; - } +.linkButton { + display: inline-flex; + align-items: center; + justify-content: center; + border: 1px solid var(--bs-border); + border-radius: 10px; + padding: 0.7rem 0.95rem; + text-decoration: none; + font-weight: 600; + color: var(--bs-text); + background: rgba(255, 255, 255, 0.04); +} - .description p { - align-items: center; - inset: 0 0 auto; - padding: 2rem 1rem 1.4rem; - border-radius: 0; - border: none; - border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); - background: linear-gradient( - to bottom, - rgba(var(--background-start-rgb), 1), - rgba(var(--callout-rgb), 0.5) - ); - background-clip: padding-box; - backdrop-filter: blur(24px); - } +.linkButton:hover { + text-decoration: none; + border-color: rgba(148, 163, 184, 0.35); +} - .description div { - align-items: flex-end; - pointer-events: none; - inset: auto 0 0; - padding: 2rem; - height: 200px; - background: linear-gradient( - to bottom, - transparent 0%, - rgb(var(--background-end-rgb)) 40% - ); - z-index: 1; - } +.instructions { + margin-top: 1.5rem; + background: var(--bs-surface); + border: 1px solid var(--bs-border); + border-radius: 14px; + padding: 1.25rem; + box-shadow: var(--bs-shadow); + backdrop-filter: blur(10px); } -/* Tablet and Smaller Desktop */ -@media (min-width: 701px) and (max-width: 1120px) { - .grid { - grid-template-columns: repeat(2, 50%); - } +.instructionsTitle { + margin: 0 0 0.75rem 0; + color: var(--bs-text); + font-size: 1.1rem; } -@media (prefers-color-scheme: dark) { - .vercelLogo { - filter: invert(1); - } +.list { + margin: 0; + padding-left: 1.25rem; + color: var(--bs-muted); + line-height: 1.65; +} - .logo { - filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70); - } +.code { + background: rgba(148, 163, 184, 0.15); + border: 1px solid rgba(148, 163, 184, 0.2); + padding: 0.1rem 0.35rem; + border-radius: 6px; + font-family: var(--font-mono); + font-size: 0.9em; + color: var(--bs-text); } -@keyframes rotate { - from { - transform: rotate(360deg); - } - to { - transform: rotate(0deg); +@media (max-width: 860px) { + .card { + grid-column: span 12; } } diff --git a/examples/logger/app/page.tsx b/examples/logger/app/page.tsx index ed114ca..d7230b9 100644 --- a/examples/logger/app/page.tsx +++ b/examples/logger/app/page.tsx @@ -3,25 +3,202 @@ import styles from './page.module.css'; import Link from 'next/link'; import { useLogger } from '@logtail/next/hooks'; +import { useEffect } from 'react'; function Home() { const logger = useLogger(); - logger.info('Hello from client', { foo: 'bar' }); + // Next.js best practice: avoid side-effects during render. + useEffect(() => { + logger.info('Page loaded', { page: 'home' }); + }, [logger]); - const logFromEventHandler = () => { - logger.info('Hello from event handler', { foo: 'bar' }); + const logInfo = () => { + logger.info('Info log from button click', { + action: 'button_click', + button: 'info', + userAgent: navigator.userAgent, + timestamp: new Date().toISOString(), + }); + }; + + const logDebug = () => { + logger.debug('Debug log from button click', { + action: 'button_click', + button: 'debug', + debugInfo: { + memory: (performance as any).memory + ? { + usedJSHeapSize: (performance as any).memory.usedJSHeapSize, + totalJSHeapSize: (performance as any).memory.totalJSHeapSize, + } + : 'unavailable', + viewport: { + width: window.innerWidth, + height: window.innerHeight, + }, + connection: (navigator as any).connection + ? { + effectiveType: (navigator as any).connection.effectiveType, + downlink: (navigator as any).connection.downlink, + } + : 'unavailable', + }, + timestamp: new Date().toISOString(), + }); + }; + + const logError = () => { + logger.error('Error log from button click', { + action: 'button_click', + button: 'error', + error: 'Simulated error for demo', + stack: new Error('Demo error').stack, + timestamp: new Date().toISOString(), + }); + }; + + const logWithContext = () => { + const userContext = { + userId: 'demo-user-123', + sessionId: 'session-' + Date.now(), + preferences: { theme: 'dark', language: 'en' }, + }; + + logger.info('Complex log with user context', { + action: 'context_demo', + context: userContext, + metadata: { + source: 'demo_app', + version: '1.0.0', + environment: 'development', + }, + timestamp: new Date().toISOString(), + }); }; return ( -
-

- RSC Page -

-

- Worker -

- +
+
+

Better Stack Next.js Demo

+

+ Send a log and verify it shows up in{' '} + + Better Stack + + . +

+
+ +
+
+

Info log

+

+ Send a basic structured log from the browser. +

+
+ +
+
+ +
+

Error log

+

+ Send an error log including a stack trace. +

+
+ +
+
+ +
+

Structured context

+

+ Send a richer log with a nested context object. +

+
+ +
+
+ +
+

Debug log

+

+ Set{' '} + + NEXT_PUBLIC_BETTER_STACK_LOG_LEVEL + {' '} + to debug to ensure debug logs + will be forwarded. +

+
+ +
+
+ +
+

Other pages

+

Try logging in different contexts.

+
+ + RSC page + + + Worker page + +
+
+
+ +
+

Setup

+
    +
  1. + Create a source in your{' '} + + Better Stack dashboard + + . +
  2. +
  3. + Copy the .env-example template + to .env. +
  4. +
  5. + Fill in the ingesting URL and source token in{' '} + .env. +
  6. +
+
); } diff --git a/examples/logger/app/rsc/page.tsx b/examples/logger/app/rsc/page.tsx index 86ab48e..6d7e735 100644 --- a/examples/logger/app/rsc/page.tsx +++ b/examples/logger/app/rsc/page.tsx @@ -1,24 +1,43 @@ -import Image from 'next/image'; -import { Inter } from 'next/font/google'; -import styles from '../page.module.css'; import { Logger } from '@logtail/next'; import Link from 'next/link'; -const inter = Inter({ subsets: ['latin'] }); +import styles from './rsc.module.css'; async function BetterStackLoggerPage() { const logger = new Logger(); - logger.info('RSC', { foo: 'bar' }); + logger.info('RSC page visited', { + page: 'rsc', + timestamp: new Date().toISOString(), + renderType: 'server', + }); await logger.flush(); return ( -
-

- Home -

-

- Worker -

+
+
+

🔧 React Server Component

+

+ This page is rendered on the server using React Server Components. The + logger instance is created server-side and logs are sent during server + rendering. +

+ +
+

✓ Server-side logging complete

+

+ Check your Better Stack dashboard to see the server-side log entry. +

+
+ +
+ + ← Back to Home + + + Web Worker → + +
+
); } diff --git a/examples/logger/app/rsc/rsc.module.css b/examples/logger/app/rsc/rsc.module.css new file mode 100644 index 0000000..ef25205 --- /dev/null +++ b/examples/logger/app/rsc/rsc.module.css @@ -0,0 +1,88 @@ +.container { + min-height: 100vh; + padding: 2rem; + display: flex; + align-items: center; + justify-content: center; +} + +.content { + max-width: 600px; + width: 100%; +} + +.content h1 { + font-size: 2.5rem; + margin-bottom: 1rem; + color: var(--bs-text); + text-align: center; +} + +.description { + font-size: 1.1rem; + color: var(--bs-muted); + text-align: center; + margin-bottom: 2rem; + line-height: 1.6; +} + +.card { + background: var(--bs-surface); + backdrop-filter: blur(12px); + border: 1px solid var(--bs-border); + border-radius: var(--border-radius); + padding: 2rem; + margin-bottom: 2rem; + box-shadow: var(--bs-shadow); +} + +.card h3 { + margin: 0 0 0.5rem 0; + font-size: 1.25rem; + color: var(--bs-text); +} + +.card p { + margin: 0; + color: var(--bs-muted); + line-height: 1.5; +} + +.links { + display: flex; + gap: 1rem; + justify-content: center; + flex-wrap: wrap; +} + +.link { + display: inline-block; + background: var(--bs-surface); + backdrop-filter: blur(12px); + color: var(--bs-link); + text-decoration: none; + padding: 0.75rem 1.5rem; + border-radius: 8px; + font-weight: 500; + border: 1px solid var(--bs-border); + transition: all 0.2s; +} + +.link:hover { + background: var(--bs-primary); + color: white; + text-decoration: none; + border-color: var(--bs-primary); + transform: translateY(-2px); + box-shadow: var(--bs-shadow); +} + +@media (max-width: 768px) { + .content h1 { + font-size: 2rem; + } + + .description { + font-size: 1rem; + } +} diff --git a/examples/logger/app/worker/page.tsx b/examples/logger/app/worker/page.tsx index d548465..cb53eac 100644 --- a/examples/logger/app/worker/page.tsx +++ b/examples/logger/app/worker/page.tsx @@ -1,14 +1,19 @@ 'use client'; import { useEffect, useRef, useCallback } from 'react'; -import styles from '../page.module.css'; +import Link from 'next/link'; +import styles from './worker.module.css'; export default function WorkerPage() { const workerRef = useRef(); useEffect(() => { workerRef.current = new Worker(new URL('../../worker.ts', import.meta.url)); - workerRef.current.onmessage = (event: MessageEvent) => - alert(`WebWorker Response => ${event.data}`); + workerRef.current.onmessage = (event: MessageEvent) => { + console.log('WebWorker Response:', event.data); + alert( + `WebWorker completed and logged ${event.data} iterations to Better Stack!`, + ); + }; return () => { workerRef.current?.terminate(); }; @@ -19,9 +24,32 @@ export default function WorkerPage() { }, []); return ( -
-

Do work in a WebWorker!

- +
+
+

⚙️ Web Worker Logging

+

+ This page demonstrates logging from a Web Worker. Click the button + below to start a computation in a background thread and send logs to + Better Stack. +

+ +
+

🚀 Start Background Task

+

The worker will process 100,000 iterations and log the results.

+ +
+ +
+ + ← Back to Home + + + Server Component → + +
+
); } diff --git a/examples/logger/app/worker/worker.module.css b/examples/logger/app/worker/worker.module.css new file mode 100644 index 0000000..e05be2e --- /dev/null +++ b/examples/logger/app/worker/worker.module.css @@ -0,0 +1,117 @@ +.container { + min-height: 100vh; + padding: 2rem; + display: flex; + align-items: center; + justify-content: center; +} + +.content { + max-width: 600px; + width: 100%; +} + +.content h1 { + font-size: 2.5rem; + margin-bottom: 1rem; + color: var(--bs-text); + text-align: center; +} + +.description { + font-size: 1.1rem; + color: var(--bs-muted); + text-align: center; + margin-bottom: 2rem; + line-height: 1.6; +} + +.card { + background: var(--bs-surface); + backdrop-filter: blur(12px); + border: 1px solid var(--bs-border); + border-radius: var(--border-radius); + padding: 2rem; + margin-bottom: 2rem; + box-shadow: var(--bs-shadow); + text-align: center; +} + +.card h3 { + margin: 0 0 0.5rem 0; + font-size: 1.25rem; + color: var(--bs-text); +} + +.card p { + margin: 0 0 1.5rem 0; + color: var(--bs-muted); + line-height: 1.5; +} + +.button { + background: var(--bs-primary); + color: white; + border: none; + padding: 0.75rem 1.5rem; + border-radius: 6px; + font-size: 1rem; + font-weight: 500; + cursor: pointer; + transition: all 0.2s; + width: 100%; + max-width: 300px; +} + +.button:hover { + background: var(--bs-primary-hover); + transform: translateY(-2px); + box-shadow: var(--bs-shadow); +} + +.button:active { + transform: translateY(0); +} + +.links { + display: flex; + gap: 1rem; + justify-content: center; + flex-wrap: wrap; +} + +.link { + display: inline-block; + background: var(--bs-surface); + backdrop-filter: blur(12px); + color: var(--bs-link); + text-decoration: none; + padding: 0.75rem 1.5rem; + border-radius: 8px; + font-weight: 500; + border: 1px solid var(--bs-border); + transition: all 0.2s; +} + +.link:hover { + background: var(--bs-primary); + color: white; + text-decoration: none; + border-color: var(--bs-primary); + transform: translateY(-2px); + box-shadow: var(--bs-shadow); +} + +@media (max-width: 768px) { + .content h1 { + font-size: 2rem; + } + + .description { + font-size: 1rem; + } + + .button { + max-width: 100%; + } +} diff --git a/examples/trpc-app-router/.env-example b/examples/trpc-app-router/.env-example index a611f7b..cec5f0f 100644 --- a/examples/trpc-app-router/.env-example +++ b/examples/trpc-app-router/.env-example @@ -1,2 +1,3 @@ NEXT_PUBLIC_BETTER_STACK_INGESTING_URL="" -NEXT_PUBLIC_BETTER_STACK_SOURCE_TOKEN="" \ No newline at end of file +NEXT_PUBLIC_BETTER_STACK_SOURCE_TOKEN="" +NEXT_PUBLIC_BETTER_STACK_LOG_LEVEL="info" diff --git a/examples/trpc-app-router/README.md b/examples/trpc-app-router/README.md index f493d9e..ce037a2 100644 --- a/examples/trpc-app-router/README.md +++ b/examples/trpc-app-router/README.md @@ -1,11 +1,40 @@ -# @logtail/next tRPC + App Router Example +# Better Stack Next.js tRPC + App Router Example This is an example for using @logtail/next with the Next.js App Router and tRPC. It is based on `create-t3-app`. ## Setup -- Clone this repo -- Add the ingesting URL and source token to `.env` -- Run `npm install` -- Run `npm run dev` -- Visit `localhost:3000`. Your app should now be logging to Better Stack. +Copy `.env-example` to `.env` and fill in your Better Stack credentials: + +```bash +cp .env-example .env +``` + +Then edit `.env`: + +```bash +# Your Better Stack source token +NEXT_PUBLIC_BETTER_STACK_SOURCE_TOKEN="your_source_token_here" + +# Your Better Stack ingesting URL +NEXT_PUBLIC_BETTER_STACK_INGESTING_URL="https://your-cluster.betterstackdata.com" + +# Log level (optional - debug/info/warn/error/off) +NEXT_PUBLIC_BETTER_STACK_LOG_LEVEL="info" +``` + +You can create your source in Better Stack -> Telemetry -> [Sources](https://telemetry.betterstack.com/team/0/sources). + +## Run the Example + +```bash +npm install +npm run dev +``` + +Open [http://localhost:3000](http://localhost:3000) to see the demo. + +## Need Help? + +- Check the [Better Stack Next.js documentation](https://betterstack.com/docs/logs/javascript/nextjs/) +- Visit the [Better Stack dashboard](https://telemetry.betterstack.com/team/0/tail) to view your logs