Skip to content

Commit

Permalink
Merge pull request #6 from axiomhq/islam/dx-77-get-rid-of-asyncawait-…
Browse files Browse the repository at this point in the history
…in-next-axiom

split behaviour between frontend and backend
  • Loading branch information
bahlo authored Jun 9, 2022
2 parents b27c8fa + 2700523 commit bca8dfb
Show file tree
Hide file tree
Showing 13 changed files with 83 additions and 72 deletions.
10 changes: 3 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,7 @@ log.warn('be careful!')
log.error('oops!')
```

Deploy your site and watch data coming into your Axiom dashboard.
Deploy your site and watch data coming into your Axiom dataset.

The log functions are asynchoronus functions, make sure to wait for them
when its appropriate, like when running on serverless/edge functions:

```js
await log.info('function will wait for the log to be sent')
```
:warning: If you log from a function, please call `await log.flush()` at the end
to ensure log delivery.
2 changes: 1 addition & 1 deletion __tests__/config.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getIngestURL, EndpointType } from '../src/config';
import { getIngestURL, EndpointType } from '../src/shared';

test('reading ingest endpoint', () => {
process.env.AXIOM_INGEST_ENDPOINT = 'https://axiom.co/api/test';
Expand Down
19 changes: 14 additions & 5 deletions __tests__/log.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,20 @@
* @jest-environment jsdom
*/
import { log } from '../src/logger';
import fetch from 'cross-fetch';

jest.mock('cross-fetch');
global.fetch = jest.fn() as jest.Mock;
jest.useFakeTimers();

test('logging', async () => {
await log.info('hello, world!');
expect(fetch).toHaveBeenCalled();
test('sending logs from browser', async () => {
log.info('hello, world!');
expect(fetch).toHaveBeenCalledTimes(0);

jest.advanceTimersByTime(1000);
expect(fetch).toHaveBeenCalledTimes(1);

log.info('hello, world!');
expect(fetch).toHaveBeenCalledTimes(1);

await log.flush();
expect(fetch).toHaveBeenCalledTimes(2);
});
4 changes: 2 additions & 2 deletions examples/logger/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
},
"dependencies": {
"next": "12.1.4",
"next-axiom": "github:axiomhq/next-axiom#150327c7c2b2b48f0003fc3b6d03fc0e06b7ba17",
"next-axiom": "github:axiomhq/next-axiom#main",
"react": "18.0.0",
"react-dom": "18.0.0",
"swr": "^1.3.0"
Expand All @@ -17,4 +17,4 @@
"eslint": "8.12.0",
"eslint-config-next": "12.1.4"
}
}
}
2 changes: 1 addition & 1 deletion examples/logger/pages/_middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ import { NextResponse } from 'next/server'
import { log } from 'next-axiom'

export async function middleware(_req, _ev) {
await log.info("Hello from middleware", { 'bar': 'baz' });
log.info("Hello from middleware", { 'bar': 'baz' });
return NextResponse.next()
}
2 changes: 1 addition & 1 deletion examples/logger/pages/api/hello.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
import { log } from 'next-axiom'

export default async function handler(req, res) {
await log.info('Hello from function', { url: req.url });
log.info('Hello from function', { url: req.url });
res.status(200).json({ name: 'John Doe' })
}
4 changes: 2 additions & 2 deletions examples/logger/pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { log } from 'next-axiom'
import useSWR from 'swr'

export async function getStaticProps(context) {
await log.info('Hello from SSR', { context })
log.info('Hello from SSR', { context })
return {
props: {},
}
}

const fetcher = async (...args) => {
console.log('Fetching', args)
await log.info('Hello from SWR', { args });
log.info('Hello from SWR', { args });
const res = await fetch(...args);
return await res.json();
}
Expand Down
32 changes: 6 additions & 26 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": "0.6.0",
"version": "0.7.0",
"author": "Axiom, Inc.",
"license": "MIT",
"contributors": [
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NextConfig } from 'next';
import { proxyPath, EndpointType, getIngestURL } from './config';
import { proxyPath, EndpointType, getIngestURL } from './shared';
export { reportWebVitals } from './webVitals';
export { log } from './logger';

Expand Down
56 changes: 40 additions & 16 deletions src/logger.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { proxyPath, isBrowser, EndpointType, getIngestURL } from './config';
import { proxyPath, isBrowser, EndpointType, getIngestURL } from './shared';
import { debounce } from './shared';

const url = isBrowser ? `${proxyPath}/logs` : getIngestURL(EndpointType.logs);
const debouncedSendLogs = debounce(sendLogs, 1000);
let logEvents: any[] = [];

async function _log(level: string, message: string, args: any = {}) {
function _log(level: string, message: string, args: any = {}) {
if (!url) {
console.warn('axiom: NEXT_PUBLIC_AXIOM_INGEST_ENDPOINT is not defined');
return;
Expand All @@ -12,21 +15,42 @@ async function _log(level: string, message: string, args: any = {}) {
if (Object.keys(args).length > 0) {
logEvent['fields'] = args;
}
const body = JSON.stringify([logEvent]);

if (typeof fetch === 'undefined') {
const fetch = await require('cross-fetch');
await fetch(url, { body, method: 'POST', keepalive: true });
} else if (isBrowser && navigator.sendBeacon) {
navigator.sendBeacon(url, body);
} else {
await fetch(url, { body, method: 'POST', keepalive: true });
}

logEvents.push(logEvent);
debouncedSendLogs();
}

export const log = {
debug: async (message: string, args: any = {}) => await _log('debug', message, args),
info: async (message: string, args: any = {}) => await _log('info', message, args),
warn: async (message: string, args: any = {}) => await _log('warn', message, args),
error: async (message: string, args: any = {}) => await _log('error', message, args),
debug: (message: string, args: any = {}) => _log('debug', message, args),
info: (message: string, args: any = {}) => _log('info', message, args),
warn: (message: string, args: any = {}) => _log('warn', message, args),
error: (message: string, args: any = {}) => _log('error', message, args),
flush: async () => {
await sendLogs();
},
};

async function sendLogs() {
if (!logEvents.length) {
return;
}

const method = 'POST';
const keepalive = true;
const body = JSON.stringify(logEvents);
// clear pending logs
logEvents = [];

try {
if (typeof fetch === 'undefined') {
const fetch = await require('cross-fetch');
await fetch(url, { body, method, keepalive });
} else if (isBrowser && navigator.sendBeacon) {
navigator.sendBeacon(url, body);
} else {
await fetch(url, { body, method, keepalive });
}
} catch (e) {
console.error(`Failed to send logs to Axiom: ${e}`);
}
}
9 changes: 9 additions & 0 deletions src/config.ts → src/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,17 @@ export const getIngestURL = (t: EndpointType) => {
if (!ingestEndpoint) {
return '';
}

const url = new URL(ingestEndpoint);
// attach type query param based on passed EndpointType
url.searchParams.set('type', t.toString());
return url.toString();
};

export const debounce = (fn: Function, ms = 300) => {
let timeoutId: ReturnType<typeof setTimeout>;
return async function (this: any, ...args: any[]) {
clearTimeout(timeoutId);
timeoutId = setTimeout(async () => await fn.apply(this, args), ms);
};
};
11 changes: 2 additions & 9 deletions src/webVitals.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
import { NextWebVitalsMetric } from 'next/app';
import { isBrowser, proxyPath } from './config';
import { isBrowser, proxyPath } from './shared';
import { debounce } from './shared';

export { log } from './logger';

const url = `${proxyPath}/web-vitals`;

const debounce = (fn: Function, ms = 300) => {
let timeoutId: ReturnType<typeof setTimeout>;
return function (this: any, ...args: any[]) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn.apply(this, args), ms);
};
};

export declare type WebVitalsMetric = NextWebVitalsMetric & { route: string };

const debouncedSendMetrics = debounce(sendMetrics, 1000);
Expand Down

1 comment on commit bca8dfb

@vercel
Copy link

@vercel vercel bot commented on bca8dfb Jun 9, 2022

Choose a reason for hiding this comment

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

Please sign in to comment.