Skip to content

Commit

Permalink
Add sendImmediately option
Browse files Browse the repository at this point in the history
  • Loading branch information
Vadorequest committed Jun 23, 2021
1 parent 5c7f1f3 commit 3d15fcc
Showing 1 changed file with 33 additions and 22 deletions.
55 changes: 33 additions & 22 deletions src/modules/core/amplitude/amplitudeServerClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { AMPLITUDE_EVENTS } from '@/modules/core/amplitude/events';
import { GenericObject } from '@/modules/core/data/types/GenericObject';
import { createLogger } from '@/modules/core/logging/logger';
import { init } from '@amplitude/node';
import { Event } from '@amplitude/types';
import { LogLevel } from '@amplitude/types/dist/src/logger';
import { Response } from '@amplitude/types/dist/src/response';
import * as Sentry from '@sentry/node';
import { Context } from '@sentry/types';
import startsWith from 'lodash.startswith';
import { v1 as uuid } from 'uuid'; // XXX Use v1 for uniqueness - See https://www.sohamkamani.com/blog/2016/10/05/uuid1-vs-uuid4/

Expand All @@ -23,18 +25,23 @@ const amplitudeServerClient = init(process.env.NEXT_PUBLIC_AMPLITUDE_API_KEY, {
*
* XXX Do not use it in Next.js pages, only in the API (it's not universal and won't work in the browser!).
*
* XXX It isn't necessary to await the "logEvent()" call, as the event seem to be ingested correctly when not awaiting them.
* But, if events aren't always processed (ingested by Amplitude) as expected, awaiting them might solve the issue.
* See https://github.com/amplitude/Amplitude-Node/issues/123#issuecomment-866278069
*
* @param eventName
* @param userId
* @param props
* @param sendImmediately
*
* @see https://developers.amplitude.com/docs/nodejs
* @see https://www.npmjs.com/package/@amplitude/node
*/
export const logEvent = async (eventName: AMPLITUDE_EVENTS, userId: string, props: GenericObject = {}): Promise<void> => {
export const logEvent = async (eventName: AMPLITUDE_EVENTS, userId: string, props: GenericObject = {}, sendImmediately = true): Promise<void> => {
try {
logger.info(`Logging Amplitude event "${eventName}"${userId ? ` for user "${userId}"` : ''} with properties:`, props);

amplitudeServerClient.logEvent({
const event: Event = {
event_type: eventName,
// Either user_id or device_id must be set, they can't both be empty or "unknown" or "eventsIngested" will be set to 0 in the response
// If the user_id isn't defined, then generate a dynamic/unique id for this event only
Expand All @@ -43,35 +50,39 @@ export const logEvent = async (eventName: AMPLITUDE_EVENTS, userId: string, prop
'customer.ref': process.env.NEXT_PUBLIC_CUSTOMER_REF,
...props,
},
})
};

amplitudeServerClient.logEvent(event)
// .then((res) => logger.info('response', res))
.catch((e) => logger.error(e));

// Send any events that are currently queued for sending.
// Will automatically happen on the next event loop.
// XXX It's necessary to await, or it might not work properly - See https://vercel.com/docs/platform/limits#streaming-responses
const response: Response = await amplitudeServerClient.flush();
if (sendImmediately) {
// Send any events that are currently queued for sending
// Will automatically happen on the next event loop
const response: Response = await amplitudeServerClient.flush();

// Monitor non 2XX response codes to allow for advanced debugging of edge cases
if (!startsWith(response?.statusCode?.toString(10), '2')) {
const message = `Amplitude event didn't return 200 response.`;
logger.error(message, response);
Sentry.withScope((scope) => {
scope.setContext('response', response);
Sentry.captureException(message);
});
} else {
// @ts-ignore
const eventsIngested = response?.body?.eventsIngested;

if (!eventsIngested) {
// See https://github.com/amplitude/Amplitude-Node/issues/123
const message = `Amplitude event wasn't ingested (it was sent, but not stored in Amplitude), expected value ">= 1" and got "${eventsIngested}". This usually happens when both user_id and device_id are invalid, they can't both be empty/undefined or 'unknown'!`;
// Monitor non 2XX response codes to allow for advanced debugging of edge cases
if (!startsWith(response?.statusCode?.toString(10), '2')) {
const message = `Amplitude event didn't return 200 response for event "${eventName}".`;
logger.error(message, response);
Sentry.withScope((scope) => {
scope.setContext('event', event as unknown as Context);
scope.setContext('response', response);
Sentry.captureException(message);
});
} else {
// @ts-ignore
const eventsIngested = response?.body?.eventsIngested;

if (!eventsIngested) {
// See https://github.com/amplitude/Amplitude-Node/issues/123
const message = `Amplitude event wasn't ingested (it was sent, but not stored in Amplitude), expected value "eventsIngested >= 1" and got "eventsIngested=${eventsIngested}". This usually happens when both user_id and device_id are invalid, they can't both be empty/undefined or 'unknown'!`;
logger.error(message, response);
Sentry.withScope((scope) => {
scope.setContext('response', response);
Sentry.captureException(message);
});
}
}
}
} catch (e) {
Expand Down

1 comment on commit 3d15fcc

@github-actions
Copy link

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.