@unito/integration-sdk
Version:
Integration SDK
66 lines (57 loc) • 2.2 kB
text/typescript
import { Request, Response, NextFunction } from 'express';
import { Error as ApiError } from '@unito/integration-api';
import { default as Logger, Metadata } from '../resources/logger.js';
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Express {
interface Locals {
logger: Logger;
error?: ApiError;
}
}
}
function onFinish(req: Request, res: Response, next: NextFunction) {
res.on('finish', function () {
const logger = res.locals.logger ?? new Logger();
const error = res.locals.error;
const endMetrics = res.locals.requestMetrics.endRequest();
const durationInNs = Number(endMetrics.durationNs);
const durationInMs = (durationInNs / 1_000_000) | 0;
const routePath = (req.route as { path?: string } | undefined)?.path;
const urlCategory = typeof routePath === 'string' ? `${req.baseUrl}${routePath}` : 'unmatched';
const message = `${req.method} ${req.originalUrl} ${res.statusCode} - ${durationInMs} ms`;
const metadata: Metadata = {
duration: durationInNs,
externalApiCount: endMetrics.apiCallCount,
externalApiTotalDuration: endMetrics.totalApiDurationNs,
throttleDelayTotal: endMetrics.totalThrottleDelayNs,
// Use reserved and standard attributes of Datadog
// https://app.datadoghq.com/logs/pipelines/standard-attributes
http: {
method: req.method,
status_code: res.statusCode,
url_category: urlCategory,
url_details: { path: req.originalUrl },
},
...(error
? {
error: {
kind: error.message,
stack: (error.originalError?.details?.stack ?? error.details?.stack) as string,
message: error.originalError?.message ?? error.message,
},
}
: {}),
};
if ([404, 429].includes(res.statusCode)) {
logger.warn(message, metadata);
} else if (res.statusCode >= 400) {
logger.error(message, metadata);
} else if (req.originalUrl !== '/health') {
// Don't log successful health check requests
logger.info(message, metadata);
}
});
next();
}
export default onFinish;