Skip to content
Effect 4.0 is here. One ecosystem. Zero dependencies.

Console

Wraps console operations in Effect.

The Console service exposes common console methods such as logging, warnings, errors, groups, counters, tables, and timers. Because console access goes through a service, programs can use custom console implementations in tests or other environments. This module also includes scoped helpers that close console groups or timers automatically.

21 exports Since v2.0.0 Source

Accessors

Since v2.0.0

Writes the supplied assertion message to the console as an error when condition is false; when condition is true, no console output is produced.

Signature

declare function assert(condition: boolean, ...args: readonly Array<any>): Effect<void>

Example Logging failed assertions

import { Console, Effect } from "effect"
const errors: Array<unknown> = []
const testConsole: Console.Console = Object.assign(Object.create(console), {
assert: (condition: boolean, ...args: ReadonlyArray<unknown>) => {
if (!condition) errors.push(...args)
}
})
const program = Effect.gen(function*() {
yield* Console.assert(2 + 2 === 4, "Math is working correctly")
yield* Console.assert(2 + 2 === 5, "This will be logged as an error")
})
Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
errors // => ["This will be logged as an error"]
Since v2.0.0

Runs the current console service's clear operation.

When to use

Use to request that the active console implementation clear its visible output.

Gotchas

The clearing behavior depends on the active console implementation and host environment.

Signature

declare const clear: Effect.Effect<void>

Example Clearing console output

import { Console, Effect } from "effect"
const operations: Array<string> = []
const testConsole: Console.Console = Object.assign(Object.create(console), {
log: (message: string) => operations.push(`log:${message}`),
clear: () => operations.push("clear")
})
const program = Effect.gen(function*() {
yield* Console.log("This will be cleared")
yield* Console.clear
yield* Console.log("This appears after clearing")
})
Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
operations // => ["log:This will be cleared", "clear", "log:This appears after clearing"]
Since v2.0.0

Logs and increments the counter associated with label, using the console's default counter when no label is provided.

Signature

declare function count(label?: string): Effect<void>

Example Counting repeated calls

import { Console, Effect } from "effect"
const counters = new Map<string, number>()
const messages: Array<string> = []
const testConsole: Console.Console = Object.assign(Object.create(console), {
count: (label = "default") => {
const count = (counters.get(label) ?? 0) + 1
counters.set(label, count)
messages.push(`${label}: ${count}`)
}
})
const program = Effect.gen(function*() {
yield* Console.count("my-counter")
yield* Console.count("my-counter")
yield* Console.count()
})
Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
messages // => ["my-counter: 1", "my-counter: 2", "default: 1"]

Resets the counter associated with the specified label back to zero.

Signature

declare function countReset(label?: string): Effect<void>

Example Resetting a counter

import { Console, Effect } from "effect"
const counters = new Map<string, number>()
const messages: Array<string> = []
const testConsole: Console.Console = Object.assign(Object.create(console), {
count: (label = "default") => {
const count = (counters.get(label) ?? 0) + 1
counters.set(label, count)
messages.push(`${label}: ${count}`)
},
countReset: (label = "default") => counters.set(label, 0)
})
const program = Effect.gen(function*() {
yield* Console.count("my-counter")
yield* Console.count("my-counter")
yield* Console.countReset("my-counter")
yield* Console.count("my-counter")
})
Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
messages // => ["my-counter: 1", "my-counter: 2", "my-counter: 1"]
Since v2.0.0

Writes a debug message through the current Console service.

Details

The arguments are passed to the service's debug method when the returned Effect is executed. Any filtering behavior depends on the active console implementation.

Signature

declare function debug(...args: readonly Array<any>): Effect<void>

Example Writing debug messages

import { Console, Effect } from "effect"
const messages: Array<ReadonlyArray<unknown>> = []
const testConsole: Console.Console = Object.assign(Object.create(console), {
debug: (...args: ReadonlyArray<unknown>) => messages.push(args)
})
const program = Effect.gen(function*() {
yield* Console.debug("Debug info:", { userId: 123, action: "login" })
yield* Console.debug("Processing step", 1, "of", 5)
})
Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
messages // => [["Debug info:", { userId: 123, action: "login" }], ["Processing step", 1, "of", 5]]
Since v2.0.0

Displays an interactive list of the properties of the specified object, optionally using console-specific inspection options for debugging complex data structures.

Signature

declare function dir(item: any, options?: any): Effect<void>

Example Inspecting an object

