UNPKG

@sonatel-os/juf-xpress-logger-edge

Version:

JUF XPress logger for Node Edge

1,038 lines (1,037 loc) 35.1 kB
var __typeError = (msg) => { throw TypeError(msg); }; var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg); var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method); var _apmClient, _crypt, _findLogFile, _logger, _apm, _appName, _hasInstance, _LoggerService_instances, isInstanciated_fn, update_fn, replaceSensitiveKeys_fn; class BaseLogger { /** * Writes a log entry. * @abstract * @param {object} logData - The data to be logged. */ writeLog(logData) { throw new Error("writeLog method must be implemented by subclasses"); } } class ConsoleLogger extends BaseLogger { /** * @constructor * @param {object} config - Configuration for the logger. * @param {boolean} config.sendToRemote - Whether to send logs to a remote server. * @param {string} [config.remoteUrl] - URL of the remote logging server. * @param {string} [config.logLevel='info'] - Log level for filtering logs. */ constructor({ sendToRemote = false, remoteUrl = "", logLevel = "info" } = {}) { super(); this.sendToRemote = sendToRemote; this.remoteUrl = remoteUrl; this.logLevel = logLevel; } /** * Writes a log entry to the console or remote server. * @param {object} logData - The data to be logged. */ async writeLog(logData) { const { logLevel = "info", message, meta } = logData; const logEntry = { "@timestamp": (/* @__PURE__ */ new Date()).toISOString(), "log.level": logLevel.toLowerCase(), "meta": { ...meta, "event.action": message, "event.category": logLevel.toUpperCase() } }; console.log(JSON.stringify(logEntry)); if (this.sendToRemote && this.remoteUrl) { try { await fetch(this.remoteUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(logEntry) }); } catch (error) { console.error("Failed to send log to remote server:", error); } } } } class LoggerFactory { /** * Creates a logger instance based on the provided configuration. * @param {object} config - Configuration for the logger. * @param {string} config.type - Type of logger ('console' or 'file'). * @param {string} [config.appName] - Application name for file logging. * @param {string} [config.logDir] - Directory for file logging. * @param {boolean} [config.constantFileName=false] - Whether to use a constant file name for logs. * @returns {BaseLogger} - Logger instance. */ static createLogger(config) { switch (config.type) { case "console": return new ConsoleLogger(); case "file": default: console.log(`Logger type ${config.type} is not supported yet`); } } } class ElasticApmClient { constructor({ serviceName, serverUrl, secretToken, environment = "development", logLevel = "info" }) { this.serviceName = serviceName; this.serverUrl = serverUrl; this.secretToken = secretToken; this.environment = environment; this.logLevel = logLevel; this.apiEndpoint = `${serverUrl}/intake/v2/events`; } /** * Send a payload to the Elastic APM server with retries * @param {Object} payload - The payload to send. * @param {number} retries - Number of retries in case of failure. */ async sendPayload(payload, retries = 3) { try { const response = await fetch(this.apiEndpoint, { method: "POST", headers: { "Content-Type": "application/x-ndjson", Authorization: `Bearer ${this.secretToken}` }, body: `${JSON.stringify(payload)} ` // NDJSON format required by Elastic APM }); if (!response.ok) { console.error(`Failed to send APM data (Status: ${response.status}):`, response.statusText); if (retries > 0) { console.log(`Retrying... (${3 - retries} attempts left)`); return this.sendPayload(payload, retries - 1); } } } catch (error) { console.error("Error sending APM data:", error); if (retries > 0) { console.log(`Retrying... (${3 - retries} attempts left)`); return this.sendPayload(payload, retries - 1); } } } /** * Report a transaction to Elastic APM * @param {Object} transaction - The transaction object to report. */ async reportTransaction(transaction) { const payload = { transaction: { ...transaction, service: { name: this.serviceName, environment: this.environment } } }; await this.sendPayload(payload); } /** * Report an error to Elastic APM * @param {Object} error - The error object to report. */ async reportError(error) { const payload = { error: { ...error, service: { name: this.serviceName, environment: this.environment } } }; await this.sendPayload(payload); } /** * Report a span to Elastic APM * @param {Object} span - The span object to report. */ async reportSpan(span) { const payload = { span: { ...span, service: { name: this.serviceName, environment: this.environment } } }; await this.sendPayload(payload); } } class StructError extends TypeError { constructor(failure, failures) { let cached; const { message, explanation, ...rest } = failure; const { path } = failure; const msg = path.length === 0 ? message : `At path: ${path.join(".")} -- ${message}`; super(explanation ?? msg); if (explanation != null) this.cause = msg; Object.assign(this, rest); this.name = this.constructor.name; this.failures = () => { return cached ?? (cached = [failure, ...failures()]); }; } } function isIterable(x) { return isObject(x) && typeof x[Symbol.iterator] === "function"; } function isObject(x) { return typeof x === "object" && x != null; } function isNonArrayObject(x) { return isObject(x) && !Array.isArray(x); } function isPlainObject(x) { if (Object.prototype.toString.call(x) !== "[object Object]") { return false; } const prototype = Object.getPrototypeOf(x); return prototype === null || prototype === Object.prototype; } function print(value) { if (typeof value === "symbol") { return value.toString(); } return typeof value === "string" ? JSON.stringify(value) : `${value}`; } function shiftIterator(input) { const { done, value } = input.next(); return done ? void 0 : value; } function toFailure(result, context, struct, value) { if (result === true) { return; } else if (result === false) { result = {}; } else if (typeof result === "string") { result = { message: result }; } const { path, branch } = context; const { type } = struct; const { refinement, message = `Expected a value of type \`${type}\`${refinement ? ` with refinement \`${refinement}\`` : ""}, but received: \`${print(value)}\`` } = result; return { value, type, refinement, key: path[path.length - 1], path, branch, ...result, message }; } function* toFailures(result, context, struct, value) { if (!isIterable(result)) { result = [result]; } for (const r of result) { const failure = toFailure(r, context, struct, value); if (failure) { yield failure; } } } function* run(value, struct, options = {}) { const { path = [], branch = [value], coerce: coerce2 = false, mask: mask2 = false } = options; const ctx = { path, branch, mask: mask2 }; if (coerce2) { value = struct.coercer(value, ctx); } let status = "valid"; for (const failure of struct.validator(value, ctx)) { failure.explanation = options.message; status = "not_valid"; yield [failure, void 0]; } for (let [k, v, s] of struct.entries(value, ctx)) { const ts = run(v, s, { path: k === void 0 ? path : [...path, k], branch: k === void 0 ? branch : [...branch, v], coerce: coerce2, mask: mask2, message: options.message }); for (const t of ts) { if (t[0]) { status = t[0].refinement != null ? "not_refined" : "not_valid"; yield [t[0], void 0]; } else if (coerce2) { v = t[1]; if (k === void 0) { value = v; } else if (value instanceof Map) { value.set(k, v); } else if (value instanceof Set) { value.add(v); } else if (isObject(value)) { if (v !== void 0 || k in value) value[k] = v; } } } } if (status !== "not_valid") { for (const failure of struct.refiner(value, ctx)) { failure.explanation = options.message; status = "not_refined"; yield [failure, void 0]; } } if (status === "valid") { yield [void 0, value]; } } class Struct { constructor(props) { const { type, schema, validator, refiner, coercer = (value) => value, entries = function* () { } } = props; this.type = type; this.schema = schema; this.entries = entries; this.coercer = coercer; if (validator) { this.validator = (value, context) => { const result = validator(value, context); return toFailures(result, context, this, value); }; } else { this.validator = () => []; } if (refiner) { this.refiner = (value, context) => { const result = refiner(value, context); return toFailures(result, context, this, value); }; } else { this.refiner = () => []; } } /** * Assert that a value passes the struct's validation, throwing if it doesn't. */ assert(value, message) { return assert(value, this, message); } /** * Create a value with the struct's coercion logic, then validate it. */ create(value, message) { return create(value, this, message); } /** * Check if a value passes the struct's validation. */ is(value) { return is(value, this); } /** * Mask a value, coercing and validating it, but returning only the subset of * properties defined by the struct's schema. Masking applies recursively to * props of `object` structs only. */ mask(value, message) { return mask(value, this, message); } /** * Validate a value with the struct's validation logic, returning a tuple * representing the result. * * You may optionally pass `true` for the `coerce` argument to coerce * the value before attempting to validate it. If you do, the result will * contain the coerced result when successful. Also, `mask` will turn on * masking of the unknown `object` props recursively if passed. */ validate(value, options = {}) { return validate(value, this, options); } } function assert(value, struct, message) { const result = validate(value, struct, { message }); if (result[0]) { throw result[0]; } } function create(value, struct, message) { const result = validate(value, struct, { coerce: true, message }); if (result[0]) { throw result[0]; } else { return result[1]; } } function mask(value, struct, message) { const result = validate(value, struct, { coerce: true, mask: true, message }); if (result[0]) { throw result[0]; } else { return result[1]; } } function is(value, struct) { const result = validate(value, struct); return !result[0]; } function validate(value, struct, options = {}) { const tuples = run(value, struct, options); const tuple = shiftIterator(tuples); if (tuple[0]) { const error = new StructError(tuple[0], function* () { for (const t of tuples) { if (t[0]) { yield t[0]; } } }); return [error, void 0]; } else { const v = tuple[1]; return [void 0, v]; } } function define(name, validator) { return new Struct({ type: name, schema: null, validator }); } function any() { return define("any", () => true); } function array(Element) { return new Struct({ type: "array", schema: Element, *entries(value) { if (Element && Array.isArray(value)) { for (const [i, v] of value.entries()) { yield [i, v, Element]; } } }, coercer(value) { return Array.isArray(value) ? value.slice() : value; }, validator(value) { return Array.isArray(value) || `Expected an array value, but received: ${print(value)}`; } }); } function boolean() { return define("boolean", (value) => { return typeof value === "boolean"; }); } function func() { return define("func", (value) => { return typeof value === "function" || `Expected a function, but received: ${print(value)}`; }); } function never() { return define("never", () => false); } function number() { return define("number", (value) => { return typeof value === "number" && !isNaN(value) || `Expected a number, but received: ${print(value)}`; }); } function object(schema) { const knowns = schema ? Object.keys(schema) : []; const Never = never(); return new Struct({ type: "object", schema: schema ? schema : null, *entries(value) { if (schema && isObject(value)) { const unknowns = new Set(Object.keys(value)); for (const key of knowns) { unknowns.delete(key); yield [key, value[key], schema[key]]; } for (const key of unknowns) { yield [key, value[key], Never]; } } }, validator(value) { return isNonArrayObject(value) || `Expected an object, but received: ${print(value)}`; }, coercer(value, ctx) { if (!isNonArrayObject(value)) { return value; } const coerced = { ...value }; if (ctx.mask && schema) { for (const key in coerced) { if (schema[key] === void 0) { delete coerced[key]; } } } return coerced; } }); } function optional(struct) { return new Struct({ ...struct, validator: (value, ctx) => value === void 0 || struct.validator(value, ctx), refiner: (value, ctx) => value === void 0 || struct.refiner(value, ctx) }); } function string() { return define("string", (value) => { return typeof value === "string" || `Expected a string, but received: ${print(value)}`; }); } function unknown() { return define("unknown", () => true); } function coerce(struct, condition, coercer) { return new Struct({ ...struct, coercer: (value, ctx) => { return is(value, condition) ? struct.coercer(coercer(value, ctx), ctx) : struct.coercer(value, ctx); } }); } function defaulted(struct, fallback, options = {}) { return coerce(struct, unknown(), (x) => { const f = typeof fallback === "function" ? fallback() : fallback; if (x === void 0) { return f; } if (!options.strict && isPlainObject(x) && isPlainObject(f)) { const ret = { ...x }; let changed = false; for (const key in f) { if (ret[key] === void 0) { ret[key] = f[key]; changed = true; } } if (changed) { return ret; } } return x; }); } const NullableObject = define("NullableObject", (value) => { return value === null || typeof value === "object"; }); const Params$1 = object({ path: string(), method: string(), headers: object(), routeParams: optional(NullableObject), queryParams: optional(NullableObject), payload: optional(any()) }); const HttpListener = object({ onFunction: optional(func()), callback: optional(func()) }); const CaptureApmMiddlewareConfig = object({ params: Params$1, callback: func(), httpListener: optional(HttpListener) }); const StartTransactionSpanConfig = object({ spanName: string(), spanType: string(), payload: optional(object()) }); const _Observability = class _Observability { /** * Creates an instance of Observability with APM configurations. * @constructor * @param {object} options - Configuration options for initializing the APM client. * @param {string} options.serviceName - The name of the service being monitored. * @param {string} options.serverUrl - The URL of the APM server. * @param {boolean} [options.startApmAgent=false] - Whether to start the observability agent. * @param {string} [options.secretToken] - Secret token for the APM server. * @param {string} [options.environment='development'] - The environment in which the service is running. * @param {string} [options.logLevel='info'] - The log level for logging. */ constructor(options) { __privateAdd(this, _apmClient); __privateSet(this, _apmClient, new ElasticApmClient(options)); if (options.startApmAgent) { this.start(); } } /** * Factory method to create an instance of Observability. * @static * @param {object} options - Configuration options for initializing the observability agent. * @returns {Observability} - An instance of Observability. */ static createObservabilityAgent(options) { return new _Observability(options); } /** * Starts the observability agent. */ start() { console.log("Observability agent started with configuration:", __privateGet(this, _apmClient)); } /** * Middleware for capturing APM data and handling transactions. * @param {object} params - Parameters for the middleware. * @param {string} params.path - The path of the request. * @param {string} params.method - The HTTP method of the request. * @param {object} params.headers - The headers of the request. * @param {object} params.routeParams - The route parameters of the request. * @param {object} params.queryParams - The query parameters of the request. * @param {object} [params.payload] - The request payload. * @param {Function} callback - Callback function to be executed after middleware processing. * @param {object} httpListener - HTTP listener object with response handling functions. * @param {Function} httpListener.onFunction - Function that listens to HTTP events (e.g., 'finish'). * @param {Function} httpListener.callback - Callback function to execute after the listener function. * @throws {Error} Throws error if there is an issue with processing. */ async middleware({ path, method, headers, routeParams, queryParams, payload }, callback, httpListener) { try { const structure = { params: { path, method, headers, routeParams, queryParams, payload }, callback, httpListener }; CaptureApmMiddlewareConfig.create(structure); } catch (error) { console.log(error.toString()); return; } const transaction = { name: `${method.toUpperCase()} ${path}`, type: "request", result: "success", // or 'failure' based on logic duration: 100, // you can calculate duration in ms context: { request: { method, url: path, headers, routeParams, queryParams, body: payload } } }; await __privateGet(this, _apmClient).reportTransaction(transaction); const { onFunction, callback: listenerCallback } = httpListener ?? {}; if (onFunction) { onFunction("finish", async () => { if (listenerCallback) listenerCallback(); }); } else if (listenerCallback) { listenerCallback(); } callback(); } /** * Starts a new span for an existing transaction, typically for monitoring external requests or operations. * @param {string} spanName - The name of the span to be started. * @param {string} spanType - The type of the span (e.g., 'external', 'db'). * @param {object} payload - The data sent or being treated in your span. * @returns {Object|null} - Returns a dummy span object or null. */ async startTransactionSpan(spanName, spanType = "custom", payload) { try { const structure = { spanName, spanType, payload }; StartTransactionSpanConfig.create(structure); } catch (error) { console.log(error.toString()); return; } const span = { name: spanName, type: spanType, context: payload }; await __privateGet(this, _apmClient).reportSpan(span); return span; } /** * Ends a given span and logs its duration for debugging purposes. * @param {object} span - The span object to be ended. * @throws {Error} Throws error if the span cannot be ended. * @example * observability.endTransactionSpan(span); */ async endTransactionSpan(span) { try { if (span) { console.log(`[Observability] Ended span: ${span.name} of type ${span.type}`); } else { console.warn("No span provided to end."); } } catch (error) { console.error("Error ending transaction span:", error); throw new Error("Failed to end transaction span."); } } }; _apmClient = new WeakMap(); let Observability = _Observability; const apmConfig = { verifyServerCert: { doc: "Verify APM server certificate", format: Boolean, default: false, env: "JUF_ELK_APM_VERIF_CERT" }, logLevel: { doc: "Log level for APM", format: String, default: "info", env: "JUF_ELK_APM_LOG_LEVEL" }, environment: { doc: "Environment name for APM", format: String, default: "<JUF_JS>", env: "JUF_ELK_APM_ENV_NAME" }, serviceName: { doc: "Service name for APM", format: String, default: "<JUF_JS>", env: "JUF_ELK_APM_SERVICE_NAME" }, secretToken: { doc: "Secret token for APM", format: String, default: "<PASSWORD>", env: "JUF_ELK_APM_SECRET_TOKEN" }, serverUrl: { doc: "Server URL for APM", format: String, default: "http://127.0.0.1:8200", env: "JUF_ELK_APM_SERVER" } }; const environmentConfig = { env: { doc: "The application environment", format: ["test", "dev", "development", "pprod", "prod", "production"], default: "dev", env: "NODE_ENV" }, port: { doc: "The port to bind", format: "port", default: 3e3, env: "PORT" } }; const configSchema = { ...environmentConfig, apm: apmConfig }; const loadConfigFromEnv = (schema) => { const config = {}; Object.entries(schema).forEach(([key, { env, default: defaultValue, format }]) => { const envValue = process.env[env]; let value = envValue !== void 0 ? envValue : defaultValue; if (typeof format === "function") { value = format(value); } config[key] = value; }); return config; }; const envConfig = { get: (name) => { return loadConfigFromEnv(configSchema[name]); } }; const DEFAULT_CRYPT_KEYS = [ "confirmPassword", "client_id", "client_secret", "cookie", "content-secured", "authorization", "password", "pass", "captcha" ]; const LoggerBootstrapStructure = object({ appName: optional(defaulted(string(), "app")), crypt: optional(defaulted(array(string()), [])), logConsole: optional(defaulted(boolean(), true)), logDir: optional(string()), constantFileName: optional(defaulted(boolean(), false)), findLogFile: optional(func()), logLevel: optional(defaulted(string(), "info")), startApmAgent: optional(defaulted(boolean(), false)) }); const StringOrObject = define("StringOrObject", (value) => { return typeof value === "string" || typeof value === "object"; }); const Params = object({ logFrom: string(), userIp: string(), method: string(), payload: StringOrObject, headers: StringOrObject, logTarget: string(), userAgent: string(), logStatus: number(), logStatusCode: string() }); const WriteLogOptions = object({ params: Params, userName: optional(defaulted(string(), "anonymousUser")), logLevel: optional(defaulted(string(), "INFO")), action: string(), duration: optional(number()) }); class LoggerService { /** * Constructs an instance of LoggerService with the specified options. * * @constructor * @param {Object} options - Options for initializing the logger service. * @param {string} [options.appName='app'] - Application name for log identification. * @param {Array<string>} [options.crypt=[]] - List of keys to mask in the log data. * @param {boolean} [options.logConsole=true] - Whether to log to the console. * @param {string} [options.remoteUrl] - URL for sending logs to a remote server. * @param {boolean} [options.sendToRemote=false] - Whether to send logs to a remote server. * @param {string} [options.logLevel='info'] - Log level for APM configuration. * @param {boolean} [options.startApmAgent=false] - Whether to start the APM agent. * @throws {Error} Throws an error if APM or logger initialization fails. */ constructor({ appName = "app", crypt = [], logLevel = "info", startApmAgent = false }) { __privateAdd(this, _LoggerService_instances); __privateAdd(this, _crypt); __privateAdd(this, _findLogFile); /** * @type {ConsoleLogger} * @description Logger instance created by the LoggerFactory. */ __privateAdd(this, _logger); /** * @type {Observability} * @description Observability agent instance for monitoring and tracing purposes. */ __privateAdd(this, _apm); /** * @type {Observability} * @description Observability agent instance for monitoring and tracing purposes. */ __privateAdd(this, _appName); /** * @type {boolean} * @description Flag to determine if the logger service has been initialized. */ __privateAdd(this, _hasInstance, false); if (__privateGet(this, _hasInstance)) { __privateSet(this, _appName, appName); __privateSet(this, _crypt, crypt); __privateSet(this, _logger, LoggerFactory.createLogger({ type: "console", logLevel })); __privateSet(this, _apm, Observability.createObservabilityAgent({ ...envConfig.get("apm"), logLevel, crypt, startApmAgent })); } } /** * Factory method to bootstrap and initialize a new instance of LoggerService with the provided configuration. * * @param {Object} config - Configuration options for LoggerService. * @param {Array<string>} [config.crypt=[]] - List of keys to mask in the log data for sensitive information. * @param {boolean} [config.logConsole=true] - Determines whether to log to the console. * @param {string} [config.logLevel='info'] - Log level for both console and APM configuration ('info', 'warn', 'error', etc.). * @param {boolean} [config.startApmAgent=false] - Indicates whether to start the APM agent for observability. * @returns {LoggerService} An initialized instance of LoggerService. * @throws {Error} Throws an error if the provided configuration is invalid. */ bootstrap({ crypt, logConsole = true, logLevel, startApmAgent }) { const config = { crypt: DEFAULT_CRYPT_KEYS.concat(crypt).filter(Boolean), logConsole, logLevel, startApmAgent }; try { LoggerBootstrapStructure.create(config); } catch (error) { console.log(error.toString()); return; } __privateSet(this, _hasInstance, true); return __privateMethod(this, _LoggerService_instances, update_fn).call(this, config); } /** * Writes a log entry using the configured logger instance. * Supports logging to the console or sending to a remote server. * Handles structured logging with sensitive data masking and optional duration tracking. * * @param {Object} options - Options for logging. * @param {Object} options.params - Log parameters including request and response details. * @param {string} options.params.logFrom - The origin IP address of the log entry. * @param {string} options.params.userIp - The user's IP address for the log entry. * @param {string} options.params.method - The HTTP method of the request (e.g., 'GET', 'POST'). * @param {Object|string} options.params.payload - The request body content, can be an object or a stringified JSON. * @param {Object|string} options.params.headers - The request headers, can be an object or a stringified JSON. * @param {string} options.params.logTarget - The target URL of the log entry. * @param {string} options.params.userAgent - The User-Agent header string. * @param {number} options.params.logStatus - The HTTP status code of the response. * @param {string} options.params.logStatusCode - The HTTP status message (e.g., 'OK', 'Not Found'). * @param {string} [options.userName='anonymousUser'] - The username associated with the log entry. * @param {string} [options.logLevel='INFO'] - The log level (e.g., 'INFO', 'ERROR', 'WARN'). * @param {string} options.action - A brief description of the action being logged. * @param {number} [options.duration] - Optional duration in milliseconds for how long the action took to complete. * @throws {Error} Throws an error if logging fails due to invalid input or processing errors. */ writeLog({ params, userName = "anonymousUser", logLevel = "INFO", action, duration }) { if (!__privateMethod(this, _LoggerService_instances, isInstanciated_fn).call(this)) return; try { WriteLogOptions.create({ params, userName, logLevel, action, duration }); } catch (error) { console.log(error.toString()); return; } try { const { logFrom, userIp, method, payload, headers, logTarget, userAgent, logStatus, logStatusCode } = params; const customAction = __privateGet(this, _findLogFile) ? __privateGet(this, _findLogFile).call(this, logTarget) : action; let parsedHeaders; let parsedPayload; try { parsedHeaders = typeof headers === "string" ? JSON.parse(headers) : headers; parsedPayload = payload ? typeof payload === "string" ? JSON.parse(payload) : payload : void 0; } catch (parseError) { __privateGet(this, _logger).writeLog({ logLevel: "warn", message: "Failed to parse headers or payload", meta: parseError }); return; } parsedPayload = parsedPayload ? __privateMethod(this, _LoggerService_instances, replaceSensitiveKeys_fn).call(this, parsedPayload, __privateGet(this, _crypt)) : parsedPayload; parsedHeaders = __privateMethod(this, _LoggerService_instances, replaceSensitiveKeys_fn).call(this, parsedHeaders, __privateGet(this, _crypt)); const logData = { logLevel: logLevel.toLowerCase(), message: customAction, meta: { "client.ip": logFrom, "source.name": userIp, "user.name": userName, "event.category": logLevel, "event.action": customAction, "http.request.method": method == null ? void 0 : method.toUpperCase(), "http.request.body.content": parsedPayload, "http.request.headers": parsedHeaders, "url.path": logTarget, "user_agent.original": userAgent, "http.response.status_code": logStatus, "http.response.status_message": logStatusCode == null ? void 0 : logStatusCode.toUpperCase(), ...duration && { "event.duration": duration } } }; __privateGet(this, _logger).writeLog(logData); } catch (err) { __privateGet(this, _logger).writeLog({ logLevel: "error", message: "Could not log entry, please review your implementation or read the documentation and try again.", meta: err }); } } } _crypt = new WeakMap(); _findLogFile = new WeakMap(); _logger = new WeakMap(); _apm = new WeakMap(); _appName = new WeakMap(); _hasInstance = new WeakMap(); _LoggerService_instances = new WeakSet(); /** * Private method to check if the LoggerService has been instantiated. If not, logs a warning and returns `false`. * * @private * @returns {boolean} Returns `true` if the logger service has been initialized, otherwise `false`. */ isInstanciated_fn = function() { if (!__privateGet(this, _hasInstance)) { console.warn("Warning: Logger has not been initialized. Please use `logger.bootstrap` before defining your method."); return false; } return true; }; /** * Updates the logger configuration and reinitializes the logger and APM agent with new settings. * * @private * @param {Object} options - Options to update the logger configuration. * @param {string} [options.appName='app'] - Application name for log identification. * @param {Array<string>} [options.crypt=[]] - List of keys to mask in the log data. * @param {string} [options.logLevel='info'] - Log level for APM configuration. * @param {boolean} [options.startApmAgent=false] - Whether to start the APM agent. * @returns {void} */ update_fn = function({ appName = "app", crypt = [], logLevel = "info", startApmAgent = false }) { __privateSet(this, _appName, appName); __privateSet(this, _crypt, crypt); __privateSet(this, _logger, LoggerFactory.createLogger({ type: "console", logLevel })); __privateSet(this, _apm, Observability.createObservabilityAgent({ ...envConfig.get("apm"), logLevel, crypt, startApmAgent })); }; /** * Replaces sensitive keys in an object with masked values to avoid logging sensitive information. * * @private * @param {Object} obj - The object containing potentially sensitive information. * @param {Array<string>} sensitiveKeys - List of keys to mask in the object. * @returns {Object} A new object with sensitive keys replaced by masked values. */ replaceSensitiveKeys_fn = function(obj, sensitiveKeys) { const MASK = "*".repeat(15); const isJSONParsable = (str) => { if (typeof str !== "string") return false; try { JSON.parse(str); return true; } catch { return false; } }; const replaceKeysRecursive = (data) => { if (typeof data !== "object" || data === null) return data; if (Array.isArray(data)) { return data.map((item) => replaceKeysRecursive(item)); } const result = { ...data }; for (const [key, value] of Object.entries(result)) { if (sensitiveKeys.includes(key)) { result[key] = MASK; } else if (typeof value === "object") { result[key] = replaceKeysRecursive(value); } else if (isJSONParsable(value)) { result[key] = JSON.stringify(replaceKeysRecursive(JSON.parse(value))); } } return result; }; return replaceKeysRecursive({ ...obj }); }; const logger = new LoggerService({}); export { logger }; //# sourceMappingURL=index.esm.js.map