Skip to content

Commit

Permalink
Merge pull request #161 from axiomhq/support-web-workers
Browse files Browse the repository at this point in the history
Support webworkers
  • Loading branch information
dasfmi authored Oct 25, 2023
2 parents 679b67c + 76ab477 commit b85adae
Show file tree
Hide file tree
Showing 12 changed files with 67 additions and 17 deletions.
1 change: 1 addition & 0 deletions examples/logger/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ function Home() {
return (
<main className={styles.main}>
<h1><Link href="/rsc">RSC Page</Link></h1>
<h1><Link href="/worker">Worker</Link></h1>
</main>
);
}
Expand Down
1 change: 1 addition & 0 deletions examples/logger/app/rsc/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ async function AxiomLoggerPage() {
return (
<main className={styles.main}>
<h1><Link href="/">Home</Link></h1>
<h1><Link href="/worker">Worker</Link></h1>
</main>
);
}
Expand Down
27 changes: 27 additions & 0 deletions examples/logger/app/worker/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use client'
import { useEffect, useRef, useCallback } from 'react'
import styles from '../page.module.css';

export default function WorkerPage() {
const workerRef = useRef<Worker>()

useEffect(() => {
workerRef.current = new Worker(new URL('../../worker.ts', import.meta.url))
workerRef.current.onmessage = (event: MessageEvent<number>) =>
alert(`WebWorker Response => ${event.data}`)
return () => {
workerRef.current?.terminate()
}
}, [])

const handleWork = useCallback(async () => {
workerRef.current?.postMessage(100000)
}, [])

return (
<main className={styles.main}>
<p>Do work in a WebWorker!</p>
<button onClick={handleWork}>Send Logs to Axiom</button>
</main>
)
}
9 changes: 9 additions & 0 deletions examples/logger/worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// This is a module worker, so we can use imports (in the browser too!)
import { log } from 'next-axiom';

