Console
The Console module is the effectful mirror of the global console. Every
method you know from console — log, error, table, group, time, and
friends — has an Effect-returning counterpart that you yield* inside
Effect.gen (or pipe through Effect.tap). The output goes wherever the
active Console reference points, which by default is globalThis.console.
import { Console, Effect } from "effect"
const program = Effect.gen(function*() { yield* Console.log("Hello, world!") yield* Console.info("server starting", { port: 3000 }) yield* Console.warn("inventory is low") yield* Console.error("checkout failed", { code: 500 })})Swapping the Console for tests
Section titled “Swapping the Console for tests”Because the console is held as a Context.Reference,
you can substitute your own implementation with Effect.provideService. This
makes console output capturable and assertable in tests, with no global
patching.
import { Console, Effect } from "effect"
// A minimal capturing console. Only implement the methods you exercise;// cast through the Console interface for the rest.const makeCapture = () => { const lines: Array<string> = [] const capture = { ...globalThis.console, log: (...args: Array<any>) => lines.push(args.join(" ")), error: (...args: Array<any>) => lines.push(`ERROR ${args.join(" ")}`) } as Console.Console return { capture, lines }}
const program = Effect.gen(function*() { yield* Console.log("hello") yield* Console.error("boom")})
const { capture, lines } = makeCapture()
Effect.runSync(program.pipe(Effect.provideService(Console.Console, capture)))
console.log(lines)// => ["hello", "ERROR boom"]Reference
Section titled “Reference”Console (interface)
Section titled “Console (interface)”The shape of a console implementation: the methods assert, clear, count,
countReset, debug, dir, dirxml, error, group, groupCollapsed,
groupEnd, info, log, table, time, timeEnd, timeLog, trace, and
warn. The browser/Node console already satisfies it, which is why it is the
default.
import { Console } from "effect"
// Provide your own implementation by satisfying this interface.const silent: Console.Console = { ...globalThis.console, log: () => {}, error: () => {}}Console (reference)
Section titled “Console (reference)”Console.Console is the Context.Reference holding the active console. It
resolves to globalThis.console unless overridden. Override it with
Effect.provideService(Console.Console, impl) to redirect output.
import { Console, Effect } from "effect"
const program = Console.log("written to the overridden console")
const myConsole = { ...globalThis.console } as Console.Console
Effect.runSync(program.pipe(Effect.provideService(Console.Console, myConsole)))consoleWith
Section titled “consoleWith”Accesses the current console and lets you build an Effect from it directly — useful when you want to call several methods on the raw console in one synchronous block.
import { Console, Effect } from "effect"
const program = Console.consoleWith((console) => Effect.sync(() => { console.log("Hello, world!") console.error("This is an error message") }))Output
Section titled “Output”Logs a general-purpose message. Accepts any number of arguments.
import { Console, Effect } from "effect"
const program = Console.log("User data:", { name: "John", age: 30 })// => User data: { name: 'John', age: 30 }Writes an error-level message, typically styled as an error by the host console.
import { Console } from "effect"
const program = Console.error("Error details:", { code: 500 })// => Error details: { code: 500 }Writes a warning-level message.
import { Console } from "effect"
const program = Console.warn("This feature is deprecated")// => This feature is deprecatedWrites an informational message.
import { Console } from "effect"
const program = Console.info("Server configuration:", { port: 3000 })// => Server configuration: { port: 3000 }Writes a debug-level message. Visibility depends on the host console’s level filtering.
import { Console } from "effect"
const program = Console.debug("Debug info:", { userId: 123, action: "login" })// => Debug info: { userId: 123, action: 'login' }Writes the current stack trace alongside the supplied arguments, showing how the current point in the code was reached.
import { Console } from "effect"
const program = Console.trace("Debug trace point")// => Trace: Debug trace point// => at ...Displays an interactive list of an object’s properties. The optional second
argument forwards inspection options (e.g. { depth, colors }) to the host
console.
import { Console } from "effect"
const obj = { name: "John", nested: { city: "New York" } }
const program = Console.dir(obj, { depth: 2 })// => { name: 'John', nested: { city: 'New York' } }dirxml
Section titled “dirxml”Displays an interactive tree of descendant XML/HTML elements, primarily useful
for inspecting DOM nodes in the browser. Falls back to dir-like output
elsewhere.
import { Console } from "effect"
const program = Console.dirxml("<user id=\"1\">Ada</user>")// => <user id="1">Ada</user>Tables and inspection
Section titled “Tables and inspection”Renders an array or object as a formatted table. The optional second argument restricts the table to the listed property names.
import { Console, Effect } from "effect"
const program = Effect.gen(function*() { const users = [ { name: "John", age: 30, city: "New York" }, { name: "Jane", age: 25, city: "London" } ] yield* Console.table(users) yield* Console.table(users, ["name", "age"]) // only these columns})// => ┌─────────┬────────┬─────┬────────────┐// => │ (index) │ name │ age │ city │// => ├─────────┼────────┼─────┼────────────┤// => │ 0 │ 'John' │ 30 │ 'New York' │// => │ 1 │ 'Jane' │ 25 │ 'London' │// => └─────────┴────────┴─────┴────────────┘assert
Section titled “assert”Writes an assertion error to the console when condition is false; produces
no output when it is true.
import { Console, Effect } from "effect"
const program = Effect.gen(function*() { yield* Console.assert(2 + 2 === 4, "Math is working") // no output yield* Console.assert(2 + 2 === 5, "This is logged as an error")})// => Assertion failed: This is logged as an errorCounters
Section titled “Counters”Logs and increments a named counter. With no label it uses the console’s default counter.
import { Console, Effect } from "effect"
const program = Effect.gen(function*() { yield* Console.count("hits") yield* Console.count("hits")})// => hits: 1// => hits: 2countReset
Section titled “countReset”Resets the named counter back to zero.
import { Console, Effect } from "effect"
const program = Effect.gen(function*() { yield* Console.count("hits") // => hits: 1 yield* Console.count("hits") // => hits: 2 yield* Console.countReset("hits") yield* Console.count("hits") // => hits: 1})Timing
Section titled “Timing”Console.time and Console.group are scoped: they require a Scope and end
the timer / close the group automatically when the scope is finalized. Run them
under Effect.scoped. For the common “time/group exactly this effect” case,
prefer the withTime / withGroup combinators below, which manage the scope
for you.
Starts a console timer for label, ending it automatically when the
surrounding scope closes. Returns Effect<void, never, Scope>.
import { Console, Effect } from "effect"
const program = Effect.scoped( Effect.gen(function*() { yield* Console.time("operation") yield* Effect.sleep("1 second") yield* Console.log("done") // timer ends here, when the scope closes }))// => done// => operation: 1004.218mstimeLog
Section titled “timeLog”Logs the elapsed time of an existing timer without stopping it — handy for
progress reports inside a time scope.
import { Console, Effect } from "effect"
const program = Effect.scoped( Effect.gen(function*() { yield* Console.time("job") yield* Effect.sleep("500 millis") yield* Console.timeLog("job", "halfway") yield* Effect.sleep("500 millis") }))// => job: 503.91ms halfway// => job: 1006.77ms (emitted when the scope closes)withTime
Section titled “withTime”Runs an Effect with a console timer wrapped around it: starts the timer before
the effect and ends it after. Dual — data-first (self, label?) or data-last
(label?) for piping. No explicit Scope needed.
import { Console, Effect } from "effect"
const work = Effect.sleep("1 second")
// data-firstconst a = Console.withTime(work, "my-operation")
// data-last (pipeable)const b = work.pipe(Console.withTime("my-operation"))// => my-operation: 1003.12msGrouping
Section titled “Grouping”Opens a console group (indenting subsequent output), closing it automatically
when the scope is finalized. Pass { collapsed: true } for a collapsed group in
the browser. Returns Effect<void, never, Scope>.
import { Console, Effect } from "effect"
const program = Effect.scoped( Effect.gen(function*() { yield* Console.group({ label: "User Processing" }) yield* Console.log("Loading user data...") yield* Console.log("Validating user...") // group closes here, when the scope closes }))// => User Processing// => Loading user data...// => Validating user...withGroup
Section titled “withGroup”Runs an Effect inside a console group, opening it before and closing it after
the effect completes. Dual — data-first (self, options?) or data-last
(options?). No explicit Scope needed.
import { Console, Effect } from "effect"
const steps = Effect.gen(function*() { yield* Console.log("Step 1: Initialize") yield* Console.log("Step 2: Process")})
const program = Console.withGroup(steps, { label: "Processing Steps", collapsed: false})// => Processing Steps// => Step 1: Initialize// => Step 2: ProcessRequests that the active console clear its visible output. The exact behavior depends on the host environment.
import { Console, Effect } from "effect"
const program = Effect.gen(function*() { yield* Console.log("This will be cleared") yield* Console.clear yield* Console.log("This appears after clearing")})