loglayer
Version:
A modern logging library with a fluent API for specifying log messages, metadata and errors
1,523 lines • 59.9 kB
JavaScript
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
let _loglayer_plugin = require("@loglayer/plugin");
let _loglayer_shared = require("@loglayer/shared");
let _loglayer_context_manager = require("@loglayer/context-manager");
let _loglayer_log_level_manager = require("@loglayer/log-level-manager");
let _loglayer_transport = require("@loglayer/transport");
//#region src/PluginManager.ts
const CALLBACK_LIST = [
_loglayer_plugin.PluginCallbackType.onBeforeDataOut,
_loglayer_plugin.PluginCallbackType.onMetadataCalled,
_loglayer_plugin.PluginCallbackType.onBeforeMessageOut,
_loglayer_plugin.PluginCallbackType.transformLogLevel,
_loglayer_plugin.PluginCallbackType.shouldSendToLogger,
_loglayer_plugin.PluginCallbackType.onContextCalled
];
/**
* A class that manages plugins and runs their callbacks.
* Used by LogLayer to run plugins at various stages of the logging process.
*/
var PluginManager = class {
idToPlugin;
transformLogLevel = [];
onBeforeDataOut = [];
shouldSendToLogger = [];
onMetadataCalled = [];
onBeforeMessageOut = [];
onContextCalled = [];
constructor(plugins) {
this.idToPlugin = {};
this.mapPlugins(plugins);
this.indexPlugins();
}
mapPlugins(plugins) {
for (const plugin of plugins) {
if (!plugin.id) plugin.id = Date.now().toString() + Math.random().toString();
if (this.idToPlugin[plugin.id]) throw new Error(`[LogLayer] Plugin with id ${plugin.id} already exists.`);
plugin["registeredAt"] = Date.now();
this.idToPlugin[plugin.id] = plugin;
}
}
indexPlugins() {
this.transformLogLevel = [];
this.onBeforeDataOut = [];
this.shouldSendToLogger = [];
this.onMetadataCalled = [];
this.onBeforeMessageOut = [];
this.onContextCalled = [];
const pluginList = Object.values(this.idToPlugin).sort((a, b) => a.registeredAt - b.registeredAt);
for (const plugin of pluginList) {
if (plugin.disabled) return;
for (const callback of CALLBACK_LIST) if (plugin[callback] && plugin.id) this[callback].push(plugin.id);
}
}
hasPlugins(callbackType) {
return this[callbackType].length > 0;
}
countPlugins(callbackType) {
if (callbackType) return this[callbackType].length;
return Object.keys(this.idToPlugin).length;
}
addPlugins(plugins) {
this.mapPlugins(plugins);
this.indexPlugins();
}
enablePlugin(id) {
const plugin = this.idToPlugin[id];
if (plugin) plugin.disabled = false;
this.indexPlugins();
}
disablePlugin(id) {
const plugin = this.idToPlugin[id];
if (plugin) plugin.disabled = true;
this.indexPlugins();
}
removePlugin(id) {
delete this.idToPlugin[id];
this.indexPlugins();
}
/**
* Runs plugins that define transformLogLevel. Returns the transformed log level or the original if no transformation is applied.
* If multiple plugins transform the log level, the last one wins.
*/
runTransformLogLevel(params, loglayer) {
let transformedLevel = null;
for (const pluginId of this.transformLogLevel) {
const plugin = this.idToPlugin[pluginId];
if (plugin.transformLogLevel) {
const result = plugin.transformLogLevel({
data: params.data,
logLevel: params.logLevel,
messages: params.messages,
error: params.error,
metadata: params.metadata,
context: params.context,
groups: params.groups,
schema: params.schema,
prefix: params.prefix
}, loglayer);
if (result !== null && result !== void 0 && result !== false) transformedLevel = result;
}
}
return transformedLevel !== null && transformedLevel !== void 0 && transformedLevel !== false ? transformedLevel : params.logLevel;
}
/**
* Runs plugins that defines onBeforeDataOut.
*/
runOnBeforeDataOut(params, loglayer) {
const initialData = { ...params };
const originalData = initialData.data;
for (const pluginId of this.onBeforeDataOut) {
const plugin = this.idToPlugin[pluginId];
if (plugin.onBeforeDataOut) {
const result = plugin.onBeforeDataOut({
data: initialData.data,
logLevel: initialData.logLevel,
error: initialData.error,
metadata: initialData.metadata,
context: initialData.context,
groups: initialData.groups,
schema: initialData.schema,
prefix: initialData.prefix
}, loglayer);
if (result) {
if (!initialData.data) initialData.data = {};
if (result !== originalData) Object.assign(initialData.data, result);
}
}
}
return initialData.data;
}
/**
* Runs plugins that define shouldSendToLogger. Any plugin that returns false will prevent the message from being sent to the transport.
*/
runShouldSendToLogger(params, loglayer) {
return !this.shouldSendToLogger.some((pluginId) => {
return !this.idToPlugin[pluginId].shouldSendToLogger?.(params, loglayer);
});
}
/**
* Runs plugins that define onMetadataCalled.
*/
runOnMetadataCalled(metadata, loglayer) {
let data = { ...metadata };
for (const pluginId of this.onMetadataCalled) {
const result = this.idToPlugin[pluginId].onMetadataCalled?.(data, loglayer);
if (result) data = result;
else return null;
}
return data;
}
runOnBeforeMessageOut(params, loglayer) {
let messages = [...params.messages];
for (const pluginId of this.onBeforeMessageOut) {
const result = this.idToPlugin[pluginId].onBeforeMessageOut?.({
messages,
logLevel: params.logLevel,
groups: params.groups,
schema: params.schema,
prefix: params.prefix
}, loglayer);
if (result) messages = result;
}
return messages;
}
/**
* Runs plugins that define onContextCalled.
*/
runOnContextCalled(context, loglayer) {
let data = { ...context };
for (const pluginId of this.onContextCalled) {
const result = this.idToPlugin[pluginId].onContextCalled?.(data, loglayer);
if (result) data = result;
else return null;
}
return data;
}
};
//#endregion
//#region src/LogLayer.ts
/**
* Wraps around a logging framework to provide convenience methods that allow
* developers to programmatically specify their errors and metadata along with
* a message in a consistent fashion.
*/
var LogLayer = class LogLayer {
pluginManager;
idToTransport;
hasMultipleTransports;
singleTransport;
contextManager;
logLevelManager;
_isLoggingLazyError = false;
_lazyContextCount = 0;
_assignedGroups = null;
_groupsConfig = null;
_activeGroups = null;
_ungroupedBehavior = "all";
/**
* The configuration object used to initialize the logger.
* This is for internal use only and should not be modified directly.
*/
_config;
constructor(config) {
this._config = {
...config,
enabled: config.enabled ?? true
};
this.contextManager = new _loglayer_context_manager.DefaultContextManager();
this.logLevelManager = new _loglayer_log_level_manager.DefaultLogLevelManager();
if (!this._config.enabled) this.disableLogging();
const plugins = [...config.plugins || [], ...mixinRegistry.pluginsToInit];
this.pluginManager = new PluginManager(plugins);
if (!this._config.errorFieldName) this._config.errorFieldName = "err";
if (!this._config.copyMsgOnOnlyError) this._config.copyMsgOnOnlyError = false;
this._initializeTransports(this._config.transport);
this._groupsConfig = config.groups ? { ...config.groups } : null;
this._ungroupedBehavior = config.ungroupedBehavior ?? "all";
this._activeGroups = config.activeGroups ? new Set(config.activeGroups) : null;
this._parseEnvGroups();
if (mixinRegistry.logLayerHandlers.length > 0) mixinRegistry.logLayerHandlers.forEach((mixin) => {
if (mixin.onConstruct) mixin.onConstruct(this, config);
});
}
/**
* Sets the context manager to use for managing context data.
*/
withContextManager(contextManager) {
if (this.contextManager && typeof this.contextManager[Symbol.dispose] === "function") this.contextManager[Symbol.dispose]();
this.contextManager = contextManager;
this._lazyContextCount = contextManager.hasContextData() ? (0, _loglayer_shared.countLazyValues)(contextManager.getContext()) : 0;
return this;
}
/**
* Returns the context manager instance being used.
*/
getContextManager() {
return this.contextManager;
}
/**
* Sets the log level manager to use for managing log levels.
*/
withLogLevelManager(logLevelManager) {
if (this.logLevelManager && typeof this.logLevelManager[Symbol.dispose] === "function") this.logLevelManager[Symbol.dispose]();
this.logLevelManager = logLevelManager;
return this;
}
/**
* Returns the log level manager instance being used.
*/
getLogLevelManager() {
return this.logLevelManager;
}
/**
* Returns the configuration object used to initialize the logger.
*/
getConfig() {
return this._config;
}
_initializeTransports(transports) {
if (this.idToTransport) {
for (const id in this.idToTransport) if (this.idToTransport[id] && typeof this.idToTransport[id][Symbol.dispose] === "function") this.idToTransport[id][Symbol.dispose]();
}
this.hasMultipleTransports = Array.isArray(transports) && transports.length > 1;
this.singleTransport = this.hasMultipleTransports ? null : Array.isArray(transports) ? transports[0] : transports;
if (Array.isArray(transports)) this.idToTransport = transports.reduce((acc, transport) => {
acc[transport.id] = transport;
return acc;
}, {});
else this.idToTransport = { [transports.id]: transports };
}
/**
* Calls child() and sets the prefix to be included with every log message.
*
* @see {@link https://loglayer.dev/logging-api/basic-logging.html#message-prefixing | Message Prefixing Docs}
*/
withPrefix(prefix) {
const logger = this.child();
logger._config.prefix = prefix;
return logger;
}
/**
* Creates a child logger with the specified group(s) persistently assigned.
* All logs from the child will be tagged with these groups.
*
* @see {@link https://loglayer.dev/logging-api/groups.html | Groups Docs}
*/
withGroup(group) {
const logger = this.child();
const newGroups = Array.isArray(group) ? group : [group];
if (logger._assignedGroups) {
const combined = new Set([...logger._assignedGroups, ...newGroups]);
logger._assignedGroups = Array.from(combined);
} else logger._assignedGroups = [...newGroups];
return logger;
}
/**
* Adds a new group definition at runtime.
*
* @see {@link https://loglayer.dev/logging-api/groups.html | Groups Docs}
*/
addGroup(name, config) {
if (!this._groupsConfig) this._groupsConfig = {};
this._groupsConfig[name] = { ...config };
return this;
}
/**
* Removes a group definition at runtime.
*
* @see {@link https://loglayer.dev/logging-api/groups.html | Groups Docs}
*/
removeGroup(name) {
if (this._groupsConfig) {
delete this._groupsConfig[name];
if (Object.keys(this._groupsConfig).length === 0) this._groupsConfig = null;
}
return this;
}
/**
* Enables a group by name (sets enabled: true).
*
* @see {@link https://loglayer.dev/logging-api/groups.html | Groups Docs}
*/
enableGroup(name) {
if (this._groupsConfig?.[name]) this._groupsConfig[name] = {
...this._groupsConfig[name],
enabled: true
};
return this;
}
/**
* Disables a group by name (sets enabled: false).
*
* @see {@link https://loglayer.dev/logging-api/groups.html | Groups Docs}
*/
disableGroup(name) {
if (this._groupsConfig?.[name]) this._groupsConfig[name] = {
...this._groupsConfig[name],
enabled: false
};
return this;
}
/**
* Sets the minimum log level for a group at runtime.
*
* @see {@link https://loglayer.dev/logging-api/groups.html | Groups Docs}
*/
setGroupLevel(name, level) {
if (this._groupsConfig?.[name]) this._groupsConfig[name] = {
...this._groupsConfig[name],
level
};
return this;
}
/**
* Sets which groups are active. Only active groups will route logs.
* Pass null to clear the filter (all groups active).
*
* @see {@link https://loglayer.dev/logging-api/groups.html | Groups Docs}
*/
setActiveGroups(groups) {
this._activeGroups = groups ? new Set(groups) : null;
return this;
}
/**
* Returns a snapshot of all group configurations.
*
* @see {@link https://loglayer.dev/logging-api/groups.html | Groups Docs}
*/
getGroups() {
return this._groupsConfig ? { ...this._groupsConfig } : {};
}
/**
* Appends context data which will be included with
* every log entry.
*
* Passing in an empty value / object will *not* clear the context.
*
* To clear the context, use {@link https://loglayer.dev/logging-api/context.html#clearing-context | clearContext()}.
*
* @see {@link https://loglayer.dev/logging-api/context.html | Context Docs}
*/
withContext(context) {
let updatedContext = context;
if (!context) {
if (this._config.consoleDebug) console.debug("[LogLayer] withContext was called with no context; dropping.");
return this;
}
if (this.pluginManager.hasPlugins(_loglayer_plugin.PluginCallbackType.onContextCalled)) {
updatedContext = this.pluginManager.runOnContextCalled(context, this);
if (!updatedContext) {
if (this._config.consoleDebug) console.debug("[LogLayer] Context was dropped due to plugin returning falsy value.");
return this;
}
}
const currentContext = this.contextManager.getContext();
for (const key of Object.keys(updatedContext)) {
const wasLazy = key in currentContext && (0, _loglayer_shared.isLazy)(currentContext[key]);
const nowLazy = (0, _loglayer_shared.isLazy)(updatedContext[key]);
if (!wasLazy && nowLazy) this._lazyContextCount++;
else if (wasLazy && !nowLazy) this._lazyContextCount--;
}
this.contextManager.appendContext(updatedContext);
return this;
}
/**
* Clears the context data. If keys are provided, only those keys will be removed.
* If no keys are provided, all context data will be cleared.
*/
clearContext(keys) {
if (keys !== void 0 && this._lazyContextCount > 0) {
const context = this.contextManager.getContext();
const keysToRemove = Array.isArray(keys) ? keys : [keys];
for (const key of keysToRemove) if (key in context && (0, _loglayer_shared.isLazy)(context[key])) this._lazyContextCount--;
} else if (keys === void 0) this._lazyContextCount = 0;
this.contextManager.clearContext(keys);
return this;
}
getContext(options) {
const context = this.contextManager.getContext();
if (options?.raw || this._lazyContextCount === 0) return context;
const { resolved, errors } = (0, _loglayer_shared.resolveLazyValues)(context);
if (errors) this._logLazyEvalErrors(errors, "context");
const { resolved: finalResolved, asyncKeys } = (0, _loglayer_shared.replacePromiseValues)(resolved);
if (asyncKeys) this._logAsyncLazyContextErrors(asyncKeys);
return finalResolved;
}
/**
* Add additional plugins.
*
* @see {@link https://loglayer.dev/plugins/ | Plugins Docs}
*/
addPlugins(plugins) {
this.pluginManager.addPlugins(plugins);
}
/**
* Enables a plugin by id.
*
* @see {@link https://loglayer.dev/plugins/ | Plugins Docs}
*/
enablePlugin(id) {
this.pluginManager.enablePlugin(id);
}
/**
* Disables a plugin by id.
*
* @see {@link https://loglayer.dev/plugins/ | Plugins Docs}
*/
disablePlugin(id) {
this.pluginManager.disablePlugin(id);
}
/**
* Removes a plugin by id.
*
* @see {@link https://loglayer.dev/plugins/ | Plugins Docs}
*/
removePlugin(id) {
this.pluginManager.removePlugin(id);
}
/**
* Specifies metadata to include with the log message
*
* @see {@link https://loglayer.dev/logging-api/metadata.html | Metadata Docs}
*/
withMetadata(metadata) {
return new LogBuilder(this).withMetadata(metadata);
}
/**
* Specifies an Error to include with the log message
*
* @see {@link https://loglayer.dev/logging-api/error-handling.html | Error Handling Docs}
*/
withError(error) {
return new LogBuilder(this).withError(error);
}
/**
* Creates a new instance of LogLayer but with the initialization
* configuration and context copied over.
*
* @see {@link https://loglayer.dev/logging-api/child-loggers.html | Child Logging Docs}
*/
child() {
const childLogger = new LogLayer({
...this._config,
transport: Array.isArray(this._config.transport) ? [...this._config.transport] : this._config.transport
}).withPluginManager(this.pluginManager).withContextManager(this.contextManager.clone()).withLogLevelManager(this.logLevelManager.clone());
this.contextManager.onChildLoggerCreated({
parentContextManager: this.contextManager,
childContextManager: childLogger.contextManager,
parentLogger: this,
childLogger
});
this.logLevelManager.onChildLoggerCreated({
parentLogLevelManager: this.logLevelManager,
childLogLevelManager: childLogger.logLevelManager,
parentLogger: this,
childLogger
});
childLogger._lazyContextCount = childLogger.contextManager.hasContextData() ? (0, _loglayer_shared.countLazyValues)(childLogger.contextManager.getContext()) : 0;
childLogger._groupsConfig = this._groupsConfig;
childLogger._activeGroups = this._activeGroups;
childLogger._ungroupedBehavior = this._ungroupedBehavior;
childLogger._assignedGroups = this._assignedGroups ? [...this._assignedGroups] : null;
return childLogger;
}
/**
* Replaces all existing transports with new ones.
*
* Transport changes only affect the current logger instance. Child loggers
* created before the change will retain their original transports, and
* parent loggers are not affected when a child modifies its transports.
*
* @see {@link https://loglayer.dev/logging-api/transport-management.html | Transport Management Docs}
*/
withFreshTransports(transports) {
this._config.transport = transports;
this._initializeTransports(transports);
return this;
}
/**
* Adds one or more transports to the existing transports.
* If a transport with the same ID already exists, it will be replaced.
*
* Transport changes only affect the current logger instance. Child loggers
* created before the change will retain their original transports, and
* parent loggers are not affected when a child modifies its transports.
*
* @see {@link https://loglayer.dev/logging-api/transport-management.html | Transport Management Docs}
*/
addTransport(transports) {
const newTransports = Array.isArray(transports) ? transports : [transports];
const existingTransports = Array.isArray(this._config.transport) ? this._config.transport : [this._config.transport];
const newTransportIds = new Set(newTransports.map((t) => t.id));
for (const transport of newTransports) {
const existingTransport = this.idToTransport[transport.id];
if (existingTransport && typeof existingTransport[Symbol.dispose] === "function") existingTransport[Symbol.dispose]();
}
const allTransports = [...existingTransports.filter((t) => !newTransportIds.has(t.id)), ...newTransports];
this._config.transport = allTransports;
for (const transport of newTransports) this.idToTransport[transport.id] = transport;
this.hasMultipleTransports = allTransports.length > 1;
this.singleTransport = this.hasMultipleTransports ? null : allTransports[0];
return this;
}
/**
* Removes a transport by its ID.
*
* Transport changes only affect the current logger instance. Child loggers
* created before the change will retain their original transports, and
* parent loggers are not affected when a child modifies its transports.
*
* @returns true if the transport was found and removed, false otherwise.
*
* @see {@link https://loglayer.dev/logging-api/transport-management.html | Transport Management Docs}
*/
removeTransport(id) {
const transport = this.idToTransport[id];
if (!transport) return false;
if (typeof transport[Symbol.dispose] === "function") transport[Symbol.dispose]();
delete this.idToTransport[id];
const remainingTransports = (Array.isArray(this._config.transport) ? this._config.transport : [this._config.transport]).filter((t) => t.id !== id);
this._config.transport = remainingTransports.length === 1 ? remainingTransports[0] : remainingTransports;
this.hasMultipleTransports = remainingTransports.length > 1;
this.singleTransport = this.hasMultipleTransports ? null : remainingTransports[0] || null;
return true;
}
/**
* Replaces all existing plugins with new ones.
*
* When used with child loggers, it only affects the current logger instance
* and does not modify the parent's plugins.
*
* @see {@link https://loglayer.dev/plugins/ | Plugins Docs}
*/
withFreshPlugins(plugins) {
this._config.plugins = plugins;
this.pluginManager = new PluginManager(plugins);
return this;
}
withPluginManager(pluginManager) {
this.pluginManager = pluginManager;
return this;
}
/**
* Logs only the error object without a log message
*
* @see {@link https://loglayer.dev/logging-api/error-handling.html | Error Handling Docs}
*/
errorOnly(error, opts) {
const logLevel = opts?.logLevel || _loglayer_shared.LogLevel.error;
if (!this.isLevelEnabled(logLevel)) return;
const { copyMsgOnOnlyError } = this._config;
const formatLogConf = {
logLevel,
err: error
};
if ((copyMsgOnOnlyError && opts?.copyMsg !== false || opts?.copyMsg === true) && error?.message) formatLogConf.params = [error.message];
this._formatLog(formatLogConf);
}
/**
* Logs only metadata without a log message
*
* @see {@link https://loglayer.dev/logging-api/metadata.html | Metadata Docs}
*/
metadataOnly(metadata, logLevel = _loglayer_shared.LogLevel.info) {
if (!this.isLevelEnabled(logLevel)) return void 0;
const { muteMetadata, consoleDebug } = this._config;
if (muteMetadata) return;
if (!metadata) {
if (consoleDebug) console.debug("[LogLayer] metadataOnly was called with no metadata; dropping.");
return;
}
let data = metadata;
if (this.pluginManager.hasPlugins(_loglayer_plugin.PluginCallbackType.onMetadataCalled)) {
data = this.pluginManager.runOnMetadataCalled(metadata, this);
if (!data) {
if (consoleDebug) console.debug("[LogLayer] Metadata was dropped due to plugin returning falsy value.");
return;
}
}
const config = {
logLevel,
metadata: data
};
return this._formatLog(config);
}
/**
* Sends a log message to the logging library under an info log level.
*
* Supports tagged template syntax:
* ```typescript
* log.info`User ${userId} logged in`;
* ```
*
* @see {@link https://loglayer.dev/logging-api/basic-logging.html | Basic Logging Docs}
*/
info(...args) {
if (!this.isLevelEnabled(_loglayer_shared.LogLevel.info)) return;
const messages = (0, _loglayer_shared.resolveMessages)(args);
this._formatMessage(messages);
this._formatLog({
logLevel: _loglayer_shared.LogLevel.info,
params: messages
});
}
/**
* Sends a log message to the logging library under the warn log level
*
* Supports tagged template syntax:
* ```typescript
* log.warn`Request ${requestId} timed out`;
* ```
*
* @see {@link https://loglayer.dev/logging-api/basic-logging.html | Basic Logging Docs}
*/
warn(...args) {
if (!this.isLevelEnabled(_loglayer_shared.LogLevel.warn)) return;
const messages = (0, _loglayer_shared.resolveMessages)(args);
this._formatMessage(messages);
this._formatLog({
logLevel: _loglayer_shared.LogLevel.warn,
params: messages
});
}
/**
* Sends a log message to the logging library under the error log level
*
* Supports tagged template syntax:
* ```typescript
* log.error`Failed to process ${taskId}`;
* ```
*
* @see {@link https://loglayer.dev/logging-api/basic-logging.html | Basic Logging Docs}
*/
error(...args) {
if (!this.isLevelEnabled(_loglayer_shared.LogLevel.error)) return;
const messages = (0, _loglayer_shared.resolveMessages)(args);
this._formatMessage(messages);
this._formatLog({
logLevel: _loglayer_shared.LogLevel.error,
params: messages
});
}
/**
* Sends a log message to the logging library under the debug log level
*
* Supports tagged template syntax:
* ```typescript
* log.debug`Cache hit for ${cacheKey}`;
* ```
*
* @see {@link https://loglayer.dev/logging-api/basic-logging.html | Basic Logging Docs}
*/
debug(...args) {
if (!this.isLevelEnabled(_loglayer_shared.LogLevel.debug)) return;
const messages = (0, _loglayer_shared.resolveMessages)(args);
this._formatMessage(messages);
this._formatLog({
logLevel: _loglayer_shared.LogLevel.debug,
params: messages
});
}
/**
* Sends a log message to the logging library under the trace log level
*
* Supports tagged template syntax:
* ```typescript
* log.trace`Entering ${functionName}`;
* ```
*
* @see {@link https://loglayer.dev/logging-api/basic-logging.html | Basic Logging Docs}
*/
trace(...args) {
if (!this.isLevelEnabled(_loglayer_shared.LogLevel.trace)) return;
const messages = (0, _loglayer_shared.resolveMessages)(args);
this._formatMessage(messages);
this._formatLog({
logLevel: _loglayer_shared.LogLevel.trace,
params: messages
});
}
/**
* Sends a log message to the logging library under the fatal log level
*
* Supports tagged template syntax:
* ```typescript
* log.fatal`System crash: ${reason}`;
* ```
*
* @see {@link https://loglayer.dev/logging-api/basic-logging.html | Basic Logging Docs}
*/
fatal(...args) {
if (!this.isLevelEnabled(_loglayer_shared.LogLevel.fatal)) return;
const messages = (0, _loglayer_shared.resolveMessages)(args);
this._formatMessage(messages);
this._formatLog({
logLevel: _loglayer_shared.LogLevel.fatal,
params: messages
});
}
/**
* Logs a raw log entry with complete control over all log parameters.
*
* This method allows you to bypass the normal LogLayer API and directly specify
* all aspects of a log entry including log level, messages, metadata, and error.
* It's useful for scenarios where you need to log structured data that doesn't
* fit the standard LogLayer patterns, or when integrating with external logging
* systems that provide pre-formatted log entries.
*
* The raw entry will still go through all LogLayer processing.
*
* @see {@link https://loglayer.dev/logging-api/basic-logging.html | Basic Logging Docs}
*/
raw(logEntry) {
if (!this.isLevelEnabled(logEntry.logLevel)) return void 0;
const formatLogConf = {
logLevel: logEntry.logLevel,
params: logEntry.messages,
metadata: logEntry.metadata,
rootData: logEntry.rootData,
err: logEntry.error,
context: logEntry.context
};
this._formatMessage(logEntry.messages);
return this._formatLog(formatLogConf);
}
/**
* All logging inputs are dropped and stops sending logs to the logging library.
*
* @see {@link https://loglayer.dev/logging-api/basic-logging.html#enabling-disabling-logging | Enabling/Disabling Logging Docs}
*/
disableLogging() {
this.logLevelManager.disableLogging();
return this;
}
/**
* Enable sending logs to the logging library.
*
* @see {@link https://loglayer.dev/logging-api/basic-logging.html#enabling-disabling-logging | Enabling/Disabling Logging Docs}
*/
enableLogging() {
this.logLevelManager.enableLogging();
return this;
}
/**
* Disables inclusion of context data in the print
*
* @see {@link https://loglayer.dev/logging-api/context.html#managing-context | Managing Context Docs}
*/
muteContext() {
this._config.muteContext = true;
return this;
}
/**
* Enables inclusion of context data in the print
*
* @see {@link https://loglayer.dev/logging-api/context.html#managing-context | Managing Context Docs}
*/
unMuteContext() {
this._config.muteContext = false;
return this;
}
/**
* Disables inclusion of metadata in the print
*
* @see {@link https://loglayer.dev/logging-api/metadata.html#controlling-metadata-output | Controlling Metadata Output Docs}
*/
muteMetadata() {
this._config.muteMetadata = true;
return this;
}
/**
* Enables inclusion of metadata in the print
*
* @see {@link https://loglayer.dev/logging-api/metadata.html#controlling-metadata-output | Controlling Metadata Output Docs}
*/
unMuteMetadata() {
this._config.muteMetadata = false;
return this;
}
/**
* Enables a specific log level
*
* @see {@link https://loglayer.dev/logging-api/basic-logging.html#enabling-disabling-logging | Enabling/Disabling Logging Docs}
*/
enableIndividualLevel(logLevel) {
this.logLevelManager.enableIndividualLevel(logLevel);
return this;
}
/**
* Disables a specific log level
*
* @see {@link https://loglayer.dev/logging-api/basic-logging.html#enabling-disabling-logging | Enabling/Disabling Logging Docs}
*/
disableIndividualLevel(logLevel) {
this.logLevelManager.disableIndividualLevel(logLevel);
return this;
}
/**
* Sets the minimum log level to be used by the logger. Only messages with
* this level or higher severity will be logged.
*
* For example, if you setLevel(LogLevel.warn), this will:
* Enable:
* - warn
* - error
* - fatal
* Disable:
* - info
* - debug
* - trace
*
* @see {@link https://loglayer.dev/logging-api/basic-logging.html#enabling-disabling-logging | Enabling/Disabling Logging Docs}
*/
setLevel(logLevel) {
this.logLevelManager.setLevel(logLevel);
return this;
}
/**
* Checks if a specific log level is enabled
*
* @see {@link https://loglayer.dev/logging-api/basic-logging.html#checking-if-a-log-level-is-enabled | Checking if a Log Level is Enabled Docs}
*/
isLevelEnabled(logLevel) {
return this.logLevelManager.isLevelEnabled(logLevel);
}
formatContext(context) {
const { contextFieldName, muteContext } = this._config;
if (context && Object.keys(context).length > 0 && !muteContext) {
if (contextFieldName) return { [contextFieldName]: { ...context } };
return { ...context };
}
return {};
}
formatMetadata(data = null) {
const { metadataFieldName, muteMetadata } = this._config;
if (data && !muteMetadata) {
if (metadataFieldName) return { [metadataFieldName]: { ...data } };
return { ...data };
}
return {};
}
/**
* Returns a logger instance for a specific transport
*
* @see {@link https://loglayer.dev/logging-api/transport-management.html | Transport Management Docs}
*/
getLoggerInstance(id) {
const transport = this.idToTransport[id];
if (!transport) return;
return transport.getLoggerInstance();
}
/**
* Parses the LOGLAYER_GROUPS environment variable and overrides
* _activeGroups and group levels accordingly.
* Format: "name,name" or "name:level,name:level"
*/
_parseEnvGroups() {
const envValue = typeof process !== "undefined" ? process.env?.LOGLAYER_GROUPS : void 0;
if (!envValue) return;
const entries = envValue.split(",").map((s) => s.trim()).filter(Boolean);
const activeNames = [];
for (const entry of entries) {
const colonIdx = entry.indexOf(":");
if (colonIdx > 0) {
const name = entry.slice(0, colonIdx);
const level = entry.slice(colonIdx + 1);
activeNames.push(name);
if (this._groupsConfig?.[name]) this._groupsConfig[name] = {
...this._groupsConfig[name],
level
};
} else activeNames.push(entry);
}
this._activeGroups = new Set(activeNames);
}
/**
* Merges per-log groups (from LogBuilder) with logger-level assigned groups.
*/
_mergeGroups(perLogGroups) {
if (this._assignedGroups && perLogGroups) {
const combined = new Set([...this._assignedGroups, ...perLogGroups]);
return Array.from(combined);
}
return perLogGroups || this._assignedGroups;
}
/**
* Applies ungrouped routing rules for a transport.
*/
_applyUngroupedRules(transportId) {
if (this._ungroupedBehavior === "all") return true;
if (this._ungroupedBehavior === "none") return false;
return this._ungroupedBehavior.includes(transportId);
}
/**
* Determines whether a transport should receive a log entry based on group routing rules.
*/
_shouldTransportReceiveLog(transportId, logLevel, groups) {
if (!this._groupsConfig) return true;
const effectiveGroups = this._mergeGroups(groups);
if (!effectiveGroups || effectiveGroups.length === 0) return this._applyUngroupedRules(transportId);
let hasAnyDefinedGroup = false;
for (const groupName of effectiveGroups) {
const groupConfig = this._groupsConfig[groupName];
if (!groupConfig) continue;
hasAnyDefinedGroup = true;
if (groupConfig.enabled === false) continue;
if (this._activeGroups && !this._activeGroups.has(groupName)) continue;
if (groupConfig.level) {
const groupLevelPriority = _loglayer_shared.LogLevelPriority[groupConfig.level];
if (_loglayer_shared.LogLevelPriority[logLevel] < groupLevelPriority) continue;
}
if (groupConfig.transports.includes(transportId)) return true;
}
if (!hasAnyDefinedGroup) return this._applyUngroupedRules(transportId);
return false;
}
_formatMessage(messages = []) {
const { prefix } = this._config;
if (prefix && typeof messages[0] === "string") messages[0] = `${prefix} ${messages[0]}`;
}
_formatLog({ logLevel, params = [], metadata = null, rootData = null, err, context = null, groups = null }) {
const rawContext = context !== null ? context : this.contextManager.getContext();
let finalContextData;
if (this._lazyContextCount > 0 || context !== null) {
const contextResult = (0, _loglayer_shared.resolveLazyValues)(rawContext);
if (contextResult.errors) this._logLazyEvalErrors(contextResult.errors, "context");
const { resolved, asyncKeys } = (0, _loglayer_shared.replacePromiseValues)(contextResult.resolved);
if (asyncKeys) this._logAsyncLazyContextErrors(asyncKeys);
finalContextData = resolved;
} else finalContextData = rawContext;
let metadataErrors = null;
if (metadata) {
const metadataResult = (0, _loglayer_shared.resolveLazyValues)(metadata);
metadata = metadataResult.resolved;
metadataErrors = metadataResult.errors;
}
if (metadataErrors) this._logLazyEvalErrors(metadataErrors, "metadata");
if (metadata ? (0, _loglayer_shared.hasPromiseValues)(metadata) : false) return this._resolveAsyncAndProcess(logLevel, params, finalContextData, metadata, rootData, err, context, groups);
this._processLog(logLevel, params, finalContextData, metadata, rootData, err, context, groups);
}
/**
* Resolves any Promise values in metadata (from async lazy callbacks)
* and then processes the log entry. Context is already fully resolved.
*/
async _resolveAsyncAndProcess(logLevel, params, contextData, metadata, rootData, err, context, groups) {
let resolvedMetadata = null;
if (metadata) {
const metadataResult = await (0, _loglayer_shared.resolvePromiseValues)(metadata);
resolvedMetadata = metadataResult.resolved;
if (metadataResult.errors) this._logLazyEvalErrors(metadataResult.errors, "metadata");
}
this._processLog(logLevel, params, contextData, resolvedMetadata, rootData, err, context, groups);
}
/**
* Logs error entries for lazy evaluation failures.
* Calls _processLog directly to bypass lazy evaluation and prevent recursion.
*/
_logLazyEvalErrors(failures, source) {
if (this._isLoggingLazyError) {
for (const f of failures) console.error(`[LogLayer] Lazy evaluation error in ${source} key "${f.key}":`, f.error);
return;
}
this._isLoggingLazyError = true;
try {
for (const failure of failures) {
const errorMessage = failure.error instanceof Error ? failure.error.message : String(failure.error);
this._processLog(_loglayer_shared.LogLevel.error, [`[LogLayer] Lazy evaluation failed for ${source} key "${failure.key}": ${errorMessage}`], {}, null, null, failure.error instanceof Error ? failure.error : void 0, {});
}
} finally {
this._isLoggingLazyError = false;
}
}
/**
* Logs error entries for async lazy values found in context.
* Async lazy values are only supported in metadata, not context.
*/
_logAsyncLazyContextErrors(keys) {
if (this._isLoggingLazyError) {
for (const key of keys) console.error(`[LogLayer] Async lazy values are not supported in context (key "${key}"). Use async lazy only in metadata.`);
return;
}
this._isLoggingLazyError = true;
try {
for (const key of keys) this._processLog(_loglayer_shared.LogLevel.error, [`[LogLayer] Async lazy values are not supported in context (key "${key}"). Use async lazy only in metadata.`], {}, null, null, void 0, {});
} finally {
this._isLoggingLazyError = false;
}
}
/**
* Processes a log entry after lazy values have been fully resolved.
* Handles data assembly, plugins, and transport dispatch.
*/
_processLog(logLevel, params, contextData, metadata, rootData, err, context, groups = null) {
const { errorSerializer, errorFieldInMetadata, muteContext, contextFieldName, metadataFieldName, errorFieldName, prefix } = this._config;
const schema = {
contextFieldName: contextFieldName ?? void 0,
metadataFieldName: metadataFieldName ?? void 0,
errorFieldName: errorFieldName ?? "err"
};
const effectiveGroups = this._mergeGroups(groups) ?? void 0;
let hasObjData = !!metadata || (muteContext ? false : context !== null ? Object.keys(context).length > 0 : this.contextManager.hasContextData());
let d = {};
if (hasObjData) if (contextFieldName && contextFieldName === metadataFieldName) {
const formattedContextData = this.formatContext(contextData)[contextFieldName];
const updatedMetadata = this.formatMetadata(metadata)[metadataFieldName];
d = { [contextFieldName]: {
...formattedContextData,
...updatedMetadata
} };
} else d = {
...this.formatContext(contextData),
...this.formatMetadata(metadata)
};
if (err) {
const serializedError = errorSerializer ? errorSerializer(err) : err;
if (errorFieldInMetadata && metadata && metadataFieldName) if (d?.[metadataFieldName]) d[metadataFieldName][errorFieldName] = serializedError;
else d = {
...d,
[metadataFieldName]: { [errorFieldName]: serializedError }
};
else if (errorFieldInMetadata && !metadata && metadataFieldName) d = {
...d,
[metadataFieldName]: { [errorFieldName]: serializedError }
};
else d = {
...d,
[errorFieldName]: serializedError
};
hasObjData = true;
}
if (rootData && Object.keys(rootData).length > 0) {
d = {
...d,
...rootData
};
hasObjData = true;
}
if (this.pluginManager.hasPlugins(_loglayer_plugin.PluginCallbackType.onBeforeDataOut)) {
d = this.pluginManager.runOnBeforeDataOut({
data: hasObjData ? d : void 0,
logLevel,
error: err,
metadata,
context: contextData,
groups: effectiveGroups,
schema,
prefix
}, this);
if (d && !hasObjData) hasObjData = true;
}
if (this.pluginManager.hasPlugins(_loglayer_plugin.PluginCallbackType.onBeforeMessageOut)) params = this.pluginManager.runOnBeforeMessageOut({
messages: [...params],
logLevel,
groups: effectiveGroups,
schema,
prefix
}, this);
if (this.pluginManager.hasPlugins(_loglayer_plugin.PluginCallbackType.transformLogLevel)) logLevel = this.pluginManager.runTransformLogLevel({
data: hasObjData ? d : void 0,
logLevel,
messages: [...params],
error: err,
metadata,
context: contextData,
groups: effectiveGroups,
schema,
prefix
}, this);
const sharedParams = {
data: hasObjData ? d : void 0,
error: err,
metadata,
context: contextData,
groups: effectiveGroups,
schema,
prefix
};
if (this.hasMultipleTransports) {
const transportPromises = this._config.transport.filter((transport) => {
if (!transport.enabled) return false;
if (!this._shouldTransportReceiveLog(transport.id, logLevel, effectiveGroups)) return false;
return true;
}).map(async (transport) => {
const currentLogLevel = logLevel;
if (this.pluginManager.hasPlugins(_loglayer_plugin.PluginCallbackType.shouldSendToLogger)) {
if (!this.pluginManager.runShouldSendToLogger({
messages: [...params],
logLevel: currentLogLevel,
transportId: transport.id,
...sharedParams
}, this)) return;
}
return transport._sendToLogger({
logLevel: currentLogLevel,
messages: [...params],
hasData: hasObjData,
...sharedParams
});
});
Promise.all(transportPromises).catch((err) => {
if (this._config.consoleDebug) console.error("[LogLayer] Error executing transports:", err);
});
} else {
if (!this.singleTransport?.enabled) return;
if (!this._shouldTransportReceiveLog(this.singleTransport.id, logLevel, effectiveGroups)) return;
if (this.pluginManager.hasPlugins(_loglayer_plugin.PluginCallbackType.shouldSendToLogger)) {
if (!this.pluginManager.runShouldSendToLogger({
messages: [...params],
logLevel,
transportId: this.singleTransport.id,
...sharedParams
}, this)) return;
}
this.singleTransport._sendToLogger({
logLevel,
messages: [...params],
hasData: hasObjData,
...sharedParams
});
}
}
};
//#endregion
//#region src/MockLogBuilder.ts
/**
* A mock implementation of the ILogBuilder interface that does nothing.
* Useful for writing unit tests.
*/
var MockLogBuilder = class {
debug(..._args) {}
error(..._args) {}
info(..._args) {}
trace(..._args) {}
warn(..._args) {}
fatal(..._args) {}
enableLogging() {
return this;
}
disableLogging() {
return this;
}
withMetadata(_metadata) {
return this;
}
withError(_error) {
return this;
}
withGroup(_group) {
return this;
}
};
//#endregion
//#region src/MockLogLayer.ts
/* istanbul ignore file */
/**
* A mock implementation of the ILogLayer interface that does nothing.
* Useful for writing unit tests.
* MockLogLayer implements both ILogLayer and ILogBuilder for simplicity in testing.
*/
var MockLogLayer = class {
mockLogBuilder = new MockLogBuilder();
mockContextManager = new _loglayer_context_manager.MockContextManager();
mockLogLevelManager = new _loglayer_log_level_manager.MockLogLevelManager();
info(..._args) {}
warn(..._args) {}
error(..._args) {}
debug(..._args) {}
trace(..._args) {}
fatal(..._args) {}
raw(_rawEntry) {}
getLoggerInstance(_id) {}
errorOnly(_error, _opts) {}
metadataOnly(_metadata, _logLevel) {}
addPlugins(_plugins) {}
removePlugin(_id) {}
enablePlugin(_id) {}
disablePlugin(_id) {}
withPrefix(_prefix) {
return this;
}
withGroup(_group) {
return this;
}
addGroup(_name, _config) {
return this;
}
removeGroup(_name) {
return this;
}
enableGroup(_name) {
return this;
}
disableGroup(_name) {
return this;
}
setGroupLevel(_name, _level) {
return this;
}
setActiveGroups(_groups) {
return this;
}
getGroups() {
return {};
}
withContext(_context) {
return this;
}
withError(_error) {
return this.mockLogBuilder.withError(_error);
}
withMetadata(_metadata) {
return this.mockLogBuilder.withMetadata(_metadata);
}
getContext(_options) {
return {};
}
clearContext(_keys) {
return this;
}
enableLogging() {
return this;
}
disableLogging() {
return this;
}
child() {
return this;
}
muteContext() {
return this;
}
unMuteContext() {
return this;
}
muteMetadata() {
return this;
}
unMuteMetadata() {
return this;
}
withFreshTransports(_transports) {
return this;
}
addTransport(_transports) {
return this;
}
removeTransport(_id) {
return true;
}
withFreshPlugins(_plugins) {
return this;
}
withContextManager(_contextManager) {
return this;
}
getContextManager() {
return this.mockContextManager;
}
withLogLevelManager(_logLevelManager) {
return this;
}
getLogLevelManager() {
return this.mockLogLevelManager;
}
getConfig() {
return {};
}
/**
* Sets the mock log builder to use for testing.
*/
setMockLogBuilder(mockLogBuilder) {
this.mockLogBuilder = mockLogBuilder;
}
enableIndividualLevel(_logLevel) {
return this;
}
disableIndividualLevel(_logLevel) {
return this;
}
setLevel(_logLevel) {
return this;
}
isLevelEnabled(_logLevel) {
return true;
}
/**
* Returns the mock log builder used for testing.
*/
getMockLogBuilder() {
return this.mockLogBuilder;
}
/**
* Resets the mock log builder to a new instance of MockLogBuilder.
*/
resetMockLogBuilder() {
this.mockLogBuilder = new MockLogBuilder();
}
};
//#endregion
//#region src/types/mixin.types.ts
/**
* The class that the mixin extends
*/
let LogLayerMixinAugmentType = /* @__PURE__ */ function(LogLayerMixinAugmentType) {
/**
* Mixin extends the LogBuilder prototype
*/
LogLayerMixinAugmentType["LogBuilder"] = "LogBuilder";
/**
* Mixin extends the LogLayer prototype
*/
LogLayerMixinAugmentType["LogLayer"] = "LogLayer";
return LogLayerMixinAugmentType;
}({});
//#endregion
//#region src/mixins.ts
const mixinRegistry = {
logLayerHandlers: [],
pluginsToInit: [],
logBuilderHandlers: []
};
/**
* Adds one or more mixins to LogLayer.
* @param mixin - The mixin(s) to register. Can be a single mixin registration or an array of mixin registrations.
*/
function useLogLayerMixin(mixin) {
const mixins = Array.isArray(mixin) ? mixin : [mixin];
for (const mixinRegistration of mixins) {
if (mixinRegistration.pluginsToAdd) for (const plugin of mixinRegistration.pluginsToAdd) mixinRegistry.pluginsToInit.push(plugin);
for (const mixinToAdd of mixinRegistration.mixinsToAdd) switch (mixinToAdd.augmentationType) {
case "LogLayer":
if (mixinToAdd.onConstruct) mixinRegistry.logLayerHandlers.push({ onConstruct: mixinToAdd.onConstruct });
mixinToAdd.augment(LogLayer.prototype);
mixinToAdd.augmentMock(MockLogLayer.prototype);
break;
case "LogBuilder":
if (mixinToAdd.onConstruct) mixinRegistry.logBuilderHandlers.push({ onConstruct: mixinToAdd.onConstruct });
mixinToAdd.augment(LogBuilder.prototype);
mixinToAdd.augmentMock(MockLogBuilder.prototype);
break;
}
}
}
//#endregion
//#region src/LogBuilder.ts
/**
* A class that contains methods to specify log metadata and an error and assembles
* it to form a data object that can be passed into the transport.
*/
var LogBuilder = class {
err;
metadata;
structuredLogger;
hasMetadata;
pluginManager;
_groups = null;
constructor(structuredLogger) {
this.err = null;
this.metadata = {};
this.structuredLogger = structuredLogger;
this.hasMetadata = false;
this.pluginManager = structuredLogger["pluginManager"];
if (mixinRegistry.logBuilderHandlers.length > 0) mixinRegistry.logBuilderHandlers.forEach((mixin) => {
if (mixin.onConstruct) mixin.onConstruct(this, structuredLogger);
});
}
/**
* Specifies metadata to include with the log message
*
* @see {@link https://loglayer.dev/logging-api/metadata.html | Metadata Docs}
*/
withMetadata(metadata) {
const { pluginManager, structuredLogger: { _config: { consoleDebug } } } = this;
if (!metadata) {
if (consoleDebug) console.debug("[LogLayer] withMetadata was called with no metadata; dropping.");
return this;
}
let data = metadata;
if (pluginManager.hasPlugins(_loglayer_plugin.PluginCallbackType.onMetadataCalled)) {
data = pluginManager.runOnMetadataCalled(metadata, this.structuredLogger);
if (!data) {
if (consoleDebug) console.debug("[LogLayer] Metadata was dropped due to plugin returning falsy value.");
return this;
}
}
this.metadata = {
...this.metadata,
...data
};
this.hasMetadata = true;
return this;
}
/**
* Specifies an Error to include with the log message
*
* @see {@link https://loglayer.dev/logging-api/error-handling.html | Error Handling Docs}
*/
withError(error) {
this.err = error;
return this;
}
/**
* Tags this log entry with one or more groups for routing.
*
* @see {@link https://loglayer.dev/logging-api/groups.html | Groups Docs}
*/
withGroup(group) {
const newGroups = Array.isArray(group) ? group : [group];
if (this._groups) {
const combined = new Set([...this._groups, ...newGroups]);
this._groups = Array.from(combined);
} else this._groups = [...newGroups];
return this;
}
/**
* Sends a log message to the logging library under an info log level.
*
* Supports tagged template syntax:
* ```typescript
* log.withMetadata({ userId }).info`User ${userId} logged in`;
* ```
*/
info(...args) {
if (!this.structuredLogger.isLevelEnabled(_loglayer_shared.LogLevel.info)) return;
const messages = (0, _loglayer_shared.resolveMessages)(args);
this.structuredLogger._formatMessage(messages);
return this.formatLog(_loglayer_shared.LogLevel.info, messages);
}
/**
* Sends a log message to the logging library under the warn log level
*
* Supports tagged template syntax:
* ```typescript
* log.withMetadata({ requestId }).warn`Request ${requestId} timed out`;
* ```
*/
warn(...args) {
if (!this.structuredLogger.isLevelEnabled(_loglayer_shared.LogLevel.warn)) return;
const messages = (0, _loglayer_shared.resolveMessages)(args);
this.structuredLogger._formatMessage(messages);
return this.formatLog(_loglayer_shared.LogLevel.warn, messages);
}
/**
* Sends a log message to the logging library under the error log level
*
* Supports tagged template syntax:
* ```typescript
* log.withError(err).error`Failed to process ${taskId}`;
* ```
*/
error(...args) {
if (!this.structuredLogger.isLevelEnabled(_loglayer_shared.LogLevel.error)) return;
const messages = (0, _loglayer_shared.resolveMessages)(args);
this.structuredLogger._formatMessage(messages);
return this.formatLog(_loglayer_shared.LogLevel.error, messages);
}
/**
* Sends a log message to the logging library under the debug log level
*
* Supports tagged template syntax:
* ```typescript
* log.withMetadata({ cacheKey }).debug`Cache hit for ${cacheKey}`;
* ```
*/
debug(...args) {
if (!this.structuredLogger.isLevelEnabled(_loglayer_shared.LogLevel.debug)) return;
const messages = (0, _loglayer_shared.resolveMessages)(args);
this.structuredLogger._formatMessage(messages);
return this.formatLog(_loglayer_shared.LogLevel.debug, messages);
}
/**
* Sends a log message to the logging library under the trace log level
*
* Supports tagged template syntax:
* ```typescript
* log.withMetadata({ functionName }).trace`Entering ${functionName}`;
* ```
*/
trace(...args) {
if (!this.structuredLogger.isLevelEnabled(_loglayer_shared.LogLevel.trace)) return;
const messages = (0, _loglayer_shared.resolveMessages)(args);
this.structuredLogger._formatMessage(messages);
return thi