import * as Arr from "../Array.js"; import * as Context from "../Context.js"; import * as FiberRefs from "../FiberRefs.js"; import { constVoid, dual } from "../Function.js"; import { globalValue } from "../GlobalValue.js"; import * as HashMap from "../HashMap.js"; import * as Inspectable from "../Inspectable.js"; import * as List from "../List.js"; import * as Option from "../Option.js"; import { pipeArguments } from "../Pipeable.js"; import * as Cause from "./cause.js"; import * as defaultServices from "./defaultServices.js"; import { consoleTag } from "./defaultServices/console.js"; import * as fiberId_ from "./fiberId.js"; import * as logSpan_ from "./logSpan.js"; /** @internal */ const LoggerSymbolKey = "effect/Logger"; /** @internal */ export const LoggerTypeId = /*#__PURE__*/Symbol.for(LoggerSymbolKey); const loggerVariance = { /* c8 ignore next */ _Message: _ => _, /* c8 ignore next */ _Output: _ => _ }; /** @internal */ export const makeLogger = log => ({ [LoggerTypeId]: loggerVariance, log, pipe() { return pipeArguments(this, arguments); } }); /** @internal */ export const mapInput = /*#__PURE__*/dual(2, (self, f) => makeLogger(options => self.log({ ...options, message: f(options.message) }))); /** @internal */ export const mapInputOptions = /*#__PURE__*/dual(2, (self, f) => makeLogger(options => self.log(f(options)))); /** @internal */ export const filterLogLevel = /*#__PURE__*/dual(2, (self, f) => makeLogger(options => f(options.logLevel) ? Option.some(self.log(options)) : Option.none())); /** @internal */ export const map = /*#__PURE__*/dual(2, (self, f) => makeLogger(options => f(self.log(options)))); /** @internal */ export const none = { [LoggerTypeId]: loggerVariance, log: constVoid, pipe() { return pipeArguments(this, arguments); } }; /** @internal */ export const simple = log => ({ [LoggerTypeId]: loggerVariance, log: ({ message }) => log(message), pipe() { return pipeArguments(this, arguments); } }); /** @internal */ export const succeed = value => { return simple(() => value); }; /** @internal */ export const sync = evaluate => { return simple(evaluate); }; /** @internal */ export const zip = /*#__PURE__*/dual(2, (self, that) => makeLogger(options => [self.log(options), that.log(options)])); /** @internal */ export const zipLeft = /*#__PURE__*/dual(2, (self, that) => map(zip(self, that), tuple => tuple[0])); /** @internal */ export const zipRight = /*#__PURE__*/dual(2, (self, that) => map(zip(self, that), tuple => tuple[1])); /** * Match strings that do not contain any whitespace characters, double quotes, * or equal signs. * * @internal */ const textOnly = /^[^\s"=]*$/; /** * Used by both {@link stringLogger} and {@link logfmtLogger} to render a log * message. * * @internal */ const format = (quoteValue, whitespace) => ({ annotations, cause, date, fiberId, logLevel, message, spans }) => { const formatValue = value => value.match(textOnly) ? value : quoteValue(value); const format = (label, value) => `${logSpan_.formatLabel(label)}=${formatValue(value)}`; const append = (label, value) => " " + format(label, value); let out = format("timestamp", date.toISOString()); out += append("level", logLevel.label); out += append("fiber", fiberId_.threadName(fiberId)); const messages = Arr.ensure(message); for (let i = 0; i < messages.length; i++) { out += append("message", Inspectable.toStringUnknown(messages[i], whitespace)); } if (!Cause.isEmptyType(cause)) { out += append("cause", Cause.pretty(cause, { renderErrorCause: true })); } for (const span of spans) { out += " " + logSpan_.render(date.getTime())(span); } for (const [label, value] of annotations) { out += append(label, Inspectable.toStringUnknown(value, whitespace)); } return out; }; /** @internal */ const escapeDoubleQuotes = s => `"${s.replace(/\\([\s\S])|(")/g, "\\$1$2")}"`; /** @internal */ export const stringLogger = /*#__PURE__*/makeLogger(/*#__PURE__*/format(escapeDoubleQuotes)); /** @internal */ export const logfmtLogger = /*#__PURE__*/makeLogger(/*#__PURE__*/format(JSON.stringify, 0)); /** @internal */ export const structuredLogger = /*#__PURE__*/makeLogger(({ annotations, cause, date, fiberId, logLevel, message, spans }) => { const now = date.getTime(); const annotationsObj = {}; const spansObj = {}; if (HashMap.size(annotations) > 0) { for (const [k, v] of annotations) { annotationsObj[k] = structuredMessage(v); } } if (List.isCons(spans)) { for (const span of spans) { spansObj[span.label] = now - span.startTime; } } const messageArr = Arr.ensure(message); return { message: messageArr.length === 1 ? structuredMessage(messageArr[0]) : messageArr.map(structuredMessage), logLevel: logLevel.label, timestamp: date.toISOString(), cause: Cause.isEmpty(cause) ? undefined : Cause.pretty(cause, { renderErrorCause: true }), annotations: annotationsObj, spans: spansObj, fiberId: fiberId_.threadName(fiberId) }; }); /** @internal */ export const structuredMessage = u => { switch (typeof u) { case "bigint": case "function": case "symbol": { return String(u); } default: { return Inspectable.toJSON(u); } } }; /** @internal */ export const jsonLogger = /*#__PURE__*/map(structuredLogger, Inspectable.stringifyCircular); /** @internal */ export const isLogger = u => { return typeof u === "object" && u != null && LoggerTypeId in u; }; const withColor = (text, ...colors) => { let out = ""; for (let i = 0; i < colors.length; i++) { out += `\x1b[${colors[i]}m`; } return out + text + "\x1b[0m"; }; const withColorNoop = (text, ..._colors) => text; const colors = { bold: "1", red: "31", green: "32", yellow: "33", blue: "34", cyan: "36", white: "37", gray: "90", black: "30", bgBrightRed: "101" }; const logLevelColors = { None: [], All: [], Trace: [colors.gray], Debug: [colors.blue], Info: [colors.green], Warning: [colors.yellow], Error: [colors.red], Fatal: [colors.bgBrightRed, colors.black] }; const logLevelStyle = { None: "", All: "", Trace: "color:gray", Debug: "color:blue", Info: "color:green", Warning: "color:orange", Error: "color:red", Fatal: "background-color:red;color:white" }; const defaultDateFormat = date => `${date.getHours().toString().padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}:${date.getSeconds().toString().padStart(2, "0")}.${date.getMilliseconds().toString().padStart(3, "0")}`; const hasProcessStdout = typeof process === "object" && process !== null && typeof process.stdout === "object" && process.stdout !== null; const processStdoutIsTTY = hasProcessStdout && process.stdout.isTTY === true; const hasProcessStdoutOrDeno = hasProcessStdout || "Deno" in globalThis; /** @internal */ export const prettyLogger = options => { const mode_ = options?.mode ?? "auto"; const mode = mode_ === "auto" ? hasProcessStdoutOrDeno ? "tty" : "browser" : mode_; const isBrowser = mode === "browser"; const showColors = typeof options?.colors === "boolean" ? options.colors : processStdoutIsTTY || isBrowser; const formatDate = options?.formatDate ?? defaultDateFormat; return isBrowser ? prettyLoggerBrowser({ colors: showColors, formatDate }) : prettyLoggerTty({ colors: showColors, formatDate, stderr: options?.stderr === true }); }; const prettyLoggerTty = options => { const processIsBun = typeof process === "object" && "isBun" in process && process.isBun === true; const color = options.colors ? withColor : withColorNoop; return makeLogger(({ annotations, cause, context, date, fiberId, logLevel, message: message_, spans }) => { const services = FiberRefs.getOrDefault(context, defaultServices.currentServices); const console = Context.get(services, consoleTag).unsafe; const log = options.stderr === true ? console.error : console.log; const message = Arr.ensure(message_); let firstLine = color(`[${options.formatDate(date)}]`, colors.white) + ` ${color(logLevel.label, ...logLevelColors[logLevel._tag])}` + ` (${fiberId_.threadName(fiberId)})`; if (List.isCons(spans)) { const now = date.getTime(); const render = logSpan_.render(now); for (const span of spans) { firstLine += " " + render(span); } } firstLine += ":"; let messageIndex = 0; if (message.length > 0) { const firstMaybeString = structuredMessage(message[0]); if (typeof firstMaybeString === "string") { firstLine += " " + color(firstMaybeString, colors.bold, colors.cyan); messageIndex++; } } log(firstLine); if (!processIsBun) console.group(); if (!Cause.isEmpty(cause)) { log(Cause.pretty(cause, { renderErrorCause: true })); } if (messageIndex < message.length) { for (; messageIndex < message.length; messageIndex++) { log(Inspectable.redact(message[messageIndex])); } } if (HashMap.size(annotations) > 0) { for (const [key, value] of annotations) { log(color(`${key}:`, colors.bold, colors.white), Inspectable.redact(value)); } } if (!processIsBun) console.groupEnd(); }); }; const prettyLoggerBrowser = options => { const color = options.colors ? "%c" : ""; return makeLogger(({ annotations, cause, context, date, fiberId, logLevel, message: message_, spans }) => { const services = FiberRefs.getOrDefault(context, defaultServices.currentServices); const console = Context.get(services, consoleTag).unsafe; const message = Arr.ensure(message_); let firstLine = `${color}[${options.formatDate(date)}]`; const firstParams = []; if (options.colors) { firstParams.push("color:gray"); } firstLine += ` ${color}${logLevel.label}${color} (${fiberId_.threadName(fiberId)})`; if (options.colors) { firstParams.push(logLevelStyle[logLevel._tag], ""); } if (List.isCons(spans)) { const now = date.getTime(); const render = logSpan_.render(now); for (const span of spans) { firstLine += " " + render(span); } } firstLine += ":"; let messageIndex = 0; if (message.length > 0) { const firstMaybeString = structuredMessage(message[0]); if (typeof firstMaybeString === "string") { firstLine += ` ${color}${firstMaybeString}`; if (options.colors) { firstParams.push("color:deepskyblue"); } messageIndex++; } } console.groupCollapsed(firstLine, ...firstParams); if (!Cause.isEmpty(cause)) { console.error(Cause.pretty(cause, { renderErrorCause: true })); } if (messageIndex < message.length) { for (; messageIndex < message.length; messageIndex++) { console.log(Inspectable.redact(message[messageIndex])); } } if (HashMap.size(annotations) > 0) { for (const [key, value] of annotations) { const redacted = Inspectable.redact(value); if (options.colors) { console.log(`%c${key}:`, "color:gray", redacted); } else { console.log(`${key}:`, redacted); } } } console.groupEnd(); }); }; /** @internal */ export const prettyLoggerDefault = /*#__PURE__*/globalValue("effect/Logger/prettyLoggerDefault", () => prettyLogger()); //# sourceMappingURL=logger.js.map