Skip to content

Commit

Permalink
feat(sdk): automatically log http calls to API (#3081)
Browse files Browse the repository at this point in the history
## Changes

- Automatically log http calls to API
While doing #3080 I noticed we don't log calls to our own API which can
lead to confusion when you expect something to happen but don't manually
log the result. Thanks to recent change, we now have a way to plug into
axios and just listen to requests.
It will log everything (i.e: triggerAction, getConnection,
getIntegration, etc.), except proxy which doesn't go through
node-client.

NB: This will for sure increase the log amount but just slightly since
we do cache a lot.

![Screenshot 2024-11-28 at 16 11
02](https://github.com/user-attachments/assets/208e2ae2-fa4b-4c8e-97e7-1f924a38f5ce)
  • Loading branch information
bodinsamuel authored Dec 3, 2024
1 parent 7adb8de commit c53fdc6
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@ export declare class NangoAction {
triggerAction<In = unknown, Out = object>(providerConfigKey: string, connectionId: string, actionName: string, input?: In): Promise<Out>;
triggerSync(providerConfigKey: string, connectionId: string, syncName: string, fullResync?: boolean): Promise<void | string>;
private sendLogToPersist;
private logAPICall;
}
export declare class NangoSync extends NangoAction {
lastSyncDate?: Date;
Expand Down
62 changes: 60 additions & 2 deletions packages/shared/lib/sdk/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { validateData } from './dataValidation.js';
import { NangoError } from '../utils/error.js';
import type { DBTeam, GetPublicIntegration, MessageRowInsert, RunnerFlags } from '@nangohq/types';
import { getProvider } from '../services/providers.js';
import { redactHeaders, redactURL } from '../utils/http.js';

const logger = getLogger('SDK');

Expand Down Expand Up @@ -475,6 +476,14 @@ export class NangoAction {
};
}
}
if (!config.axios?.response) {
// Leave the priority to saving response instead of logging
axiosSettings.interceptors = {
response: {
onFulfilled: this.logAPICall.bind(this)
}
};
}

if (config.environmentName) {
this.environmentName = config.environmentName;
Expand Down Expand Up @@ -760,7 +769,15 @@ export class NangoAction {
const level = userDefinedLevel?.level ?? 'info';

if (this.dryRun) {
logger[logLevelToLogger[level] ?? 'info'].apply(null, args as any);
const logLevel = logLevelToLogger[level] ?? 'info';

// TODO: we shouldn't use a floating logger, it should be passed from dryrun or runner
if (args.length > 1 && 'type' in args[1] && args[1].type === 'http') {
logger[logLevel].apply(null, [args[0], { status: args[1]?.response?.code || 'xxx' }] as any);
} else {
logger[logLevel].apply(null, args as any);
}

return;
}

Expand Down Expand Up @@ -909,10 +926,51 @@ export class NangoAction {
}

if (response.status > 299) {
logger.error(`Request to persist API (log) failed: errorCode=${response.status} response='${JSON.stringify(response.data)}'`, this.stringify());
logger.error(
`Request to persist API (log) failed: errorCode=${response.status} response='${JSON.stringify(response.data)}' log=${JSON.stringify(log)}`,
this.stringify()
);
throw new Error(`Failed to log: ${JSON.stringify(response.data)}`);
}
}

private logAPICall(res: AxiosResponse): AxiosResponse {
if (!res.config.url) {
return res;
}

// We compte on the fly because connection's credentials can change during a single run
// We could further optimize this and cache it when the memoizedConnection is updated
const valuesToFilter: string[] = [
...Array.from(this.memoizedConnections.values()).reduce<string[]>((acc, conn) => {
if (!conn) {
return acc;
}
acc.push(...Object.values(conn.connection.credentials));
return acc;
}, []),
this.nango.secretKey
];

const method = res.config.method?.toLocaleUpperCase(); // axios put it in lowercase;
void this.log(
`${method} ${res.config.url}`,
{
type: 'http',
request: {
method: method,
url: redactURL({ url: res.config.url, valuesToFilter }),
headers: redactHeaders({ headers: res.config.headers, valuesToFilter })
},
response: {
code: res.status,
headers: redactHeaders({ headers: res.headers, valuesToFilter })
}
},
{ level: res.status > 299 ? 'error' : 'info' }
);
return res;
}
}

export class NangoSync extends NangoAction {
Expand Down
4 changes: 2 additions & 2 deletions packages/shared/lib/services/proxy.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ class ProxyService {
},
response: {
code: response.status,
headers: response.headers as Record<string, string>
headers: (response.headers || {}) as Record<string, string>
}
}
]
Expand Down Expand Up @@ -574,7 +574,7 @@ class ProxyService {
},
response: {
code: error.response?.status || 500,
headers: error.response?.headers as Record<string, string>
headers: (error.response?.headers || {}) as Record<string, string>
},
error: {
name: error.name,
Expand Down

0 comments on commit c53fdc6

Please sign in to comment.