Skip to content

Commit

Permalink
feat(api): add correlationIds to logger#info & logger#error
Browse files Browse the repository at this point in the history
  • Loading branch information
aurelie-crouillebois authored and dlahaye committed Jul 4, 2024
1 parent b8a9039 commit 9ec29d1
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 59 deletions.
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
93 changes: 91 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,88 @@ 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 _logErrorWithCorrelationIds(logger) {
return function (data) {
const parsedData = parseDataForLogger(data);
const context = getCorrelationContext();
logger.error(
{
...context,
...omit(parsedData, 'message'),
},
get(parsedData, 'message', '-'),
);
};
}

function _logInfoWithCorrelationIds(logger) {
return function (data) {
const parsedData = parseDataForLogger(data);
const context = getCorrelationContext();
logger.info(
{
...context,
...omit(parsedData, 'message'),
},
get(parsedData, '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',
* };
* logErrorWithCorrelationIds(data);
*
* @param {object} data
*/
const logErrorWithCorrelationIds = _logErrorWithCorrelationIds(internalLogger);
const logInfoWithCorrelationIds = _logInfoWithCorrelationIds(internalLogger);

const logger = {
error: logErrorWithCorrelationIds,
info: logInfoWithCorrelationIds,
warn: internalLogger.warn.bind(internalLogger),
trace: internalLogger.trace.bind(internalLogger),
fatal: internalLogger.fatal.bind(internalLogger),
debug: internalLogger.debug.bind(internalLogger),
};

export { getCorrelationContext, logErrorWithCorrelationIds, logger, logInfoWithCorrelationIds };

0 comments on commit 9ec29d1

Please sign in to comment.