From f0fa024a9f27c50035bf3a9e90b11d0794dcfc5d Mon Sep 17 00:00:00 2001 From: Petr Heinz Date: Wed, 7 Jan 2026 11:54:48 +0100 Subject: [PATCH 1/2] Fix example project styles and READMEs --- examples/logger/.env-example | 3 +- examples/logger/README.md | 48 +-- examples/logger/app/globals.css | 49 +++- examples/logger/app/page.module.css | 293 +++++++------------ examples/logger/app/page.tsx | 159 +++++++++- examples/logger/app/rsc/page.tsx | 36 ++- examples/logger/app/rsc/rsc.module.css | 88 ++++++ examples/logger/app/worker/page.tsx | 33 ++- examples/logger/app/worker/worker.module.css | 117 ++++++++ examples/trpc-app-router/.env-example | 3 +- examples/trpc-app-router/README.md | 41 ++- 11 files changed, 625 insertions(+), 245 deletions(-) create mode 100644 examples/logger/app/rsc/rsc.module.css create mode 100644 examples/logger/app/worker/worker.module.css 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..ff2dbc8 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..80d7781 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,25 @@ 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..a3208a5 100644 --- a/examples/logger/app/page.module.css +++ b/examples/logger/app/page.module.css @@ -1,231 +1,160 @@ -.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; -} - -.card span { - display: inline-block; - transition: transform 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 h2 { - font-weight: 600; - margin-bottom: 0.7rem; +.cardTitle { + margin: 0 0 0.4rem 0; + font-size: 1.1rem; + color: var(--bs-text); } -.card p { - margin: 0; - opacity: 0.6; - font-size: 0.9rem; - line-height: 1.5; - max-width: 30ch; +.cardBody { + margin: 0 0 1rem 0; + color: var(--bs-muted); + line-height: 1.55; } -.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..18e0136 100644 --- a/examples/logger/app/page.tsx +++ b/examples/logger/app/page.tsx @@ -3,25 +3,162 @@ 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..a105110 100644 --- a/examples/logger/app/rsc/page.tsx +++ b/examples/logger/app/rsc/page.tsx @@ -1,24 +1,36 @@ -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..b45fb78 100644 --- a/examples/logger/app/worker/page.tsx +++ b/examples/logger/app/worker/page.tsx @@ -1,14 +1,17 @@ '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 +22,27 @@ 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..75495bf 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 From c4a7f770bbd475fb6211bad6492f96f933392ed2 Mon Sep 17 00:00:00 2001 From: Petr Heinz Date: Wed, 7 Jan 2026 13:31:36 +0100 Subject: [PATCH 2/2] npm run format --- examples/logger/README.md | 2 +- examples/logger/app/globals.css | 12 ++++- examples/logger/app/page.module.css | 5 +- examples/logger/app/page.tsx | 78 ++++++++++++++++++++++------- examples/logger/app/rsc/page.tsx | 23 ++++++--- examples/logger/app/worker/page.tsx | 19 ++++--- examples/trpc-app-router/README.md | 2 +- 7 files changed, 103 insertions(+), 38 deletions(-) diff --git a/examples/logger/README.md b/examples/logger/README.md index ff2dbc8..36b648a 100644 --- a/examples/logger/README.md +++ b/examples/logger/README.md @@ -25,7 +25,7 @@ NEXT_PUBLIC_BETTER_STACK_INGESTING_URL="https://your-cluster.betterstackdata.com NEXT_PUBLIC_BETTER_STACK_LOG_LEVEL="info" ``` -You can create your source in Better Stack -> Telemetry -> [Sources](https://telemetry.betterstack.com/team/0/sources). +You can create your source in Better Stack -> Telemetry -> [Sources](https://telemetry.betterstack.com/team/0/sources). ### Run the Example diff --git a/examples/logger/app/globals.css b/examples/logger/app/globals.css index 80d7781..df8fb88 100644 --- a/examples/logger/app/globals.css +++ b/examples/logger/app/globals.css @@ -122,8 +122,16 @@ body { body { color: rgb(var(--foreground-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%), + 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; } diff --git a/examples/logger/app/page.module.css b/examples/logger/app/page.module.css index a3208a5..11850fa 100644 --- a/examples/logger/app/page.module.css +++ b/examples/logger/app/page.module.css @@ -65,7 +65,10 @@ 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; + transition: + transform 0.05s ease, + background-color 0.15s ease, + border-color 0.15s ease; background: var(--bs-primary); color: white; } diff --git a/examples/logger/app/page.tsx b/examples/logger/app/page.tsx index 18e0136..d7230b9 100644 --- a/examples/logger/app/page.tsx +++ b/examples/logger/app/page.tsx @@ -27,18 +27,22 @@ function Home() { action: 'button_click', button: 'debug', debugInfo: { - memory: (performance as any).memory ? { - usedJSHeapSize: (performance as any).memory.usedJSHeapSize, - totalJSHeapSize: (performance as any).memory.totalJSHeapSize, - } : 'unavailable', + 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', + connection: (navigator as any).connection + ? { + effectiveType: (navigator as any).connection.effectiveType, + downlink: (navigator as any).connection.downlink, + } + : 'unavailable', }, timestamp: new Date().toISOString(), }); @@ -79,7 +83,11 @@ function Home() {

Better Stack Next.js Demo

Send a log and verify it shows up in{' '} - + Better Stack . @@ -89,7 +97,9 @@ function Home() {

Info log

-

Send a basic structured log from the browser.

+

+ Send a basic structured log from the browser. +

@@ -109,9 +125,15 @@ function Home() {

Structured context

-

Send a richer log with a nested context object.

+

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

-
@@ -119,9 +141,20 @@ function Home() {

Debug log

-

Set NEXT_PUBLIC_BETTER_STACK_LOG_LEVEL to debug to ensure debug logs will be forwarded.

+

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

-
@@ -146,16 +179,23 @@ function Home() {
  1. Create a source in your{' '} - + Better Stack dashboard .
  2. - Copy the .env-example template to .env. + Copy the .env-example template + to .env.
  3. - Fill in the ingesting URL and source token in .env. + Fill in the ingesting URL and source token in{' '} + .env.
diff --git a/examples/logger/app/rsc/page.tsx b/examples/logger/app/rsc/page.tsx index a105110..6d7e735 100644 --- a/examples/logger/app/rsc/page.tsx +++ b/examples/logger/app/rsc/page.tsx @@ -4,10 +4,10 @@ import styles from './rsc.module.css'; async function BetterStackLoggerPage() { const logger = new Logger(); - logger.info('RSC page visited', { + logger.info('RSC page visited', { page: 'rsc', timestamp: new Date().toISOString(), - renderType: 'server' + renderType: 'server', }); await logger.flush(); @@ -17,18 +17,25 @@ async function BetterStackLoggerPage() {

🔧 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. + 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.

+

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

- ← Back to Home - Web Worker → + + ← Back to Home + + + Web Worker → +
diff --git a/examples/logger/app/worker/page.tsx b/examples/logger/app/worker/page.tsx index b45fb78..cb53eac 100644 --- a/examples/logger/app/worker/page.tsx +++ b/examples/logger/app/worker/page.tsx @@ -10,7 +10,9 @@ export default function WorkerPage() { workerRef.current = new Worker(new URL('../../worker.ts', import.meta.url)); workerRef.current.onmessage = (event: MessageEvent) => { console.log('WebWorker Response:', event.data); - alert(`WebWorker completed and logged ${event.data} iterations to Better Stack!`); + alert( + `WebWorker completed and logged ${event.data} iterations to Better Stack!`, + ); }; return () => { workerRef.current?.terminate(); @@ -26,10 +28,11 @@ export default function WorkerPage() {

⚙️ 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. + 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.

@@ -39,8 +42,12 @@ export default function WorkerPage() {
- ← Back to Home - Server Component → + + ← Back to Home + + + Server Component → +
diff --git a/examples/trpc-app-router/README.md b/examples/trpc-app-router/README.md index 75495bf..ce037a2 100644 --- a/examples/trpc-app-router/README.md +++ b/examples/trpc-app-router/README.md @@ -23,7 +23,7 @@ NEXT_PUBLIC_BETTER_STACK_INGESTING_URL="https://your-cluster.betterstackdata.com NEXT_PUBLIC_BETTER_STACK_LOG_LEVEL="info" ``` -You can create your source in Better Stack -> Telemetry -> [Sources](https://telemetry.betterstack.com/team/0/sources). +You can create your source in Better Stack -> Telemetry -> [Sources](https://telemetry.betterstack.com/team/0/sources). ## Run the Example