UNPKG

@sap-cloud-sdk/util

Version:

SAP Cloud SDK for JavaScript general utilities

303 lines • 12.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.cloudSdkExceptionLogger = exports.logFormat = void 0; exports.muteLoggers = muteLoggers; exports.unmuteLoggers = unmuteLoggers; exports.disableExceptionLogger = disableExceptionLogger; exports.enableExceptionLogger = enableExceptionLogger; exports.createLogger = createLogger; exports.getLogger = getLogger; exports.setLogLevel = setLogLevel; exports.setGlobalLogLevel = setGlobalLogLevel; exports.getGlobalLogLevel = getGlobalLogLevel; exports.setGlobalTransports = setGlobalTransports; exports.setLogFormat = setLogFormat; exports.setGlobalLogFormat = setGlobalLogFormat; exports.getGlobalLogFormat = getGlobalLogFormat; exports.sanitizeRecord = sanitizeRecord; exports.resetCustomLogLevels = resetCustomLogLevels; exports.resetCustomLogFormats = resetCustomLogFormats; const winston_1 = require("winston"); const format_1 = require("./format"); const loggerReference = 'sap-cloud-sdk-logger'; const exceptionLoggerId = 'sap-cloud-sdk-exception-logger'; const container = new winston_1.Container(); /** * Log formats provided by the util package. */ exports.logFormat = { kibana: format_1.kibana, local: format_1.local }; // Set default format based on NODE_ENV container.options.format = process.env.NODE_ENV === 'production' ? exports.logFormat.kibana : exports.logFormat.local; const exceptionTransport = new winston_1.transports.Console(); const customLogLevels = {}; const customLogFormats = {}; const DEFAULT_LOGGER__MESSAGE_CONTEXT = '__DEFAULT_LOGGER__MESSAGE_CONTEXT'; let silent = false; const moduleLogger = createLogger({ package: 'util', messageContext: 'cloud-sdk-logger' }); function toggleMuteLoggers(silence) { silent = silence; container.loggers.forEach(logger => toggleSilenceTransports(logger, silence)); } function toggleSilenceTransports(logger, silence) { logger.transports.forEach(transport => (transport.silent = silence)); } /** * Mute all logger output created by the SAP Cloud SDK Logger. This also applies to future loggers created. Useful for tests. */ function muteLoggers() { toggleMuteLoggers(true); } /** * Unmute all logger output created by the SAP Cloud SDK Logger. This also applies to future loggers created. Useful for tests. */ function unmuteLoggers() { toggleMuteLoggers(false); } /** * Default logger for the SAP Cloud SDK for unhandled exceptions. */ exports.cloudSdkExceptionLogger = container.get(exceptionLoggerId, { defaultMeta: { logger: loggerReference, test: 'exception' }, format: container.options.format, exceptionHandlers: [exceptionTransport] }); /** * Disable logging of exceptions. Enabled by default. */ function disableExceptionLogger() { exports.cloudSdkExceptionLogger.exceptions.unhandle(); } /** * Enable logging of exceptions. Enabled by default. */ function enableExceptionLogger() { // Flush all possible handlers to make sure there is only one in the end. disableExceptionLogger(); exports.cloudSdkExceptionLogger.exceptions.handle(exceptionTransport); } /** * Create a logger for the given message context, if available. * * Usage: * To create a logger in your module, it is recommended to pass a module identifier that will be logged as `messageContext` for all messages from this logger: * `const logger = createLogger('my-module');`. Not setting any module identifier will retrieve the default logger. * Use this logger throughout your module. If the module is spread over multiple files, you can retrieve the logger instance by calling the `createLogger` function with the respective module identifier. * There will always be only one instance of a logger per module identifier. * You can pass any custom data that you want to be logged in addition by passing an object instead. You can change the default logging level (`INFO`) using the `level` key in the object. * In those cases, provide the `messageContext` as a key in the object: * ``` * const logger = createLogger({ * messageContext: 'my-module', * myCustomKey: 'my-custom-data', * level: 'debug' * }); * ``` * You will find these information under the _custom_fields_ key in your Cloud Foundry logs. * * To retrieve a logger after its creation use {@link getLogger}. * If you want to change the log level of a logger use {@link setLogLevel}. * @param messageContext - Either a key for the message context of all messages produced by the logger or an object with additional keys to set in the message. * @returns A newly created or an already existing logger for the given context. */ function createLogger(messageContext) { const customFields = typeof messageContext === 'string' ? { messageContext } : { ...messageContext }; const logger = container.get(customFields.messageContext, { level: process.env.SAP_CLOUD_SDK_LOG_LEVEL || customLogLevels[customFields.messageContext] || customFields.level || container.options.level || 'info', defaultMeta: { ...(Object.entries(customFields).length && { custom_fields: customFields }), logger: customFields.logger || loggerReference }, format: customLogFormats[customFields.messageContext] || customFields.format || container.options.format || exports.logFormat.local, transports: [new winston_1.transports.Console()] }); toggleSilenceTransports(logger, silent); return logger; } /** * Get logger for a given message context, if available. * @param messageContext - A key for the message context of all messages produced by the logger. * @returns The logger for the given messageContext if it was created before. */ function getLogger(messageContext = DEFAULT_LOGGER__MESSAGE_CONTEXT) { if (container.has(messageContext)) { return container.get(messageContext); } } /** * Change the log level of a logger based on its message context. * e.g., to set the log level for the destination accessor module of the SDK to _debug_, simply call `setLogLevel('debug', 'destination-accessor')`. * @param level - Level to set the logger to. Use an empty string '' as level to unset context level. * @param messageContextOrLogger - Message context of the logger to change the log level for or the logger itself. */ function setLogLevel(level, messageContextOrLogger = DEFAULT_LOGGER__MESSAGE_CONTEXT) { const messageContext = typeof messageContextOrLogger === 'string' ? messageContextOrLogger : getMessageContext(messageContextOrLogger); if (messageContext) { customLogLevels[messageContext] = level; if (container.has(messageContext)) { const logger = container.get(messageContext); logger.level = level; } } else if (typeof messageContextOrLogger !== 'string') { moduleLogger.warn('Setting log level for logger with unknown message context'); messageContextOrLogger.level = level; } } /** * Change the global log level of the container which will set default level for all active loggers. * e.g., to set the global log level call `setGlobalLogLevel('debug')`. * @param level - The log level to set the global log level to. */ function setGlobalLogLevel(level) { container.options.level = level; // Update existing loggers' log level with global level. container.loggers.forEach(logger => { logger.level = level; }); } /** * Get the global log level of the container. * @returns The global log level, or `undefined` when not defined. */ function getGlobalLogLevel() { return container.options.level; } /** * Change the global transport of the container which will set default transport for all active loggers. * e.g., to set the global transport call `setGlobalTransports(httpTransport)`. * @param customTransports - The transport to set the global transport to. Both single transport and an array with multiple transports are supported. */ function setGlobalTransports(customTransports) { container.options.transports = customTransports; container.loggers.forEach(logger => { logger.clear(); return Array.isArray(customTransports) ? customTransports.forEach(transport => logger.add(transport)) : logger.add(customTransports); }); } /** * Change the log format of a logger based on its message context. * e.g., to set the log format for the destination accessor module of the SDK to `local`, simply call `setLogFormat(logFormat.local, 'destination-accessor')`. * @param format - Format to set the logger to. Use `logFormat` to get the pre-defined log formats or use a custom log format. * @param messageContextOrLogger - Message context of the logger to change the log level for or the logger itself. */ function setLogFormat(format, messageContextOrLogger = DEFAULT_LOGGER__MESSAGE_CONTEXT) { const messageContext = typeof messageContextOrLogger === 'string' ? messageContextOrLogger : getMessageContext(messageContextOrLogger); if (messageContext) { customLogFormats[messageContext] = format; if (container.has(messageContext)) { const logger = container.get(messageContext); logger.format = format; } } else if (typeof messageContextOrLogger !== 'string') { moduleLogger.warn('Setting log format for logger with unknown message context'); messageContextOrLogger.format = format; } } /** * Change the global log format of the container which will set default format for all active loggers. * e.g., to set the global log format to `local` call `setGlobalLogLevel(logFormat.local)` or use a custom log format. * @param format - The log format to set the global log format to. */ function setGlobalLogFormat(format) { container.options.format = format; // Update existing loggers' log level with global level. container.loggers.forEach(logger => { logger.format = format; }); } /** * Get the global log format of the container. * @returns The global log format, or `undefined` when not defined. */ function getGlobalLogFormat() { return container.options.format; } const defaultSensitiveKeys = [ 'access_token', 'authentication', 'authorization', 'apiKey', 'credentials', 'csrf', 'xsrf', 'secret', 'password', 'JTENANT', 'JSESSION' ]; /** * Check if the input key contains or matches any of the sensitive keys. * @param inputKey - Key of the record to be sanitized. * @param value - Value corresponding to the inputKey. * @param sensitiveKeys - List of keys to be matched. * @returns A boolean to indicate if the key contains or matches any sensitive key. */ function isSensitive(inputKey, value, sensitiveKeys) { const normalizedKeys = sensitiveKeys.map(key => key.toLowerCase()); // If checking cookie header, it matches the content instead of the key const input = isCookieHeader(inputKey, value) ? value : inputKey; return normalizedKeys.some(normalizedKey => input.toLowerCase().includes(normalizedKey)); } function isCookieHeader(inputKey, value) { return inputKey.toLowerCase() === 'cookie' && typeof value === 'string'; } /** * Potentially sensitive keys will be matched case-insensitive and as substrings. * Matches will be replaced with a placeholder string. * @param input - The record to be sanitized. * @param replacementString - The placeholder string. * @param sensitiveKeys - The list of keys to be replaced. This overrides the default list. * @returns The sanitized copy of the input record. */ function sanitizeRecord(input, replacementString = '<DATA NOT LOGGED TO PREVENT LEAKING SENSITIVE DATA>', sensitiveKeys = defaultSensitiveKeys) { return Object.fromEntries(Object.entries(input).map(([inputKey, value]) => isSensitive(inputKey, value, sensitiveKeys) ? [inputKey, replacementString] : [inputKey, value])); } function getMessageContext(logger) { // This is a workaround for the missing defaultMeta property on the winston logger. const loggerOptions = logger; if (loggerOptions && loggerOptions.defaultMeta && loggerOptions.defaultMeta.custom_fields) { return loggerOptions.defaultMeta.custom_fields.messageContext; } } /** * Reset all the custom log levels for loggers and message context. */ function resetCustomLogLevels() { Object.keys(customLogLevels).forEach(key => delete customLogLevels[key]); } /** * Reset all the custom log formats for loggers and message context. */ function resetCustomLogFormats() { Object.keys(customLogFormats).forEach(key => delete customLogFormats[key]); } //# sourceMappingURL=cloud-sdk-logger.js.map