/** * @since 2.0.0 */ import type * as Cause from "./Cause.js" import type { DurationInput } from "./Duration.js" import type { Effect } from "./Effect.js" import type * as FiberId from "./FiberId.js" import type * as FiberRefs from "./FiberRefs.js" import type { LazyArg } from "./Function.js" import type * as HashMap from "./HashMap.js" import * as fiberRuntime from "./internal/fiberRuntime.js" import * as circular from "./internal/layer/circular.js" import * as internalCircular from "./internal/logger-circular.js" import * as internal from "./internal/logger.js" import type * as Layer from "./Layer.js" import type * as List from "./List.js" import type * as LogLevel from "./LogLevel.js" import type * as LogSpan from "./LogSpan.js" import type * as Option from "./Option.js" import type { Pipeable } from "./Pipeable.js" import type { Scope } from "./Scope.js" import type * as Types from "./Types.js" /** * @since 2.0.0 * @category symbols */ export const LoggerTypeId: unique symbol = internal.LoggerTypeId /** * @since 2.0.0 * @category symbols */ export type LoggerTypeId = typeof LoggerTypeId /** * @since 2.0.0 * @category models */ export interface Logger extends Logger.Variance, Pipeable { log(options: Logger.Options): Output } /** * @since 2.0.0 */ export declare namespace Logger { /** * @since 2.0.0 * @category models */ export interface Variance { readonly [LoggerTypeId]: { readonly _Message: Types.Contravariant readonly _Output: Types.Covariant } } /** * @since 2.0.0 * @category models */ export interface Options { readonly fiberId: FiberId.FiberId readonly logLevel: LogLevel.LogLevel readonly message: Message readonly cause: Cause.Cause readonly context: FiberRefs.FiberRefs readonly spans: List.List readonly annotations: HashMap.HashMap readonly date: Date } } /** * Creates a custom logger that formats log messages according to the provided * function. * * @example * ```ts * import { Effect, Logger, LogLevel } from "effect" * * const logger = Logger.make(({ logLevel, message }) => { * globalThis.console.log(`[${logLevel.label}] ${message}`) * }) * * const task1 = Effect.logDebug("task1 done") * const task2 = Effect.logDebug("task2 done") * * const program = Effect.gen(function*() { * yield* Effect.log("start") * yield* task1 * yield* task2 * yield* Effect.log("done") * }).pipe( * Logger.withMinimumLogLevel(LogLevel.Debug), * Effect.provide(Logger.replace(Logger.defaultLogger, logger)) * ) * * Effect.runFork(program) * // [INFO] start * // [DEBUG] task1 done * // [DEBUG] task2 done * // [INFO] done * ``` * * @category constructors * @since 2.0.0 */ export const make: (log: (options: Logger.Options) => Output) => Logger = internal.makeLogger /** * @since 2.0.0 * @category context */ export const add: (logger: Logger) => Layer.Layer = circular.addLogger /** * @since 2.0.0 * @category context */ export const addEffect: (effect: Effect, E, R>) => Layer.Layer = circular.addLoggerEffect /** * @since 2.0.0 * @category context */ export const addScoped: ( effect: Effect, E, R> ) => Layer.Layer> = circular.addLoggerScoped /** * @since 2.0.0 * @category mapping */ export const mapInput: { /** * @since 2.0.0 * @category mapping */ (f: (message: Message2) => Message): (self: Logger) => Logger /** * @since 2.0.0 * @category mapping */ (self: Logger, f: (message: Message2) => Message): Logger } = internal.mapInput /** * @since 2.0.0 * @category mapping */ export const mapInputOptions: { /** * @since 2.0.0 * @category mapping */ (f: (options: Logger.Options) => Logger.Options): (self: Logger) => Logger /** * @since 2.0.0 * @category mapping */ ( self: Logger, f: (options: Logger.Options) => Logger.Options ): Logger } = internal.mapInputOptions /** * Returns a version of this logger that only logs messages when the log level * satisfies the specified predicate. * * @since 2.0.0 * @category filtering */ export const filterLogLevel: { /** * Returns a version of this logger that only logs messages when the log level * satisfies the specified predicate. * * @since 2.0.0 * @category filtering */ (f: (logLevel: LogLevel.LogLevel) => boolean): (self: Logger) => Logger> /** * Returns a version of this logger that only logs messages when the log level * satisfies the specified predicate. * * @since 2.0.0 * @category filtering */ (self: Logger, f: (logLevel: LogLevel.LogLevel) => boolean): Logger> } = internal.filterLogLevel /** * @since 2.0.0 * @category mapping */ export const map: { /** * @since 2.0.0 * @category mapping */ (f: (output: Output) => Output2): (self: Logger) => Logger /** * @since 2.0.0 * @category mapping */ (self: Logger, f: (output: Output) => Output2): Logger } = internal.map /** * Creates a batched logger that groups log messages together and processes them * in intervals. * * @example * ```ts * import { Console, Effect, Logger } from "effect" * * const LoggerLive = Logger.replaceScoped( * Logger.defaultLogger, * Logger.logfmtLogger.pipe( * Logger.batched("500 millis", (messages) => Console.log("BATCH", `[\n${messages.join("\n")}\n]`)) * ) * ) * * const program = Effect.gen(function*() { * yield* Effect.log("one") * yield* Effect.log("two") * yield* Effect.log("three") * }).pipe(Effect.provide(LoggerLive)) * * Effect.runFork(program) * // BATCH [ * // timestamp=... level=INFO fiber=#0 message=one * // timestamp=... level=INFO fiber=#0 message=two * // timestamp=... level=INFO fiber=#0 message=three * // ] * ``` * * @since 2.0.0 * @category mapping */ export const batched: { /** * Creates a batched logger that groups log messages together and processes them * in intervals. * * @example * ```ts * import { Console, Effect, Logger } from "effect" * * const LoggerLive = Logger.replaceScoped( * Logger.defaultLogger, * Logger.logfmtLogger.pipe( * Logger.batched("500 millis", (messages) => Console.log("BATCH", `[\n${messages.join("\n")}\n]`)) * ) * ) * * const program = Effect.gen(function*() { * yield* Effect.log("one") * yield* Effect.log("two") * yield* Effect.log("three") * }).pipe(Effect.provide(LoggerLive)) * * Effect.runFork(program) * // BATCH [ * // timestamp=... level=INFO fiber=#0 message=one * // timestamp=... level=INFO fiber=#0 message=two * // timestamp=... level=INFO fiber=#0 message=three * // ] * ``` * * @since 2.0.0 * @category mapping */ ( window: DurationInput, f: (messages: Array>) => Effect ): (self: Logger) => Effect, never, R | Scope> /** * Creates a batched logger that groups log messages together and processes them * in intervals. * * @example * ```ts * import { Console, Effect, Logger } from "effect" * * const LoggerLive = Logger.replaceScoped( * Logger.defaultLogger, * Logger.logfmtLogger.pipe( * Logger.batched("500 millis", (messages) => Console.log("BATCH", `[\n${messages.join("\n")}\n]`)) * ) * ) * * const program = Effect.gen(function*() { * yield* Effect.log("one") * yield* Effect.log("two") * yield* Effect.log("three") * }).pipe(Effect.provide(LoggerLive)) * * Effect.runFork(program) * // BATCH [ * // timestamp=... level=INFO fiber=#0 message=one * // timestamp=... level=INFO fiber=#0 message=two * // timestamp=... level=INFO fiber=#0 message=three * // ] * ``` * * @since 2.0.0 * @category mapping */ ( self: Logger, window: DurationInput, f: (messages: Array>) => Effect ): Effect, never, Scope | R> } = fiberRuntime.batchedLogger /** * @since 2.0.0 * @category console */ export const withConsoleLog: (self: Logger) => Logger = fiberRuntime.loggerWithConsoleLog /** * Takes a `Logger` and returns a logger that calls the respective `Console` method * based on the log level. * * @example * ```ts * import { Logger, Effect } from "effect" * * const loggerLayer = Logger.replace( * Logger.defaultLogger, * Logger.withLeveledConsole(Logger.stringLogger), * ) * * Effect.gen(function* () { * yield* Effect.logError("an error") * yield* Effect.logInfo("an info") * }).pipe(Effect.provide(loggerLayer)) * ``` * * @since 3.8.0 * @category console */ export const withLeveledConsole: (self: Logger) => Logger = fiberRuntime.loggerWithLeveledLog /** * @since 2.0.0 * @category console */ export const withConsoleError: (self: Logger) => Logger = fiberRuntime.loggerWithConsoleError /** * A logger that does nothing in response to logging events. * * @since 2.0.0 * @category constructors */ export const none: Logger = internal.none /** * @since 2.0.0 * @category context */ export const remove: (logger: Logger) => Layer.Layer = circular.removeLogger /** * @since 2.0.0 * @category context */ export const replace: { /** * @since 2.0.0 * @category context */ (that: Logger): (self: Logger) => Layer.Layer /** * @since 2.0.0 * @category context */ (self: Logger, that: Logger): Layer.Layer } = circular.replaceLogger /** * @since 2.0.0 * @category context */ export const replaceEffect: { /** * @since 2.0.0 * @category context */ (that: Effect, E, R>): (self: Logger) => Layer.Layer /** * @since 2.0.0 * @category context */ (self: Logger, that: Effect, E, R>): Layer.Layer } = circular.replaceLoggerEffect /** * @since 2.0.0 * @category context */ export const replaceScoped: { /** * @since 2.0.0 * @category context */ (that: Effect, E, R>): (self: Logger) => Layer.Layer> /** * @since 2.0.0 * @category context */ (self: Logger, that: Effect, E, R>): Layer.Layer> } = circular.replaceLoggerScoped /** * @since 2.0.0 * @category constructors */ export const simple: (log: (a: A) => B) => Logger = internal.simple /** * @since 2.0.0 * @category constructors */ export const succeed: (value: A) => Logger = internal.succeed /** * @since 2.0.0 * @category constructors */ export const sync: (evaluate: LazyArg) => Logger = internal.sync /** * @since 2.0.0 * @category constructors */ export const test: { /** * @since 2.0.0 * @category constructors */ (input: Message): (self: Logger) => Output /** * @since 2.0.0 * @category constructors */ (self: Logger, input: Message): Output } = internalCircular.test /** * Sets the minimum log level for subsequent logging operations, allowing * control over which log messages are displayed based on their severity. * * @example * ```ts * import { Effect, Logger, LogLevel } from "effect" * * const program = Effect.logDebug("message1").pipe(Logger.withMinimumLogLevel(LogLevel.Debug)) * * Effect.runFork(program) * // timestamp=... level=DEBUG fiber=#0 message=message1 * ``` * * @since 2.0.0 * @category context */ export const withMinimumLogLevel: { /** * Sets the minimum log level for subsequent logging operations, allowing * control over which log messages are displayed based on their severity. * * @example * ```ts * import { Effect, Logger, LogLevel } from "effect" * * const program = Effect.logDebug("message1").pipe(Logger.withMinimumLogLevel(LogLevel.Debug)) * * Effect.runFork(program) * // timestamp=... level=DEBUG fiber=#0 message=message1 * ``` * * @since 2.0.0 * @category context */ (level: LogLevel.LogLevel): (self: Effect) => Effect /** * Sets the minimum log level for subsequent logging operations, allowing * control over which log messages are displayed based on their severity. * * @example * ```ts * import { Effect, Logger, LogLevel } from "effect" * * const program = Effect.logDebug("message1").pipe(Logger.withMinimumLogLevel(LogLevel.Debug)) * * Effect.runFork(program) * // timestamp=... level=DEBUG fiber=#0 message=message1 * ``` * * @since 2.0.0 * @category context */ (self: Effect, level: LogLevel.LogLevel): Effect } = circular.withMinimumLogLevel /** * @since 2.0.0 * @category tracing */ export const withSpanAnnotations: (self: Logger) => Logger = fiberRuntime.loggerWithSpanAnnotations /** * Combines this logger with the specified logger to produce a new logger that * logs to both this logger and that logger. * * @since 2.0.0 * @category zipping */ export const zip: { /** * Combines this logger with the specified logger to produce a new logger that * logs to both this logger and that logger. * * @since 2.0.0 * @category zipping */ (that: Logger): (self: Logger) => Logger /** * Combines this logger with the specified logger to produce a new logger that * logs to both this logger and that logger. * * @since 2.0.0 * @category zipping */ (self: Logger, that: Logger): Logger } = internal.zip /** * @since 2.0.0 * @category zipping */ export const zipLeft: { /** * @since 2.0.0 * @category zipping */ (that: Logger): (self: Logger) => Logger /** * @since 2.0.0 * @category zipping */ (self: Logger, that: Logger): Logger } = internal.zipLeft /** * @since 2.0.0 * @category zipping */ export const zipRight: { /** * @since 2.0.0 * @category zipping */ (that: Logger): (self: Logger) => Logger /** * @since 2.0.0 * @category zipping */ (self: Logger, that: Logger): Logger } = internal.zipRight /** * @since 2.0.0 * @category constructors */ export const defaultLogger: Logger = fiberRuntime.defaultLogger /** * The `jsonLogger` logger formats log entries as JSON objects, making them easy to * integrate with logging systems that consume JSON data. * * @example * ```ts * import { Effect, Logger } from "effect" * * const program = Effect.log("message1", "message2").pipe( * Effect.annotateLogs({ key1: "value1", key2: "value2" }), * Effect.withLogSpan("myspan") * ) * * Effect.runFork(program.pipe(Effect.provide(Logger.json))) * // {"message":["message1","message2"],"logLevel":"INFO","timestamp":"...","annotations":{"key2":"value2","key1":"value1"},"spans":{"myspan":0},"fiberId":"#0"} * ``` * * @since 2.0.0 * @category constructors */ export const jsonLogger: Logger = internal.jsonLogger /** * This logger outputs logs in a human-readable format that is easy to read * during development or in a production console. * * @example * ```ts * import { Effect, Logger } from "effect" * * const program = Effect.log("message1", "message2").pipe( * Effect.annotateLogs({ key1: "value1", key2: "value2" }), * Effect.withLogSpan("myspan") * ) * * Effect.runFork(program.pipe(Effect.provide(Logger.logFmt))) * // timestamp=... level=INFO fiber=#0 message=message1 message=message2 myspan=0ms key2=value2 key1=value1 * ``` * * @since 2.0.0 * @category constructors */ export const logfmtLogger: Logger = internal.logfmtLogger /** * @since 2.0.0 * @category constructors */ export const stringLogger: Logger = internal.stringLogger /** * The pretty logger utilizes the capabilities of the console API to generate * visually engaging and color-enhanced log outputs. This feature is * particularly useful for improving the readability of log messages during * development and debugging processes. * * @example * ```ts * import { Effect, Logger } from "effect" * * const program = Effect.log("message1", "message2").pipe( * Effect.annotateLogs({ key1: "value1", key2: "value2" }), * Effect.withLogSpan("myspan") * ) * * Effect.runFork(program.pipe(Effect.provide(Logger.pretty))) * // green --v v-- bold and cyan * // [07:51:54.434] INFO (#0) myspan=1ms: message1 * // message2 * // v-- bold * // key2: value2 * // key1: value1 * ``` * * @since 3.5.0 * @category constructors */ export const prettyLogger: ( options?: { readonly colors?: "auto" | boolean | undefined readonly stderr?: boolean | undefined readonly formatDate?: ((date: Date) => string) | undefined readonly mode?: "browser" | "tty" | "auto" | undefined } ) => Logger = internal.prettyLogger /** * A default version of the pretty logger. * * @since 3.8.0 * @category constructors */ export const prettyLoggerDefault: Logger = internal.prettyLoggerDefault /** * The structured logger provides detailed log outputs, structured in a way that * retains comprehensive traceability of the events, suitable for deeper * analysis and troubleshooting. * * @example * ```ts * import { Effect, Logger } from "effect" * * const program = Effect.log("message1", "message2").pipe( * Effect.annotateLogs({ key1: "value1", key2: "value2" }), * Effect.withLogSpan("myspan") * ) * * Effect.runFork(program.pipe(Effect.provide(Logger.structured))) * // { * // message: [ 'message1', 'message2' ], * // logLevel: 'INFO', * // timestamp: '2024-07-09T14:05:41.623Z', * // cause: undefined, * // annotations: { key2: 'value2', key1: 'value1' }, * // spans: { myspan: 0 }, * // fiberId: '#0' * // } * ``` * * @since 2.0.0 * @category constructors */ export const structuredLogger: Logger< unknown, { readonly logLevel: string readonly fiberId: string readonly timestamp: string readonly message: unknown readonly cause: string | undefined readonly annotations: Record readonly spans: Record } > = internal.structuredLogger /** * @since 2.0.0 * @category constructors */ export const tracerLogger: Logger = fiberRuntime.tracerLogger /** * The `json` logger formats log entries as JSON objects, making them easy to * integrate with logging systems that consume JSON data. * * @example * ```ts * import { Effect, Logger } from "effect" * * const program = Effect.log("message1", "message2").pipe( * Effect.annotateLogs({ key1: "value1", key2: "value2" }), * Effect.withLogSpan("myspan") * ) * * Effect.runFork(program.pipe(Effect.provide(Logger.json))) * // {"message":["message1","message2"],"logLevel":"INFO","timestamp":"...","annotations":{"key2":"value2","key1":"value1"},"spans":{"myspan":0},"fiberId":"#0"} * ``` * * @since 2.0.0 * @category constructors */ export const json: Layer.Layer = replace(fiberRuntime.defaultLogger, fiberRuntime.jsonLogger) /** * This logger outputs logs in a human-readable format that is easy to read * during development or in a production console. * * @example * ```ts * import { Effect, Logger } from "effect" * * const program = Effect.log("message1", "message2").pipe( * Effect.annotateLogs({ key1: "value1", key2: "value2" }), * Effect.withLogSpan("myspan") * ) * * Effect.runFork(program.pipe(Effect.provide(Logger.logFmt))) * // timestamp=... level=INFO fiber=#0 message=message1 message=message2 myspan=0ms key2=value2 key1=value1 * ``` * * @since 2.0.0 * @category constructors */ export const logFmt: Layer.Layer = replace(fiberRuntime.defaultLogger, fiberRuntime.logFmtLogger) /** * The pretty logger utilizes the capabilities of the console API to generate * visually engaging and color-enhanced log outputs. This feature is * particularly useful for improving the readability of log messages during * development and debugging processes. * * @example * ```ts * import { Effect, Logger } from "effect" * * const program = Effect.log("message1", "message2").pipe( * Effect.annotateLogs({ key1: "value1", key2: "value2" }), * Effect.withLogSpan("myspan") * ) * * Effect.runFork(program.pipe(Effect.provide(Logger.pretty))) * // green --v v-- bold and cyan * // [07:51:54.434] INFO (#0) myspan=1ms: message1 * // message2 * // v-- bold * // key2: value2 * // key1: value1 * ``` * * @since 3.5.0 * @category constructors */ export const pretty: Layer.Layer = replace(fiberRuntime.defaultLogger, fiberRuntime.prettyLogger) /** * The structured logger provides detailed log outputs, structured in a way that * retains comprehensive traceability of the events, suitable for deeper * analysis and troubleshooting. * * @example * ```ts * import { Effect, Logger } from "effect" * * const program = Effect.log("message1", "message2").pipe( * Effect.annotateLogs({ key1: "value1", key2: "value2" }), * Effect.withLogSpan("myspan") * ) * * Effect.runFork(program.pipe(Effect.provide(Logger.structured))) * // { * // message: [ 'message1', 'message2' ], * // logLevel: 'INFO', * // timestamp: '2024-07-09T14:05:41.623Z', * // cause: undefined, * // annotations: { key2: 'value2', key1: 'value1' }, * // spans: { myspan: 0 }, * // fiberId: '#0' * // } * ``` * * @since 2.0.0 * @category constructors */ export const structured: Layer.Layer = replace(fiberRuntime.defaultLogger, fiberRuntime.structuredLogger) /** * Sets the minimum log level for logging operations, allowing control over * which log messages are displayed based on their severity. * * @example * ```ts * import { Effect, Logger, LogLevel } from "effect" * * const program = Effect.gen(function*() { * yield* Effect.log("Executing task...") * yield* Effect.sleep("100 millis") * console.log("task done") * }) * * // Logging disabled using a layer * Effect.runFork(program.pipe(Effect.provide(Logger.minimumLogLevel(LogLevel.None)))) * // task done * ``` * * @since 2.0.0 * @category context */ export const minimumLogLevel: (level: LogLevel.LogLevel) => Layer.Layer = circular.minimumLogLevel /** * Returns `true` if the specified value is a `Logger`, otherwise returns `false`. * * @since 1.0.0 * @category guards */ export const isLogger: (u: unknown) => u is Logger = internal.isLogger