Skip to content
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

[TECH] Récupérer les correlations IDs dans le logger.js (PIX-12823) #9425

Draft
wants to merge 2 commits into
base: dev
Choose a base branch
from
Draft
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
67 changes: 10 additions & 57 deletions api/lib/infrastructure/monitoring-tools.js
Original file line number Diff line number Diff line change
@@ -1,70 +1,22 @@
import async_hooks from 'node:async_hooks';

import Request from '@hapi/hapi/lib/request.js';
import lodash from 'lodash';

import {
getCorrelationContext,
logErrorWithCorrelationIds,
logInfoWithCorrelationIds,
} from '../../src/shared/infrastructure/utils/logger.js';
import * as requestResponseUtils from '../../src/shared/infrastructure/utils/request-response-utils.js';
import { config } from '../config.js';

const { get, set, update, omit } = lodash;
import async_hooks from 'node:async_hooks';

import { logger } from '../../src/shared/infrastructure/utils/logger.js';
import * as requestResponseUtils from '../../src/shared/infrastructure/utils/request-response-utils.js';
const { get, set, update } = lodash;

const { AsyncLocalStorage } = async_hooks;

const asyncLocalStorage = new AsyncLocalStorage();

function getCorrelationContext() {
if (!config.hapi.enableRequestMonitoring) {
return {};
}
const context = asyncLocalStorage.getStore();
const request = get(context, 'request');
return {
user_id: extractUserIdFromRequest(request),
request_id: get(request, 'info.id', '-'),
};
}

function logInfoWithCorrelationIds(data) {
const context = getCorrelationContext();
logger.info(
{
...context,
...omit(data, 'message'),
},
get(data, 'message', '-'),
);
}

/**
* In order to be displayed properly in Datadog,
* the parameter "data" should contain
* - a required property message as string
* - all other properties you need to pass to Datadog
*
* @example
* const data = {
* message: 'Error message',
* context: 'My Context',
* data: { more: 'data', if: 'needed' },
* event: 'Event which trigger this error',
* team: 'My Team',
* };
* monitoringTools.logErrorWithCorrelationIds(data);
*
* @param {object} data
*/
function logErrorWithCorrelationIds(data) {
const context = getCorrelationContext();
logger.error(
{
...context,
...omit(data, 'message'),
},
get(data, 'message', '-'),
);
}

function extractUserIdFromRequest(request) {
let userId = get(request, 'auth.credentials.userId');
if (!userId && get(request, 'headers.authorization')) userId = requestResponseUtils.extractUserIdFromRequest(request);
Expand Down Expand Up @@ -138,6 +90,7 @@ export {
asyncLocalStorage,
extractUserIdFromRequest,
getContext,
getCorrelationContext,
getInContext,
incrementInContext,
installHapiHook,
Expand Down
110 changes: 108 additions & 2 deletions api/src/shared/infrastructure/utils/logger.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import lodash from 'lodash';
import pino from 'pino';
import pretty from 'pino-pretty';

import { asyncLocalStorage } from '../../../prescription/shared/infrastructure/utils/async-local-storage.js';
import { config } from '../../config.js';
import { requestResponseUtils } from './request-response-utils.js';

const { get, omit } = lodash;

const { logging } = config;

Expand All @@ -16,7 +21,7 @@ if (logging.logForHumans) {
});
}

const logger = pino(
const internalLogger = pino(
{
level: logging.logLevel,
redact: ['req.headers.authorization'],
Expand All @@ -25,4 +30,105 @@ const logger = pino(
prettyPrint,
);

export { logger };
function getCorrelationContext() {
if (!config.hapi.enableRequestMonitoring) {
return {};
}
const context = asyncLocalStorage.getStore();
const request = get(context, 'request');
return {
user_id: extractUserIdFromRequest(request),
request_id: get(request, 'info.id', '-'),
};
}

function extractUserIdFromRequest(request) {
let userId = get(request, 'auth.credentials.userId');
if (!userId && get(request, 'headers.authorization')) userId = requestResponseUtils.extractUserIdFromRequest(request);
return userId || '-';
}

function _parseDataForLogger(data) {
return typeof data === 'string' || data instanceof String ? { message: data } : data;
}

function _logWithCorrelationIds(loggingFunction) {
return function (data) {
const parsedData = _parseDataForLogger(data);
const context = getCorrelationContext();
loggingFunction(
{
...context,
...omit(parsedData, 'message'),
},
get(parsedData, 'message', '-'),
);
};
}

/**
* @param {object|string} data
* @see : logger#error for details
* @deprecated use logger#error instead
*/
const logErrorWithCorrelationIds = _logWithCorrelationIds(internalLogger.error.bind(internalLogger));

/**
* @param {object|string} data
* @see : logger#info for details
* @deprecated use logger#info instead
*/
const logInfoWithCorrelationIds = _logWithCorrelationIds(internalLogger.info.bind(internalLogger));

const logger = {
/**
* In order to be displayed properly in Datadog,
* the parameter "data" should contain
* - a required property message as string
* - all other properties you need to pass to Datadog
*
* It is also possible to provide raw message as string.
*
* @example
* const data = {
* message: 'Error message',
* context: 'My Context',
* data: { more: 'data', if: 'needed' },
* event: 'Event which trigger this error',
* team: 'My Team',
* };
* logger.error(data);
* or
* logger.error('Error message');
*
* @param {object|string} data
*/
error: _logWithCorrelationIds(internalLogger.error.bind(internalLogger)),
/**
* @param {object|string} data
* @see : logger#error for details
*/
info: _logWithCorrelationIds(internalLogger.info.bind(internalLogger)),
/**
* @param {object|string} data
* @see : logger#error for details
*/
warn: internalLogger.warn.bind(internalLogger),
/**
* @param {object|string} data
* @see : logger#error for details
*/
trace: internalLogger.trace.bind(internalLogger),
/**
* @param {object|string} data
* @see : logger#error for details
*/
fatal: internalLogger.fatal.bind(internalLogger),
/**
* @param {object|string} data
* @see : logger#error for details
*/
debug: internalLogger.debug.bind(internalLogger),
};

export { getCorrelationContext, logErrorWithCorrelationIds, logger, logInfoWithCorrelationIds };