2024-11-08 05:21:56 -05:00
|
|
|
const Stream = require('node:stream')
|
2019-03-08 11:06:24 -05:00
|
|
|
const bunyan = require('bunyan')
|
2021-10-27 08:06:50 -04:00
|
|
|
const GCPManager = require('./gcp-manager')
|
|
|
|
const SentryManager = require('./sentry-manager')
|
|
|
|
const Serializers = require('./serializers')
|
|
|
|
const {
|
|
|
|
FileLogLevelChecker,
|
2021-12-16 04:04:32 -05:00
|
|
|
GCEMetadataLogLevelChecker,
|
2021-10-27 08:06:50 -04:00
|
|
|
} = require('./log-level-checker')
|
|
|
|
|
|
|
|
const LoggingManager = {
|
2024-06-27 04:10:31 -04:00
|
|
|
/**
|
|
|
|
* @param {string} name - The name of the logger
|
|
|
|
*/
|
2019-03-08 09:09:44 -05:00
|
|
|
initialize(name) {
|
|
|
|
this.isProduction =
|
2020-07-02 05:51:37 -04:00
|
|
|
(process.env.NODE_ENV || '').toLowerCase() === 'production'
|
2019-03-08 09:09:44 -05:00
|
|
|
this.defaultLevel =
|
2022-05-16 08:38:27 -04:00
|
|
|
process.env.LOG_LEVEL || (this.isProduction ? 'info' : 'debug')
|
2019-03-08 11:06:24 -05:00
|
|
|
this.loggerName = name
|
2019-03-08 09:09:44 -05:00
|
|
|
this.logger = bunyan.createLogger({
|
|
|
|
name,
|
2019-07-11 06:11:09 -04:00
|
|
|
serializers: {
|
2021-10-27 08:06:50 -04:00
|
|
|
err: Serializers.err,
|
2023-08-24 07:26:10 -04:00
|
|
|
error: Serializers.err,
|
2021-10-27 08:06:50 -04:00
|
|
|
req: Serializers.req,
|
2021-12-16 04:04:32 -05:00
|
|
|
res: Serializers.res,
|
2019-07-11 06:11:09 -04:00
|
|
|
},
|
2021-12-16 04:04:32 -05:00
|
|
|
streams: [this._getOutputStreamConfig()],
|
2019-03-08 11:06:24 -05:00
|
|
|
})
|
2019-10-24 16:41:46 -04:00
|
|
|
this._setupRingBuffer()
|
|
|
|
this._setupLogLevelChecker()
|
2019-03-08 11:06:24 -05:00
|
|
|
return this
|
2019-03-08 09:09:44 -05:00
|
|
|
},
|
|
|
|
|
2021-09-22 06:25:28 -04:00
|
|
|
initializeErrorReporting(dsn, options) {
|
2021-10-27 08:06:50 -04:00
|
|
|
this.sentryManager = new SentryManager()
|
2019-03-08 09:09:44 -05:00
|
|
|
},
|
|
|
|
|
2024-06-27 04:10:31 -04:00
|
|
|
/**
|
|
|
|
* @param {Record<string, any>|string} attributes - Attributes to log (nice serialization for err, req, res)
|
|
|
|
* @param {string} [message] - Optional message
|
|
|
|
* @signature `debug(attributes, message)`
|
|
|
|
* @signature `debug(message)`
|
|
|
|
*/
|
|
|
|
debug(attributes, message, ...args) {
|
|
|
|
return this.logger.debug(attributes, message, ...args)
|
2019-03-08 09:09:44 -05:00
|
|
|
},
|
|
|
|
|
2024-06-27 04:10:31 -04:00
|
|
|
/**
|
|
|
|
* @param {Record<string, any>|string} attributes - Attributes to log (nice serialization for err, req, res)
|
|
|
|
* @param {string} [message]
|
|
|
|
* @signature `info(attributes, message)`
|
|
|
|
* @signature `info(message)`
|
|
|
|
*/
|
|
|
|
info(attributes, message, ...args) {
|
|
|
|
return this.logger.info(attributes, message, ...args)
|
2019-03-08 09:09:44 -05:00
|
|
|
},
|
|
|
|
|
2024-06-27 04:10:31 -04:00
|
|
|
/**
|
|
|
|
* @param {Record<string, any>} attributes - Attributes to log (nice serialization for err, req, res)
|
|
|
|
* @param {string} [message]
|
|
|
|
*/
|
2019-03-08 09:09:44 -05:00
|
|
|
error(attributes, message, ...args) {
|
2019-03-12 08:23:37 -04:00
|
|
|
if (this.ringBuffer !== null && Array.isArray(this.ringBuffer.records)) {
|
2020-07-02 06:01:23 -04:00
|
|
|
attributes.logBuffer = this.ringBuffer.records.filter(function (record) {
|
2019-03-12 08:23:37 -04:00
|
|
|
return record.level !== 50
|
|
|
|
})
|
2019-03-08 09:09:44 -05:00
|
|
|
}
|
2019-03-08 11:06:24 -05:00
|
|
|
this.logger.error(attributes, message, ...Array.from(args))
|
2021-10-27 08:06:50 -04:00
|
|
|
if (this.sentryManager) {
|
|
|
|
this.sentryManager.captureExceptionRateLimited(attributes, message)
|
2019-03-08 09:09:44 -05:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2024-06-27 04:10:31 -04:00
|
|
|
/**
|
|
|
|
* Alias to the error method.
|
|
|
|
* @param {Record<string, any>} attributes - Attributes to log (nice serialization for err, req, res)
|
|
|
|
* @param {string} [message]
|
|
|
|
*/
|
|
|
|
err(attributes, message, ...args) {
|
|
|
|
return this.error(attributes, message, ...args)
|
2019-03-08 09:09:44 -05:00
|
|
|
},
|
|
|
|
|
2024-06-27 04:10:31 -04:00
|
|
|
/**
|
|
|
|
* @param {Record<string, any>|string} attributes - Attributes to log (nice serialization for err, req, res)
|
|
|
|
* @param {string} [message]
|
|
|
|
* @signature `warn(attributes, message)`
|
|
|
|
* @signature `warn(message)`
|
|
|
|
*/
|
|
|
|
warn(attributes, message, ...args) {
|
|
|
|
return this.logger.warn(attributes, message, ...args)
|
2019-03-08 09:09:44 -05:00
|
|
|
},
|
|
|
|
|
2024-06-27 04:10:31 -04:00
|
|
|
/**
|
|
|
|
* @param {Record<string, any>} attributes - Attributes to log (nice serialization for err, req, res)
|
|
|
|
* @param {string} [message]
|
|
|
|
*/
|
2021-09-22 06:25:28 -04:00
|
|
|
fatal(attributes, message) {
|
2019-03-08 11:06:24 -05:00
|
|
|
this.logger.fatal(attributes, message)
|
2021-10-27 08:06:50 -04:00
|
|
|
if (this.sentryManager) {
|
|
|
|
this.sentryManager.captureException(attributes, message, 'fatal')
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_getOutputStreamConfig() {
|
2022-03-01 10:09:36 -05:00
|
|
|
switch (process.env.LOGGING_FORMAT) {
|
|
|
|
case 'gke': {
|
|
|
|
const stream = new Stream.Writable({
|
|
|
|
objectMode: true,
|
|
|
|
write(entry, encoding, callback) {
|
|
|
|
const gcpEntry = GCPManager.convertLogEntry(entry)
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
console.log(JSON.stringify(gcpEntry, bunyan.safeCycles()))
|
|
|
|
setImmediate(callback)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
return { level: this.defaultLevel, type: 'raw', stream }
|
|
|
|
}
|
|
|
|
case 'gce': {
|
|
|
|
const { LoggingBunyan } = require('@google-cloud/logging-bunyan')
|
|
|
|
return new LoggingBunyan({
|
|
|
|
logName: this.loggerName,
|
|
|
|
serviceContext: { service: this.loggerName },
|
|
|
|
}).stream(this.defaultLevel)
|
|
|
|
}
|
|
|
|
default: {
|
|
|
|
return { level: this.defaultLevel, stream: process.stdout }
|
|
|
|
}
|
2019-03-08 09:09:44 -05:00
|
|
|
}
|
2019-10-24 16:41:46 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
_setupRingBuffer() {
|
2020-07-02 05:51:37 -04:00
|
|
|
this.ringBufferSize = parseInt(process.env.LOG_RING_BUFFER_SIZE) || 0
|
2019-10-24 16:41:46 -04:00
|
|
|
if (this.ringBufferSize > 0) {
|
|
|
|
this.ringBuffer = new bunyan.RingBuffer({ limit: this.ringBufferSize })
|
|
|
|
this.logger.addStream({
|
|
|
|
level: 'trace',
|
|
|
|
type: 'raw',
|
2021-12-16 04:04:32 -05:00
|
|
|
stream: this.ringBuffer,
|
2019-10-24 16:41:46 -04:00
|
|
|
})
|
|
|
|
} else {
|
|
|
|
this.ringBuffer = null
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2021-10-27 08:06:50 -04:00
|
|
|
_setupLogLevelChecker() {
|
|
|
|
const logLevelSource = (
|
|
|
|
process.env.LOG_LEVEL_SOURCE || 'file'
|
|
|
|
).toLowerCase()
|
|
|
|
|
|
|
|
if (this.logLevelChecker) {
|
|
|
|
this.logLevelChecker.stop()
|
|
|
|
this.logLevelChecker = null
|
2019-10-24 16:41:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (this.isProduction) {
|
2021-10-27 08:06:50 -04:00
|
|
|
switch (logLevelSource) {
|
|
|
|
case 'file':
|
2022-02-16 09:04:44 -05:00
|
|
|
this.logLevelChecker = new FileLogLevelChecker(
|
|
|
|
this.logger,
|
|
|
|
this.defaultLevel
|
|
|
|
)
|
2021-10-27 08:06:50 -04:00
|
|
|
break
|
|
|
|
case 'gce_metadata':
|
2022-02-16 09:04:44 -05:00
|
|
|
this.logLevelChecker = new GCEMetadataLogLevelChecker(
|
|
|
|
this.logger,
|
|
|
|
this.defaultLevel
|
|
|
|
)
|
2021-10-27 08:06:50 -04:00
|
|
|
break
|
|
|
|
case 'none':
|
|
|
|
break
|
|
|
|
default:
|
2021-12-16 04:04:32 -05:00
|
|
|
// eslint-disable-next-line no-console
|
2021-10-27 08:06:50 -04:00
|
|
|
console.log(`Unrecognised log level source: ${logLevelSource}`)
|
2019-10-24 16:41:46 -04:00
|
|
|
}
|
2021-10-27 08:06:50 -04:00
|
|
|
if (this.logLevelChecker) {
|
|
|
|
this.logLevelChecker.start()
|
2020-07-24 06:59:23 -04:00
|
|
|
}
|
2019-10-24 16:41:46 -04:00
|
|
|
}
|
2021-12-16 04:04:32 -05:00
|
|
|
},
|
2021-10-27 08:06:50 -04:00
|
|
|
}
|
|
|
|
|
2024-02-06 04:34:15 -05:00
|
|
|
LoggingManager.initialize('default')
|
2019-03-08 09:09:44 -05:00
|
|
|
|
2023-08-24 07:26:26 -04:00
|
|
|
function handleWarning(err) {
|
|
|
|
LoggingManager.warn({ err }, 'Warning details')
|
|
|
|
}
|
|
|
|
|
|
|
|
process.on('warning', handleWarning)
|
|
|
|
LoggingManager.removeWarningHandler = () => {
|
|
|
|
process.off('warning', handleWarning)
|
|
|
|
}
|
|
|
|
|
2021-10-27 08:06:50 -04:00
|
|
|
module.exports = LoggingManager
|