Skip to content

Provide observability and stack trace on runtime worker errors. #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Release Notes

### v1.1.4

- Provide log line and stack trace on runtime worker errors.

### v1.1.3

- Exported `axios` and `axiosClient` with exponential backoff retry mechanism for HTTP requests and omitting Authorization headers from Axios errors.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@devrev/ts-adaas",
"version": "1.1.3",
"version": "1.1.4",
"description": "Typescript library containing the ADaaS(AirDrop as a Service) control protocol.",
"type": "commonjs",
"main": "./dist/index.js",
Expand Down
4 changes: 2 additions & 2 deletions src/common/control-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
LoaderEvent,
} from '../types/extraction';
import { LoaderEventType } from '../types/loading';
import { formatAxiosError } from '../logger/logger';
import { serializeAxiosError } from '../logger/logger';

export interface EmitInterface {
event: AirdropEvent;
Expand Down Expand Up @@ -49,7 +49,7 @@ export const emit = async ({
if (axios.isAxiosError(error)) {
console.error(
`Failed to emit event with event type ${eventType}.`,
formatAxiosError(error)
serializeAxiosError(error)
);
} else {
// TODO: Stop it through UI or think about retrying this request. Implement exponential retry mechanism.
Expand Down
8 changes: 4 additions & 4 deletions src/common/install-initial-domain-mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { axios, axiosClient } from '../http/axios-client';
import { FunctionInput } from '@devrev/typescript-sdk/dist/snap-ins';

import { InitialDomainMapping } from '../types/common';
import { formatAxiosError } from '../logger/logger';
import { serializeAxiosError } from '../logger/logger';

export async function installInitialDomainMapping(
event: FunctionInput,
Expand Down Expand Up @@ -71,7 +71,7 @@ export async function installInitialDomainMapping(
if (axios.isAxiosError(error)) {
console.error(
'Error while creating recipe blueprint',
formatAxiosError(error)
serializeAxiosError(error)
);
} else {
console.error('Error while creating recipe blueprint', error);
Expand Down Expand Up @@ -109,7 +109,7 @@ export async function installInitialDomainMapping(
if (axios.isAxiosError(error)) {
console.error(
'Error while installing initial domain mapping',
formatAxiosError(error)
serializeAxiosError(error)
);
} else {
console.error('Error while installing initial domain mapping', error);
Expand All @@ -118,7 +118,7 @@ export async function installInitialDomainMapping(
}
} catch (error) {
if (axios.isAxiosError(error)) {
console.error('Error while fetching snap in', formatAxiosError(error));
console.error('Error while fetching snap in', serializeAxiosError(error));
} else {
console.error('Error while fetching snap in', error);
}
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ export { spawn } from './workers/spawn';
export * from './types/workers';

export { formatAxiosError } from './logger/logger';
export { serializeAxiosError } from './logger/logger';
25 changes: 16 additions & 9 deletions src/logger/logger.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AxiosError } from 'axios';
import { getPrintableState, formatAxiosError } from './logger';
import { getPrintableState, serializeAxiosError } from './logger';

it('getPrintableState should return printable state', () => {
const state = {
Expand Down Expand Up @@ -33,7 +33,7 @@ it('getPrintableState should return printable state', () => {
});
});

it('formatAxiosError should return formatted error', () => {
it('serializeAxiosError should return formatted error', () => {
const error = {
response: {
status: 500,
Expand All @@ -44,14 +44,21 @@ it('formatAxiosError should return formatted error', () => {
},
} as AxiosError;

const formattedError = formatAxiosError(error);
const formattedError = serializeAxiosError(error);

expect(formattedError).toEqual({
status: 500,
data: 'Internal server error',
method: 'GET',
baseURL: undefined,
url: undefined,
payload: undefined,
config: {
method: 'GET',
params: undefined,
url: undefined,
},
isAxiosError: true,
isCorsOrNoNetworkError: false,
response: {
data: 'Internal server error',
headers: undefined,
status: 500,
statusText: undefined,
},
});
});
49 changes: 37 additions & 12 deletions src/logger/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from './logger.interfaces';
import { isMainThread, parentPort } from 'node:worker_threads';
import { WorkerAdapterOptions, WorkerMessageSubject } from '../types/workers';
import { AxiosError } from 'axios';
import { AxiosError, RawAxiosResponseHeaders } from 'axios';
import { getCircularReplacer } from '../common/helpers';

export class Logger extends Console {
Expand Down Expand Up @@ -93,18 +93,43 @@ export function getPrintableState(state: Record<string, any>): PrintableState {
// Process the state object directly since it's guaranteed to be an object
return processValue(state) as PrintableState;
}

/**
* @deprecated
*/
export function formatAxiosError(error: AxiosError): object {
if (error.response) {
return {
status: error.response.status,
data: error.response.data,
method: error.config?.method,
baseURL: error.config?.baseURL,
url: error.config?.url,
payload: error.config?.data,
};
return serializeAxiosError(error);
}

export const serializeError = (error: unknown): Error => {
let serializedError = error;
try {
serializedError = JSON.parse(
JSON.stringify(error, Object.getOwnPropertyNames(error))
);
} catch (err) {
console.error('Failed to serialize error object for logger', err);
}
return serializedError as Error;
};

return error;
export function serializeAxiosError(error: AxiosError) {
const response = error.response
? {
data: error.response.data,
headers: error.response.headers as RawAxiosResponseHeaders,
status: error.response.status,
statusText: error.response.statusText,
}
: null;
const config = {
method: error.config?.method,
params: error.config?.params,
url: error.config?.url,
};
return {
config,
isAxiosError: true,
isCorsOrNoNetworkError: !error.response,
response,
};
}
4 changes: 2 additions & 2 deletions src/state/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { axios, axiosClient } from '../http/axios-client';

import { AirdropEvent, SyncMode } from '../types/extraction';
import { STATELESS_EVENT_TYPES } from '../common/constants';
import { formatAxiosError, getPrintableState } from '../logger/logger';
import { serializeAxiosError, getPrintableState } from '../logger/logger';
import { ErrorRecord } from '../types/common';

import { AdapterState, SdkState, StateInterface } from './state.interfaces';
Expand Down Expand Up @@ -101,7 +101,7 @@ export class State<ConnectorState> {
);
} catch (error) {
if (axios.isAxiosError(error)) {
console.error('Failed to update state.', formatAxiosError(error));
console.error('Failed to update state.', serializeAxiosError(error));
} else {
console.error('Failed to update state.', error);
}
Expand Down
12 changes: 6 additions & 6 deletions src/uploader/uploader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
StreamAttachmentsResponse,
UploaderFactoryInterface,
} from './uploader.interfaces';
import { formatAxiosError } from '../logger/logger';
import { serializeAxiosError } from '../logger/logger';

export class Uploader {
private event: AirdropEvent;
Expand Down Expand Up @@ -106,7 +106,7 @@ export class Uploader {
if (axios.isAxiosError(error)) {
console.error(
'Error while preparing artifact.',
formatAxiosError(error)
serializeAxiosError(error)
);
} else {
console.error('Error while preparing artifact.', error);
Expand Down Expand Up @@ -137,7 +137,7 @@ export class Uploader {
if (axios.isAxiosError(error)) {
console.error(
'Error while uploading artifact.',
formatAxiosError(error)
serializeAxiosError(error)
);
} else {
console.error('Error while uploading artifact.', error);
Expand Down Expand Up @@ -171,7 +171,7 @@ export class Uploader {
if (axios.isAxiosError(error)) {
console.error(
'Error while streaming artifact.',
formatAxiosError(error)
serializeAxiosError(error)
);
} else {
console.error('Error while streaming artifact.', error);
Expand Down Expand Up @@ -278,7 +278,7 @@ export class Uploader {
if (axios.isAxiosError(error)) {
console.error(
'Error while downloading artifact from URL.',
formatAxiosError(error)
serializeAxiosError(error)
);
} else {
console.error('Error while downloading artifact from URL.', error);
Expand Down Expand Up @@ -386,7 +386,7 @@ export class Uploader {
if (axios.isAxiosError(error)) {
console.error(
'Error while fetching attachment from URL.',
formatAxiosError(error)
serializeAxiosError(error)
);
} else {
console.error('Error while fetching attachment from URL.', error);
Expand Down
4 changes: 2 additions & 2 deletions src/workers/create-worker.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { isMainThread, Worker } from 'node:worker_threads';

import { WorkerData, WorkerEvent } from '../types/workers';
import { Logger } from '../logger/logger';
import { Logger, serializeError } from '../logger/logger';

async function createWorker<ConnectorState>(
workerData: WorkerData<ConnectorState>
Expand All @@ -19,7 +19,7 @@ async function createWorker<ConnectorState>(
} as WorkerOptions);

worker.on(WorkerEvent.WorkerError, (error) => {
logger.error('Worker error', error);
logger.error('Worker error', serializeError(error));
reject();
});
worker.on(WorkerEvent.WorkerOnline, () => {
Expand Down
8 changes: 4 additions & 4 deletions src/workers/worker-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
import { addReportToLoaderReport, getFilesToLoad } from '../common/helpers';
import { Mappers } from '../mappers/mappers';
import { Uploader } from '../uploader/uploader';
import { formatAxiosError } from '../logger/logger';
import { serializeAxiosError } from '../logger/logger';
import { SyncMapperRecordStatus } from '../mappers/mappers.interface';

export function createWorkerAdapter<ConnectorState>({
Expand Down Expand Up @@ -481,7 +481,7 @@ export class WorkerAdapter<ConnectorState> {
if (axios.isAxiosError(error)) {
console.error(
'Failed to update sync mapper record',
formatAxiosError(error)
serializeAxiosError(error)
);
return {
error: {
Expand Down Expand Up @@ -561,7 +561,7 @@ export class WorkerAdapter<ConnectorState> {
if (axios.isAxiosError(error)) {
console.error(
'Failed to create sync mapper record',
formatAxiosError(error)
serializeAxiosError(error)
);
return {
error: {
Expand Down Expand Up @@ -595,7 +595,7 @@ export class WorkerAdapter<ConnectorState> {
} else {
console.error(
'Failed to get sync mapper record',
formatAxiosError(error)
serializeAxiosError(error)
);
return {
error: {
Expand Down
Loading