🤔 Как не терять requestId в логах не пробрасывая его через параметры методов?
Классическая проблема NodeJS разработки: логи не читаемы. При дебаге когда пользователь один, разобрать что происходит ещё можно. Но после запуска в прод в логах сборная солянка из запросов и восстановить трейс вызова композиции методов нельзя
import { scoped } from "di-scoped";
export interface IRequestContext {
userId: string;
requestId: string;
serviceName: "mobile" | "desktop";
version: number;
}
export const RequestContextService = scoped(
class {
constructor(readonly context: IRequestContext) {}
}
);
export type TRequestContextService = InstanceType<
typeof RequestContextService
>;
export default RequestContextService;
Согласно MVC, делается два слоя: View и Controller. На уровне view кладем requestId в контекст исполнения через RequestContextService.runInContext
import { inject } from "di-kit";
export class SocketViewService {
readonly loggerService = inject<LoggerService>(TYPES.loggerService);
readonly socketControllerService = inject<SocketControllerService>(TYPES.socketControllerService);
public sendNewOrder = async (
request: TRequest<{ room: string; orderId: string }>,
): Promise<TResponse<SendNewOrder>> => {
this.loggerService.log("socketViewService sendNewOrder", { request });
try {
const data = await RequestContextService.runInContext(
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
async () =>
await this.socketControllerService.sendNewOrder(request.data),
request,
);
return { status: "ok", serviceName: request.serviceName, version: request.version, userId: request.userId, requestId: request.requestId, data };
} catch (error: any) {
this.loggerService.log("socketViewService sendNewOrder error", { request, error: errorData(error) });
return { status: "error", error: VerificationError.getErrorMessage(error), errorCode: VerificationError.getErrorCode(error), serviceName: request.serviceName, version: request.version, userId: request.userId, requestId: request.requestId };
}
};
}![]()
Voila! Во всех вложенных сервисах к записи в лог будет приложен идентификатор requestId. Дополнительно, этот код позволяет сериализовать ошибки так, чтобы передавать их по шине gRPC
import { inject } from "di-kit";
import { createLogger } from 'pinolog';
const logger = createLogger("socket.log");
export class LoggerService {
readonly requestContextService = inject<TRequestContextService>(TYPES.requestContextService);
private get context() {
if (RequestContextService.hasContext()) {
// в лог - только конверт IRequestContext; scoped-контекст. но дебаггеру виден весь запрос
const { serviceName, version, userId, requestId } = this.contextService.context;
return { serviceName, version, userId, requestId };
}
return {};
}
public log = (topic: string, ...args: any[]) => {
logger.log(topic, ...args, this.context);
}
}