import { Console, Effect } from "effect"
const inspected: Array<unknown> = []
const testConsole: Console.Console = Object.assign(Object.create(console), {
dir: (item: unknown, options?: unknown) => inspected.push([item, options])
})
const program = Effect.gen(function*() {
const obj = { name: "John", age: 30, nested: { city: "New York" } }
yield* Console.dir(obj)
yield* Console.dir(obj, { depth: 2 })
})
Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
const expected = [
[{ name: "John", age: 30, nested: { city: "New York" } }, undefined],
[{ name: "John", age: 30, nested: { city: "New York" } }, { depth: 2 }]
]
inspected // => expected
Since v2.0.0

Displays an interactive tree of descendant XML or HTML elements, which is particularly useful for inspecting DOM elements in browser environments.

Signature

declare function dirxml(...args: readonly Array<any>): Effect<void>

Example Inspecting XML-like data

import { Console, Effect } from "effect"
const messages: Array<unknown> = []
const testConsole: Console.Console = Object.assign(Object.create(console), {
dirxml: (...args: ReadonlyArray<unknown>) => messages.push(...args)
})
const program = Effect.gen(function*() {
yield* Console.dirxml("<user id=\"1\">Ada</user>")
})
Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
messages // => ["<user id=\"1\">Ada</user>"]
Since v2.0.0

Writes an error-level message to the console, typically displayed with error styling by the active console implementation.

Signature

declare function error(...args: readonly Array<any>): Effect<void>

Example Writing error messages

import { Console, Effect } from "effect"
const messages: Array<ReadonlyArray<unknown>> = []
const testConsole: Console.Console = Object.assign(Object.create(console), {
error: (...args: ReadonlyArray<unknown>) => messages.push(args)
})
const program = Effect.gen(function*() {
yield* Console.error("Something went wrong!")
yield* Console.error("Error details:", {
code: 500,
message: "Internal Server Error"
})
})
Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
const expected = [
["Something went wrong!"],
["Error details:", { code: 500, message: "Internal Server Error" }]
]
messages // => expected
Since v2.0.0

Creates a scoped console group, optionally collapsed and labeled, and closes it automatically when the Effect scope is finalized.

Signature

declare function group(options?: {
collapsed?: boolean;
label?: string;
}): Effect<void, never, Scope>

Example Grouping scoped output

import { Console, Effect } from "effect"
const operations: Array<string> = []
const testConsole: Console.Console = Object.assign(Object.create(console), {
group: (label?: string) => operations.push(`group:${label}`),
groupEnd: () => operations.push("groupEnd"),
log: (message: string) => operations.push(`log:${message}`)
})
const program = Effect.gen(function*() {
yield* Effect.scoped(
Effect.gen(function*() {
yield* Console.group({ label: "User Processing" })
yield* Console.log("Loading user data...")
yield* Console.log("Validating user...")
yield* Console.log("User processed successfully")
})
)
})
Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
const expected = [
"group:User Processing",
"log:Loading user data...",
"log:Validating user...",
"log:User processed successfully",
"groupEnd"
]
operations // => expected
Since v2.0.0

Writes an informational message to the console, typically displayed with info styling by the active console implementation.

Signature

declare function info(...args: readonly Array<any>): Effect<void>

Example Writing informational messages

import { Console, Effect } from "effect"
const messages: Array<ReadonlyArray<unknown>> = []
const testConsole: Console.Console = Object.assign(Object.create(console), {
info: (...args: ReadonlyArray<unknown>) => messages.push(args)
})
const program = Effect.gen(function*() {
yield* Console.info("Application started successfully")
yield* Console.info("Server configuration:", {
port: 3000,
env: "development"
})
})
Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
const expected = [
["Application started successfully"],
["Server configuration:", { port: 3000, env: "development" }]
]
messages // => expected
Since v2.0.0

Logs a general-purpose message to the console.

Signature

declare function log(...args: readonly Array<any>): Effect<void>

Example Writing log messages

import { Console, Effect } from "effect"
const messages: Array<ReadonlyArray<unknown>> = []
const testConsole: Console.Console = Object.assign(Object.create(console), {
log: (...args: ReadonlyArray<unknown>) => messages.push(args)
})
const program = Effect.gen(function*() {
yield* Console.log("Hello, world!")
yield* Console.log("User data:", { name: "John", age: 30 })
yield* Console.log("Processing", 42, "items")
})
Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
const expected = [
["Hello, world!"],
["User data:", { name: "John", age: 30 }],
["Processing", 42, "items"]
]
messages // => expected
Since v2.0.0

