Skip to content

Commit

Permalink
feat(ama-sdk): enhance performance plugin to report performance mark (#…
Browse files Browse the repository at this point in the history
…1908)

## Proposed change

Enhance performance plugin
  • Loading branch information
kpanot authored Jun 24, 2024
2 parents 9c161cc + 4b03e9c commit 42841da
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 25 deletions.
99 changes: 76 additions & 23 deletions packages/@ama-sdk/core/src/plugins/perf-metric/perf-metric.fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ export interface Mark {
*/
endTime?: number;
}
/** Performance object supporting NodeJs Performance and Web Performance reporting */
type CrossPlatformPerformance = {
/** @see Performance.mark */
mark: (...x: Parameters<Performance['mark']>) => ReturnType<Performance['mark']> | void;

/** @see Performance.measure */
measure: (measureName: string, startOrMeasureOptions?: string, endMark?: string) => ReturnType<Performance['measure']> | void;
};

/**
* Options for this plugin.
Expand All @@ -48,12 +56,30 @@ export interface PerformanceMetricOptions {
/**
* Callback function to be called when a mark is closed.
*/
onMarkComplete: (mark: Mark) => void;
onMarkComplete: (mark: Mark) => void | Promise<void>;

/**
* Callback function to be called when a mark is closed with an error.
*/
onMarkError: (mark: Mark) => void;
onMarkError: (mark: Mark) => void | Promise<void>;

/**
* Callback function called when a mark is opened.
*/
onMarkOpen: (mark: Mark) => void | Promise<void>;

/**
* Instance of the performance reporter to use for performance measurements.
* @default window.performance on browser only, undefined on node
*/
performance: CrossPlatformPerformance;

/**
* Retrieve the performance tag name
* @param status status of the call
* @param markId Mark ID
*/
getPerformanceTag: (status: string, markId: string) => string;
}

/**
Expand All @@ -63,49 +89,70 @@ export class PerformanceMetricPlugin implements FetchPlugin {
/**
* Callback function called when a mark is closed.
*/
public onMarkComplete?: (mark: Mark) => void;
public onMarkComplete?: (mark: Mark) => void | Promise<void>;

/**
* Callback function called when a mark is closed with an error.
*/
public onMarkError?: (mark: Mark) => void;
public onMarkError?: (mark: Mark) => void | Promise<void>;

/**
* Callback function called when a mark is opened.
*/
public onMarkOpen?: (mark: Mark) => void | Promise<void>;

/**
* Opened marks.
*/
protected openMarks: {[markId: string]: Mark} = {};
protected readonly openMarks: {[markId: string]: Mark} = {};

/**
* Performance reporter to use for performance measurements.
* @default window.performance on browser only, undefined on node
*/
protected readonly performance;

/**
* Method used to get the current time.
* Date.now() is used by default, but we fall back on window.performance.now() if available.
* Method used to get the current time as default implementation if no Performance API available.
* Date.now() is used by default.
*/
protected getTime: () => number = Date.now;

constructor(options?: Partial<PerformanceMetricOptions>) {
this.getPerformanceTag = options?.getPerformanceTag || this.getPerformanceTag;
this.performance = options?.performance || (typeof window !== 'undefined' ? window.performance : undefined);
this.onMarkComplete = options ? options.onMarkComplete : this.onMarkComplete;
this.onMarkError = options ? options.onMarkError : this.onMarkError;

if (typeof window !== 'undefined' && !!window.performance && !!window.performance.now) {
this.getTime = () => window.performance.now();
}
this.onMarkOpen = options ? options.onMarkOpen : this.onMarkOpen;
}

/**
* Retrieve the performance tag name
* @param status status of the call
* @param markId Mark ID
*/
protected getPerformanceTag = (status: string, markId: string) => `sdk:${status}:${markId}`;


/**
* Opens a mark associated to a call.
* @param url URL of the call associated to the mark to open
* @param requestOptions Options of the call associated to the mark to open
*/
public openMark(url: string, requestOptions: RequestInit) {
const markId = v4();
this.openMarks = {
...this.openMarks,
[markId]: {
markId,
url,
requestOptions,
startTime: this.getTime()
}
const perfMark = this.performance?.mark(this.getPerformanceTag('start', markId)) || undefined;
const startTime = perfMark?.startTime ?? this.getTime();
const mark: Mark = {
markId,
url,
requestOptions,
startTime
};
this.openMarks[markId] = mark;
if (this.onMarkOpen) {
void this.onMarkOpen(mark);
}
return markId;
}

Expand All @@ -115,15 +162,18 @@ export class PerformanceMetricPlugin implements FetchPlugin {
* @param response Response of the call associated to the mark to close
*/
public closeMark(markId: string, response: Response) {
const perfMark = this.performance?.mark(this.getPerformanceTag('end', markId)) || undefined;
const endTime = perfMark?.startTime ?? this.getTime();
this.performance?.measure(this.getPerformanceTag('measure', markId), this.getPerformanceTag('start', markId), this.getPerformanceTag('end', markId));
const mark = this.openMarks[markId];
if (!mark) {
return;
}
if (this.onMarkComplete) {
this.onMarkComplete({
void this.onMarkComplete({
...mark,
response,
endTime: this.getTime()
endTime
});
}
delete this.openMarks[markId];
Expand All @@ -135,15 +185,18 @@ export class PerformanceMetricPlugin implements FetchPlugin {
* @param error Optional error of the call associated to the mark to close
*/
public closeMarkWithError(markId: string, error: Error | undefined) {
const perfMark = this.performance?.mark(this.getPerformanceTag('error', markId)) || undefined;
const endTime = perfMark?.startTime ?? this.getTime();
this.performance?.measure(this.getPerformanceTag('measure', markId), this.getPerformanceTag('start', markId), this.getPerformanceTag('error', markId));
const mark = this.openMarks[markId];
if (!mark) {
return;
}
if (this.onMarkError) {
this.onMarkError({
void this.onMarkError({
...mark,
error,
endTime: this.getTime()
endTime
});
}
delete this.openMarks[markId];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import {PerformanceMetricPlugin} from './perf-metric.fetch';
import { PerformanceMetricPlugin } from './perf-metric.fetch';

let perfPlugin: PerformanceMetricPlugin;
describe('PerformanceMetricPlugin', () => {
let onMarkOpen!: jest.Mock;

beforeEach(() => {
perfPlugin = new PerformanceMetricPlugin({});
onMarkOpen = jest.fn();
perfPlugin = new PerformanceMetricPlugin({onMarkOpen});
});

it('should generate new mark ids', () => {
Expand Down Expand Up @@ -47,4 +50,9 @@ describe('PerformanceMetricPlugin', () => {
perfPlugin.closeMarkWithError(markId, {} as Error);
return ret;
});

it('should include call the open mark callback', () => {
const markId = perfPlugin.openMark('my-url', {});
expect(onMarkOpen).toHaveBeenCalledWith(expect.objectContaining({ markId }));
});
});

0 comments on commit 42841da

Please sign in to comment.