@earnaha/auth0-action-helper
Version:
AHA auth0 action helper
246 lines (213 loc) • 5.2 kB
JavaScript
const createEcsPinoOptions = require('@elastic/ecs-pino-format');
const Sentry = require('@sentry/node');
const pino = require('pino');
const pinoOpenSearch = require('pino-opensearch');
const pretty = require('pino-pretty');
const DEFAULT_CONFIG = Object.freeze({
SENTRY_TRACES_SAMPLE_RATE: 0.05,
LOWEST_LOG_LEVEL: 'trace',
LOG_LEVEL: 'info',
});
class LogHelper {
constructor(
NODE_ENV,
APP,
options = {
CONSOLE: false,
},
) {
if (!NODE_ENV || !APP) {
throw new Error('NODE_ENV and APP are required');
}
this._DEBUG_LEVEL = DEFAULT_CONFIG.LOG_LEVEL;
this._NODE_ENV = NODE_ENV;
this._APP = APP;
this._OPTIONS = options;
this.logger = null;
}
setOpenSearchNode(node) {
this._OPEN_SEARCH_NODE = node;
if (this.logger) {
this.initialize();
}
return this;
}
setSentryDsn(dsn) {
this._SENTRY_DSN = dsn;
if (this.logger) {
this.initialize();
}
return this;
}
setSentryTracesSampleRate(rate) {
this._SENTRY_TRACES_SAMPLE_RATE = rate;
if (this.logger) {
this.initialize();
}
return this;
}
setMeta(meta = {}) {
if (typeof meta === 'object') {
this._META = { ...this._META, ...meta };
if (this.logger) {
this.initialize();
}
}
return this;
}
setLogLevel(level) {
if (level) {
if (Object.keys(pino.levels.values).includes(level)) {
this._DEBUG_LEVEL = level;
if (this.logger) {
this.logger.level = level;
}
return this;
}
throw new Error('Invalid log level');
}
return this;
}
initialize() {
const pinoStreams = [];
const {
_APP: APP,
_NODE_ENV: NODE_ENV,
_OPEN_SEARCH_NODE: OPEN_SEARCH_NODE,
} = this;
const isTest = this._getIsTestEnv();
let pinoConfig = {
formatters: {
log: obj => ({
data: obj,
meta: this._formatMeta(),
msg: obj.message || obj.msg,
}),
},
};
if (isTest || (this._OPTIONS && this._OPTIONS.CONSOLE)) {
// In order for multistream to work, the log level must be set to the lowest level used in the streams array.
pinoStreams.push({
level: 'trace',
stream: this._getStreamToConsole(),
});
}
if (OPEN_SEARCH_NODE) {
if (typeof OPEN_SEARCH_NODE !== 'string')
throw new Error('OPEN_SEARCH_NODE must be a string');
if (!OPEN_SEARCH_NODE.startsWith('https'))
throw new Error('OPEN_SEARCH_NODE must start with https');
}
if (
!isTest &&
OPEN_SEARCH_NODE &&
typeof OPEN_SEARCH_NODE === 'string' &&
OPEN_SEARCH_NODE.startsWith('https')
) {
const that = this;
const escFormatters = createEcsPinoOptions();
pinoConfig = {
timestamp: () => `,"@timestamp":"${new Date().toISOString()}"`,
formatters: {
...escFormatters.formatters,
log(obj) {
const pinoPayload = escFormatters.formatters.log(obj);
return {
data: pinoPayload,
meta: that._formatMeta(),
msg: obj.message || obj.msg,
};
},
},
};
pinoStreams.push({
level: 'trace',
stream: this._getStreamToOpenSearch(),
});
}
this.logger = pino(
{
...pinoConfig,
level: isTest
? DEFAULT_CONFIG.LOWEST_LOG_LEVEL
: this._DEBUG_LEVEL,
},
pino.multistream(pinoStreams),
);
this.logger.streams = pinoStreams;
this.logger.NODE_ENV = NODE_ENV;
this.logger.APP = APP;
return this;
}
_getIsTestEnv() {
return this._NODE_ENV === 'test';
}
_getStreamToConsole() {
return pretty({
sync: true,
singleLine: true,
levelFirst: true,
minimumLevel: DEFAULT_CONFIG.LOWEST_LOG_LEVEL,
});
}
_getStreamToOpenSearch(isTest) {
const {
_OPEN_SEARCH_NODE: OPEN_SEARCH_NODE,
_SENTRY_DSN: SENTRY_DSN,
_SENTRY_TRACES_SAMPLE_RATE: SENTRY_TRACES_SAMPLE_RATE,
} = this;
const streamToOpenSearch = pinoOpenSearch({
node: OPEN_SEARCH_NODE,
consistency: 'one',
'es-version': 7,
index(logTime) {
return `logs-${logTime.substring(0, 10).replace(/-/g, '.')}`;
},
});
if (SENTRY_DSN && !isTest) {
Sentry.init({
dsn: SENTRY_DSN,
tracesSampleRate:
(typeof SENTRY_TRACES_SAMPLE_RATE === 'string'
? parseFloat(SENTRY_TRACES_SAMPLE_RATE)
: SENTRY_TRACES_SAMPLE_RATE) ||
DEFAULT_CONFIG.SENTRY_TRACES_SAMPLE_RATE,
});
}
streamToOpenSearch.on('unknown', (line, error) => {
// eslint-disable-next-line no-console
console.error(
'streamToOpenSearch: Expect to see a lot of these with Pino Pretty turned on.',
line,
error,
);
Sentry.captureException(error);
});
streamToOpenSearch.on('insertError', error => {
// eslint-disable-next-line no-console
console.error(
`streamToOpenSearch: An error occurred insert document:`,
error,
);
Sentry.captureException(error);
});
streamToOpenSearch.on('error', error => {
// eslint-disable-next-line no-console
console.error(`streamToOpenSearch: An error occurred :`, error);
Sentry.captureException(error);
});
return streamToOpenSearch;
}
_formatMeta() {
const { _APP: APP, _META: META, _NODE_ENV: NODE_ENV } = this;
return {
...META,
app: APP,
env: NODE_ENV,
service: `aha${
(NODE_ENV || '').startsWith('prod') ? '' : `-${NODE_ENV}`
}`,
};
}
}
module.exports = LogHelper;