@axiomhq/logging
Version:
The official logging package for Axiom
191 lines (190 loc) • 5.81 kB
JavaScript
import { defaultFormatters } from "./default-formatters.js";
import { Version, isBrowser } from "./runtime.js";
const LOG_LEVEL = "info";
const EVENT = /* @__PURE__ */ Symbol.for("logging.event");
const LogLevelValue = {
debug: 0,
info: 1,
warn: 2,
error: 3,
off: 100
};
const LogLevel = {
debug: "debug",
info: "info",
warn: "warn",
error: "error",
off: "off"
};
class Logger {
constructor(initConfig) {
this.initConfig = initConfig;
if (this.initConfig.logLevel != void 0) {
this.logLevel = LogLevelValue[this.initConfig.logLevel];
} else {
this.logLevel = LogLevelValue[LOG_LEVEL];
}
this.config = { ...initConfig };
if (!this.config.overrideDefaultFormatters) {
this.config.formatters = [...defaultFormatters, ...this.config.formatters ?? []];
}
}
initConfig;
children = [];
logLevel = LogLevelValue.debug;
config;
raw(log) {
this.config.transports.forEach((transport) => transport.log([log]));
}
/**
* Log a debug message
* @param message The log message
* @param options Log options that can include fields and a special EVENT symbol
*
* @example
* // Add fields to the log event
* logger.debug("User action", { userId: 123 });
*/
debug = (message, args = {}) => {
this.log(LogLevel.debug, message, args);
};
/**
* Log an info message
* @param message The log message
* @param options Log options that can include fields and a special EVENT symbol
*
* @example
* // Add fields to the log event
* logger.info("User logged in", { userId: 123 });
*/
info = (message, args = {}) => {
this.log(LogLevel.info, message, args);
};
/**
* Log a warning message
* @param message The log message
* @param options Log options that can include fields and a special EVENT symbol
*
* @example
* // Add fields to the log event
* logger.warn("Rate limit approaching", { requestCount: 950 });
*/
warn = (message, args = {}) => {
this.log(LogLevel.warn, message, args);
};
/**
* Log an error message
* @param message The log message
* @param options Log options that can include fields and a special EVENT symbol
*
* @example
* // Log an error with stack trace
* try {
* // some code that throws
* } catch (err) {
* logger.error("Operation failed", err);
* }
*/
error = (message, args = {}) => {
this.log(LogLevel.error, message, args);
};
/**
* Create a child logger with additional context fields
* @param fields Additional context fields to include in all logs from this logger
*
* @example
* // Create a child logger with additional fields
* const childLogger = logger.with({ userId: 123 });
*/
with = (fields) => {
const { [EVENT]: argsEventFields, ...argsRest } = this.config.args ?? {};
const { [EVENT]: _eventFields, ...rest } = fields;
const eventFields = { ...argsEventFields ?? {}, ..._eventFields ?? {} };
const childConfig = { ...this.config, args: { ...argsRest, ...rest, [EVENT]: eventFields } };
const child = new Logger(childConfig);
this.children.push(child);
return child;
};
_transformEvent = (level, message, args = {}) => {
let rootFields = {};
let fields = this.config.args ?? {};
if (this.config.args && EVENT in this.config.args) {
const { [EVENT]: argsEventFields, ...argsRest } = this.config.args ?? {};
rootFields = { ...argsEventFields ?? {} };
fields = argsRest;
}
const logEvent = {
level: LogLevel[level].toString(),
message,
_time: new Date(Date.now()).toISOString(),
fields,
"@app": {
"axiom-logging-version": Version
},
source: isBrowser ? "browser-log" : "server-log"
};
if (rootFields && typeof rootFields === "object") {
Object.assign(logEvent, rootFields);
}
if (args instanceof Error) {
logEvent.fields = {
...logEvent.fields,
message: args.message,
stack: args.stack,
name: args.name
};
}
if (typeof args === "object" && args !== null) {
const { [EVENT]: rootArgs, ...fieldArgs } = args;
const parsedArgs = JSON.parse(JSON.stringify(fieldArgs, jsonFriendlyErrorReplacer));
if (rootArgs && typeof rootArgs === "object" && rootArgs !== null) {
Object.assign(logEvent, rootArgs);
}
if (Object.keys(parsedArgs).length > 0) {
logEvent.fields = { ...logEvent.fields, ...parsedArgs };
}
} else if (Array.isArray(args)) {
logEvent.fields = { ...logEvent.fields, args };
}
if (this.config.formatters && this.config.formatters.length > 0) {
return this.config.formatters.reduce((acc, formatter) => formatter(acc), logEvent);
}
return logEvent;
};
/**
* Log a message with the specified level
* @param level The log level
* @param message The log message
* @param options Log options or Error object
*/
log = (level, message, args = {}) => {
this.config.transports.forEach((transport) => transport.log([this._transformEvent(level, message, args)]));
};
flush = async () => {
const promises = [
...this.config.transports.map((transport) => transport.flush()),
...this.children.map((child) => child.flush())
];
await Promise.allSettled(promises);
};
}
function jsonFriendlyErrorReplacer(_key, value) {
if (value instanceof Error) {
return {
// Pull all enumerable properties, supporting properties on custom Errors
...value,
// Explicitly pull Error's non-enumerable properties
name: value.name,
message: value.message,
stack: value.stack
};
}
return value;
}
export {
EVENT,
LogLevel,
LogLevelValue,
Logger
};
//# sourceMappingURL=logger.js.map