Displays tabular data as a formatted table in the console, optionally limited to selected properties.

Signature

declare function table(tabularData: any, properties?: readonly Array<string>): Effect<void>

Example Displaying tabular data

import { Console, Effect } from "effect"
const calls: Array<unknown> = []
const testConsole: Console.Console = Object.assign(Object.create(console), {
table: (data: ReadonlyArray<unknown>, properties?: ReadonlyArray<string>) => {
calls.push({ rows: data.length, properties })
}
})
const program = Effect.gen(function*() {
const users = [
{ name: "John", age: 30, city: "New York" },
{ name: "Jane", age: 25, city: "London" },
{ name: "Bob", age: 35, city: "Paris" }
]
yield* Console.table(users)
yield* Console.table(users, ["name", "age"]) // Only show specific columns
})
Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
calls // => [{ rows: 3, properties: undefined }, { rows: 3, properties: ["name", "age"] }]
Since v2.0.0

Starts a scoped timer for label and automatically ends it when the Effect scope is finalized.

Signature

declare function time(label?: string): Effect<void, never, Scope>

Example Timing scoped work

import { Console, Effect } from "effect"
const operations: Array<string> = []
const testConsole: Console.Console = Object.assign(Object.create(console), {
time: (label?: string) => operations.push(`start:${label}`),
timeEnd: (label?: string) => operations.push(`end:${label}`),
log: (message: string) => operations.push(`log:${message}`)
})
const program = Effect.gen(function*() {
yield* Effect.scoped(
Effect.gen(function*() {
yield* Console.time("operation-timer")
yield* Console.log("Operation completed")
// Timer ends automatically when scope closes
})
)
})
Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
operations // => ["start:operation-timer", "log:Operation completed", "end:operation-timer"]
Since v2.0.0

Logs the elapsed time for an existing timer without stopping it, allowing progress reports for long-running operations.

Signature

declare function timeLog(label?: string, ...args: readonly Array<any>): Effect<void>

Example Logging timer progress

import { Console, Effect } from "effect"
const operations: Array<unknown> = []
const testConsole: Console.Console = Object.assign(Object.create(console), {
time: (label?: string) => operations.push(["start", label]),
timeLog: (label?: string, ...args: ReadonlyArray<unknown>) => operations.push(["log", label, ...args]),
timeEnd: (label?: string) => operations.push(["end", label])
})
const program = Effect.gen(function*() {
yield* Effect.scoped(
Effect.gen(function*() {
yield* Console.time("long-operation")
yield* Console.timeLog("long-operation", "Halfway done")
// Timer ends when scope closes
})
)
})
Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
operations // => [["start", "long-operation"], ["log", "long-operation", "Halfway done"], ["end", "long-operation"]]
Since v2.0.0

Writes the current stack trace to the console to show how the current point in the code was reached.

Signature

declare function trace(...args: readonly Array<any>): Effect<void>

Example Writing stack traces

import { Console, Effect } from "effect"
const traces: Array<ReadonlyArray<unknown>> = []
const testConsole: Console.Console = Object.assign(Object.create(console), {
trace: (...args: ReadonlyArray<unknown>) => traces.push(args)
})
const program = Effect.gen(function*() {
yield* Console.trace("Debug trace point")
yield* Console.trace("Function call:", { functionName: "processData" })
})
Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
traces // => [["Debug trace point"], ["Function call:", { functionName: "processData" }]]
Since v2.0.0

Writes a warning-level message to the console, typically displayed with warning styling by the active console implementation.

Signature

declare function warn(...args: readonly Array<any>): Effect<void>

Example Writing warning messages

import { Console, Effect } from "effect"
const messages: Array<ReadonlyArray<unknown>> = []
const testConsole: Console.Console = Object.assign(Object.create(console), {
warn: (...args: ReadonlyArray<unknown>) => messages.push(args)
})
const program = Effect.gen(function*() {
yield* Console.warn("This feature is deprecated")
yield* Console.warn("Performance warning:", {
slowQuery: "SELECT * FROM large_table"
})
})
Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
const expected = [
["This feature is deprecated"],
["Performance warning:", { slowQuery: "SELECT * FROM large_table" }]
]
messages // => expected

Runs an Effect inside an optionally labeled or collapsed console group, starting the group before execution and ending it after the Effect completes.

Signature