addEventListener('message', async (event: MessageEvent<number>) => {
log.info("fired from worker")
log.info(event.data.toString())
await log.flush()
postMessage('done')
})
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "next-axiom",
"description": "Send WebVitals from your Next.js project to Axiom.",
"version": "1.0.0",
"version": "1.1.0",
"author": "Axiom, Inc.",
"license": "MIT",
"contributors": [
Expand Down
11 changes: 11 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,20 @@ import GenericConfig from './platform/generic';
import VercelConfig from './platform/vercel';
import NetlifyConfig from './platform/netlify';

declare global {
var EdgeRuntime: string; // Edge runtime
var WorkerGlobalScope: any; // Non-standard global only used on Cloudflare: https://developers.cloudflare.com/workers/runtime-apis/websockets
}

export const Version = require('../package.json').version;
export const isVercel = process.env.NEXT_PUBLIC_AXIOM_INGEST_ENDPOINT || process.env.AXIOM_INGEST_ENDPOINT;
export const isNetlify = process.env.NETLIFY == 'true';
export const isWebWorker =
typeof self !== 'undefined' &&
typeof globalThis.WorkerGlobalScope !== 'undefined' &&
self instanceof WorkerGlobalScope;
export const isBrowser = typeof window !== 'undefined' || isWebWorker;
export const isEdgeRuntime = globalThis.EdgeRuntime ? true : false;

// Detect the platform provider, and return the appropriate config
// fallback to generic config if no provider is detected
Expand Down
7 changes: 4 additions & 3 deletions src/logger.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { config, isVercel, Version } from './config';
import { config, isBrowser, isVercel, Version } from './config';
import { NetlifyInfo } from './platform/netlify';
import { isNoPrettyPrint, throttle } from './shared';

const url = config.getLogsEndpoint();

const LOG_LEVEL = process.env.NEXT_PUBLIC_AXIOM_LOG_LEVEL || 'debug';

export interface LogEvent {
Expand Down Expand Up @@ -178,7 +179,7 @@ export class Logger {
if (typeof fetch === 'undefined') {
const fetch = await require('whatwg-fetch');
return fetch(url, reqOptions).catch(console.error);
} else if (config.isBrowser && isVercel && navigator.sendBeacon) {
} else if (isBrowser && isVercel && navigator.sendBeacon) {
// sendBeacon fails if message size is greater than 64kb, so
// we fall back to fetch.
// Navigator has to be bound to ensure it does not error in some browsers
Expand Down Expand Up @@ -244,7 +245,7 @@ export function prettyPrint(ev: LogEvent) {
let msgString = '';
let args: any[] = [ev.level, ev.message];

if (config.isBrowser) {
if (isBrowser) {
msgString = '%c%s - %s';
args = [`color: ${levelColors[ev.level].browser};`, ...args];
} else {
Expand Down
6 changes: 3 additions & 3 deletions src/platform/generic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { GetServerSidePropsContext, NextApiRequest } from "next";
import { LogEvent, RequestReport } from "../logger";
import { EndpointType } from "../shared";
import type Provider from "./base";
import { isBrowser } from "../config";

// This is the generic config class for all platforms that doesn't have a special
// implementation (e.g: vercel, netlify). All config classes extends this one.
export default class GenericConfig implements Provider {
proxyPath = '/_axiom';
isBrowser = typeof window !== 'undefined';
shouldSendEdgeReport = false;
token = process.env.NEXT_PUBLIC_AXIOM_TOKEN || process.env.AXIOM_TOKEN;
dataset = process.env.NEXT_PUBLIC_AXIOM_DATASET || process.env.AXIOM_DATASET;
Expand All @@ -24,11 +24,11 @@ export default class GenericConfig implements Provider {
}

getLogsEndpoint(): string {
return this.isBrowser ? `${this.proxyPath}/logs` : this.getIngestURL(EndpointType.logs);
return isBrowser ? `${this.proxyPath}/logs` : this.getIngestURL(EndpointType.logs);
}

getWebVitalsEndpoint(): string {
return this.isBrowser ? `${this.proxyPath}/logs` : this.getIngestURL(EndpointType.webVitals);
return isBrowser ? `${this.proxyPath}/web-vitals` : this.getIngestURL(EndpointType.webVitals);
}

wrapWebVitalsObject(metrics: any[]): any {
Expand Down
5 changes: 5 additions & 0 deletions src/platform/vercel.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isBrowser } from '../config';
import { LogEvent } from '../logger';
import { EndpointType } from '../shared';
import type Provider from './base';
Expand Down Expand Up @@ -27,6 +28,10 @@ export default class VercelConfig extends GenericConfig implements Provider {
return `${this.proxyPath}/web-vitals`;
}

getLogsEndpoint(): string {
return isBrowser ? `${this.proxyPath}/logs` : this.getIngestURL(EndpointType.logs);
}

wrapWebVitalsObject(metrics: any[]) {
return {
webVitals: metrics,
Expand Down
4 changes: 2 additions & 2 deletions src/webVitals/webVitals.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NextWebVitalsMetric } from 'next/app';
import { config, isVercel, Version } from '../config';
import { config, isBrowser, isVercel, Version } from '../config';
import { throttle } from '../shared';

const url = config.getWebVitalsEndpoint();
Expand Down Expand Up @@ -35,7 +35,7 @@ function sendMetrics() {
fetch(url, reqOptions).catch(console.error);
}

if (config.isBrowser && isVercel && navigator.sendBeacon) {
if (isBrowser && isVercel && navigator.sendBeacon) {
try {
// See https://github.com/vercel/next.js/pull/26601
// Navigator has to be bound to ensure it does not error in some browsers
Expand Down
7 changes: 1 addition & 6 deletions src/withAxiom.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import { NextConfig } from 'next';
import { Rewrite } from 'next/dist/lib/load-custom-routes';
import { config } from './config';
import { config, isEdgeRuntime } from './config';
import { Logger, RequestReport } from './logger';
import { type NextRequest, type NextResponse } from 'next/server';
import { EndpointType } from './shared';

declare global {
var EdgeRuntime: string;
}

export function withAxiomNextConfig(nextConfig: NextConfig): NextConfig {
return {
...nextConfig,
Expand Down Expand Up @@ -75,7 +71,6 @@ export function withAxiomRouteHandler(handler: NextHandler): NextHandler {
ip: req.headers.get('x-forwarded-for'),
region,
};
const isEdgeRuntime = globalThis.EdgeRuntime ? true : false;

const logger = new Logger({ req: report, source: isEdgeRuntime ? 'edge' : 'lambda' });
const axiomContext = req as AxiomRequest;
Expand Down

1 comment on commit b85adae

@vercel
Copy link

@vercel vercel bot commented on b85adae Oct 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

next-axiom-example – ./

next-axiom-example-git-main.axiom.dev
next-axiom-example-lemon.vercel.app
next-axiom-example.axiom.dev

Please sign in to comment.