declare const withGroup: (options?: {
readonly collapsed?: boolean;
readonly label?: string;
}) => <A, E, R>(self: Effect<A, E, R>) => Effect<A, E, R> & <A, E, R>(self: Effect<A, E, R>, options?: {
readonly collapsed?: boolean;
readonly label?: string;
}) => Effect<A, E, R>

Example Wrapping an effect in a group

import { Console, Effect } from "effect"
const operations: Array<string> = []
const testConsole: Console.Console = Object.assign(Object.create(console), {
group: (label?: string) => operations.push(`group:${label}`),
groupEnd: () => operations.push("groupEnd"),
log: (message: string) => operations.push(`log:${message}`)
})
const program = Effect.gen(function*() {
yield* Console.withGroup(
Effect.gen(function*() {
yield* Console.log("Step 1: Initialize")
yield* Console.log("Step 2: Process")
yield* Console.log("Step 3: Complete")
}),
{ label: "Processing Steps", collapsed: false }
)
})
Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
const expected = [
"group:Processing Steps",
"log:Step 1: Initialize",
"log:Step 2: Process",
"log:Step 3: Complete",
"groupEnd"
]
operations // => expected
Since v2.0.0

Runs an Effect with a console timer, starting the timer before execution and ending it after the Effect completes.

Signature

declare const withTime: (label?: string) => <A, E, R>(self: Effect<A, E, R>) => Effect<A, E, R> & <A, E, R>(self: Effect<A, E, R>, label?: string) => Effect<A, E, R>

Example Timing an effect

import { Console, Effect } from "effect"
const operations: Array<string> = []
const testConsole: Console.Console = Object.assign(Object.create(console), {
time: (label?: string) => operations.push(`start:${label}`),
timeEnd: (label?: string) => operations.push(`end:${label}`),
log: (message: string) => operations.push(`log:${message}`)
})
const program = Effect.gen(function*() {
yield* Console.withTime(
Effect.gen(function*() {
yield* Console.log("Operation completed")
}),
"my-operation"
)
})
Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
operations // => ["start:my-operation", "log:Operation completed", "end:my-operation"]

Constructors

Creates an Effect that provides access to the current console service and lets you perform operations with it within an Effect context.

Signature

declare function consoleWith<A, E, R>(f: (console: Console) => Effect<A, E, R>): Effect<A, E, R>

Example Accessing the current console service

import { Console, Effect } from "effect"
const messages: Array<unknown> = []
const testConsole: Console.Console = Object.assign(Object.create(console), {
log: (...args: ReadonlyArray<unknown>) => messages.push(...args),
error: (...args: ReadonlyArray<unknown>) => messages.push(...args)
})
const program = Console.consoleWith((console) =>
Effect.sync(() => {
console.log("Hello, world!")
console.error("This is an error message")
})
)
Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
messages // => ["Hello, world!", "This is an error message"]

Services

Since v2.0.0

Context reference for the current console service in the Effect system, allowing access to the active console implementation from within the Effect context.

When to use

Use when you need an effect to run against a provided console implementation, such as tests or alternate runtimes, rather than the default console.

Details

When no override is provided, the reference resolves to globalThis.console.

See

  • consoleWith for using the current console service inside an effect

Signature

declare const Console: Reference<Console>

Example Accessing the current console

import { Console, Effect } from "effect"
const messages: Array<unknown> = []
const testConsole: Console.Console = Object.assign(Object.create(console), {
log: (...args: ReadonlyArray<unknown>) => messages.push(...args)
})
const program = Console.consoleWith((console) =>
Effect.sync(() => {
console.log("Hello from current console!")
})
)
Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
messages // => ["Hello from current console!"]

Console

interface
Since v2.0.0

Represents a console interface for logging, debugging, timing, and grouping output.

Signature

interface Console {
assert(condition: boolean, ...args: readonly Array<any>): void;
clear(): void;
count(label?: string): void;
countReset(label?: string): void;
debug(...args: readonly Array<any>): void;
dir(item: any, options?: any): void;
dirxml(...args: readonly Array<any>): void;
error(...args: readonly Array<any>): void;
group(...args: readonly Array<any>): void;
groupCollapsed(...args: readonly Array<any>): void;
groupEnd(): void;
info(...args: readonly Array<any>): void;
log(...args: readonly Array<any>): void;
table(tabularData: any, properties?: readonly Array<string>): void;
time(label?: string): void;
timeEnd(label?: string): void;
timeLog(label?: string, ...args: readonly Array<any>): void;
trace(...args: readonly Array<any>): void;
warn(...args: readonly Array<any>): void;
}