Logger<
unknown,
{
readonly level: string
readonly fiberId: string
readonly timestamp: string
readonly message: unknown
readonly cause: string | undefined
readonly annotations: Record<string, unknown>
readonly spans: Record<string, number>
}
>A Logger which outputs logs using a structured format.
Details
For example, a structured entry can contain message: [ "hello" ],
level: "INFO", timestamp: "2025-01-03T14:25:39.666Z",
annotations: { key: "value" }, spans: { label: 0 }, and
fiberId: "#1".
Example (Formatting logs as structured objects)
import { Effect, Logger } from "effect"
// Use the structured format logger
const structuredLoggerProgram = Effect.log("Hello Structured Format").pipe(
Effect.provide(Logger.layer([Logger.formatStructured]))
)
// Perfect for JSON processing and analytics
const analyticsProgram = Effect.gen(function*() {
yield* Effect.log("User action", { action: "click", element: "button" })
yield* Effect.logInfo("API call", { endpoint: "/users", duration: 150 })
}).pipe(
Effect.annotateLogs("sessionId", "abc123"),
Effect.withLogSpan("request"),
Effect.provide(Logger.layer([Logger.formatStructured]))
)
// Process structured output
const processingLogger = Logger.map(Logger.formatStructured, (output) => {
// Process the structured object
const enhanced = { ...output, processed: true }
return enhanced
})export const const formatStructured: Logger<
unknown,
{
readonly level: string
readonly fiberId: string
readonly timestamp: string
readonly message: unknown
readonly cause: string | undefined
readonly annotations: Record<string, unknown>
readonly spans: Record<string, number>
}
>
const formatStructured: {
log: (options: Options<unknown>) => { readonly level: string; readonly fiberId: string; readonly timestamp: string; readonly message: unknown; readonly cause: string | undefined; readonly annotations: Record<string, unknown>; readonly spans: Re…;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
}
A Logger which outputs logs using a structured format.
Details
For example, a structured entry can contain message: [ "hello" ],
level: "INFO", timestamp: "2025-01-03T14:25:39.666Z",
annotations: { key: "value" }, spans: { label: 0 }, and
fiberId: "#1".
Example (Formatting logs as structured objects)
import { Effect, Logger } from "effect"
// Use the structured format logger
const structuredLoggerProgram = Effect.log("Hello Structured Format").pipe(
Effect.provide(Logger.layer([Logger.formatStructured]))
)
// Perfect for JSON processing and analytics
const analyticsProgram = Effect.gen(function*() {
yield* Effect.log("User action", { action: "click", element: "button" })
yield* Effect.logInfo("API call", { endpoint: "/users", duration: 150 })
}).pipe(
Effect.annotateLogs("sessionId", "abc123"),
Effect.withLogSpan("request"),
Effect.provide(Logger.layer([Logger.formatStructured]))
)
// Process structured output
const processingLogger = Logger.map(Logger.formatStructured, (output) => {
// Process the structured object
const enhanced = { ...output, processed: true }
return enhanced
})
formatStructured: interface Logger<in Message, out Output>A logger that transforms a runtime log event into an output value.
Details
The runtime calls log with the message, level, cause, fiber, and timestamp
for each log event. Use Logger.layer to install one or more loggers for an
effect.
Example (Creating custom loggers)
import { Effect, Logger } from "effect"
// Create a custom logger that accepts unknown messages and returns void
const stringLogger = Logger.make<unknown, void>((options) => {
console.log(`[${options.logLevel}] ${options.message}`)
})
// Create a logger that accepts any message type and returns a formatted string
const formattedLogger = Logger.make<unknown, string>((options) =>
`${options.date.toISOString()} [${options.logLevel}] ${options.message}`
)
// Use the logger in an Effect program
const program = Effect.log("Hello World").pipe(
Effect.provide(Logger.layer([stringLogger]))
)
Logger<unknown, {
readonly level: stringlevel: string
readonly fiberId: stringfiberId: string
readonly timestamp: stringtimestamp: string
readonly message: unknownmessage: unknown
readonly cause: string | undefinedcause: string | undefined
readonly annotations: Record<string, unknown>annotations: type Record<K extends keyof any, T> = {
[P in K]: T
}
Construct a type with a set of properties K of type T
Record<string, unknown>
readonly spans: Record<string, number>spans: type Record<K extends keyof any, T> = {
[P in K]: T
}
Construct a type with a set of properties K of type T
Record<string, number>
}> = import effecteffect.const loggerMake: <Message, Output>(
log: (
options: Logger.Options<Message>
) => Output
) => Logger.Logger<Message, Output>
loggerMake(({ cause: Cause.Cause<unknown>(parameter) cause: {
reasons: ReadonlyArray<Reason<E>>;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
cause, date: Datedate, fiber: Fiber.Fiber<unknown, unknown>(parameter) fiber: {
id: number;
currentOpCount: number;
getRef: <A>(ref: Context.Reference<A>) => A;
context: Context.Context<never>;
setContext: (context: Context.Context<never>) => void;
currentScheduler: Scheduler;
currentDispatcher: SchedulerDispatcher;
currentSpan: AnySpan | undefined;
currentLogLevel: LogLevel;
minimumLogLevel: LogLevel;
currentStackFrame: StackFrame | undefined;
maxOpsBeforeYield: number;
currentPreventYield: boolean;
addObserver: (cb: (exit: Exit<A, E>) => void) => () => void;
interruptUnsafe: (fiberId?: number | undefined, annotations?: Context.Context<never> | undefined) => void;
pollUnsafe: () => Exit<A, E> | undefined;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
}
fiber, logLevel: LogLevel.LogLevellogLevel, message: unknownmessage }) => {
const const annotationsObj: Record<
string,
unknown
>
annotationsObj: type Record<K extends keyof any, T> = {
[P in K]: T
}
Construct a type with a set of properties K of type T
Record<string, unknown> = {}
const const spansObj: Record<string, number>spansObj: type Record<K extends keyof any, T> = {
[P in K]: T
}
Construct a type with a set of properties K of type T
Record<string, number> = {}
const const annotations: ReadonlyRecord<
string,
unknown
>
annotations = fiber: Fiber.Fiber<unknown, unknown>(parameter) fiber: {
id: number;
currentOpCount: number;
getRef: <A>(ref: Context.Reference<A>) => A;
context: Context.Context<never>;
setContext: (context: Context.Context<never>) => void;
currentScheduler: Scheduler;
currentDispatcher: SchedulerDispatcher;
currentSpan: AnySpan | undefined;
currentLogLevel: LogLevel;
minimumLogLevel: LogLevel;
currentStackFrame: StackFrame | undefined;
maxOpsBeforeYield: number;
currentPreventYield: boolean;
addObserver: (cb: (exit: Exit<A, E>) => void) => () => void;
interruptUnsafe: (fiberId?: number | undefined, annotations?: Context.Context<never> | undefined) => void;
pollUnsafe: () => Exit<A, E> | undefined;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
}
fiber.getRef(const CurrentLogAnnotations: Context.Reference<
ReadonlyRecord<string, unknown>
>
(alias) const CurrentLogAnnotations: {
defaultValue: () => Shape;
of: (this: void, self: ReadonlyRecord<string, unknown>) => ReadonlyRecord<string, unknown>;
context: (self: ReadonlyRecord<string, unknown>) => Context.Context<never>;
use: (f: (service: ReadonlyRecord<string, unknown>) => Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
useSync: (f: (service: ReadonlyRecord<string, unknown>) => A) => Effect.Effect<A, never, never>;
Identifier: Identifier;
Service: Shape;
key: string;
stack: string | undefined;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
Context reference for managing log annotations that are automatically added to all log entries.
These annotations provide contextual metadata that appears in every log message.
When to use
Use to attach shared contextual metadata to every log entry emitted in the
current context.
Example (Managing log annotations)
import { Console, Effect, References } from "effect"
const logAnnotationExample = Effect.gen(function*() {
// Get current annotations (empty by default)
const current = yield* References.CurrentLogAnnotations
console.log(current) // {}
// Run with custom log annotations
yield* Effect.provideService(
Effect.gen(function*() {
const annotations = yield* References.CurrentLogAnnotations
console.log(annotations) // { requestId: "req-123", userId: "user-456", version: "1.0.0" }
// All log entries will include these annotations
yield* Console.log("Starting operation")
yield* Console.info("Processing data")
}),
References.CurrentLogAnnotations,
{
requestId: "req-123",
userId: "user-456",
version: "1.0.0"
}
)
// Run with extended annotations
yield* Effect.provideService(
Effect.gen(function*() {
const extended = yield* References.CurrentLogAnnotations
console.log(extended) // { requestId: "req-123", userId: "user-456", version: "1.0.0", operation: "data-sync", timestamp: 1234567890 }
yield* Console.log("Operation completed with extended context")
}),
References.CurrentLogAnnotations,
{
requestId: "req-123",
userId: "user-456",
version: "1.0.0",
operation: "data-sync",
timestamp: 1234567890
}
)
})
CurrentLogAnnotations)
for (const [const key: stringkey, const value: unknownvalue] of var Object: ObjectConstructorProvides functionality common to all JavaScript objects.
Object.ObjectConstructor.entries<unknown>(o: {
[s: string]: unknown;
} | ArrayLike<unknown>): [string, unknown][] (+1 overload)
Returns an array of key/values of the enumerable own properties of an object
entries(const annotations: ReadonlyRecord<
string,
unknown
>
annotations)) {
const annotationsObj: Record<
string,
unknown
>
annotationsObj[const key: stringkey] = import effecteffect.const structuredMessage: (
u: unknown
) => unknown
structuredMessage(const value: unknownvalue)
}
const const now: numbernow = date: Datedate.Date.getTime(): numberReturns the stored time value in milliseconds since midnight, January 1, 1970 UTC.
getTime()
const const spans: anyspans = fiber: Fiber.Fiber<unknown, unknown>(parameter) fiber: {
id: number;
currentOpCount: number;
getRef: <A>(ref: Context.Reference<A>) => A;
context: Context.Context<never>;
setContext: (context: Context.Context<never>) => void;
currentScheduler: Scheduler;
currentDispatcher: SchedulerDispatcher;
currentSpan: AnySpan | undefined;
currentLogLevel: LogLevel;
minimumLogLevel: LogLevel;
currentStackFrame: StackFrame | undefined;
maxOpsBeforeYield: number;
currentPreventYield: boolean;
addObserver: (cb: (exit: Exit<A, E>) => void) => () => void;
interruptUnsafe: (fiberId?: number | undefined, annotations?: Context.Context<never> | undefined) => void;
pollUnsafe: () => Exit<A, E> | undefined;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
}
fiber.getRef(const CurrentLogSpans: Context.Reference<
ReadonlyArray<
[label: string, timestamp: number]
>
>
(alias) const CurrentLogSpans: {
key: string;
Service: {
length: number;
toString: () => string;
toLocaleString: { (): string; (locales: string | string[], options?: Intl.NumberFormatOptions & Intl.DateTimeFormatOptions): string };
concat: { (...items: Array<ConcatArray<[label: string, timestamp: number]>>): Array<[label: string, timestamp: number]>; (...items: Array<[label: string, timestamp: number] | ConcatArray<[label: string, timestamp: number]>>): Array<[label: string,…;
join: (separator?: string) => string;
slice: (start?: number, end?: number) => Array<[label: string, timestamp: number]>;
indexOf: (searchElement: [label: string, timestamp: number], fromIndex?: number) => number;
lastIndexOf: (searchElement: [label: string, timestamp: number], fromIndex?: number) => number;
every: { (predicate: (value: [label: string, timestamp: number], index: number, array: ReadonlyArray<[label: string, timestamp: number]>) => value is S, thisArg?: any): this is readonly S[]; (predicate: (value: [label: string, timestamp: number],…;
some: (predicate: (value: [label: string, timestamp: number], index: number, array: ReadonlyArray<[label: string, timestamp: number]>) => unknown, thisArg?: any) => boolean;
forEach: (callbackfn: (value: [label: string, timestamp: number], index: number, array: ReadonlyArray<[label: string, timestamp: number]>) => void, thisArg?: any) => void;
map: (callbackfn: (value: [label: string, timestamp: number], index: number, array: ReadonlyArray<[label: string, timestamp: number]>) => U, thisArg?: any) => Array<U>;
filter: { (predicate: (value: [label: string, timestamp: number], index: number, array: ReadonlyArray<[label: string, timestamp: number]>) => value is S, thisArg?: any): Array<S>; (predicate: (value: [label: string, timestamp: number], index: numb…;
reduce: { (callbackfn: (previousValue: [label: string, timestamp: number], currentValue: [label: string, timestamp: number], currentIndex: number, array: ReadonlyArray<[label: string, timestamp: number]>) => [label: string, timestamp: number]): [l…;
reduceRight: { (callbackfn: (previousValue: [label: string, timestamp: number], currentValue: [label: string, timestamp: number], currentIndex: number, array: ReadonlyArray<[label: string, timestamp: number]>) => [label: string, timestamp: number]): [l…;
find: { (predicate: (value: [label: string, timestamp: number], index: number, obj: ReadonlyArray<[label: string, timestamp: number]>) => value is S, thisArg?: any): S | undefined; (predicate: (value: [label: string, timestamp: number], index: n…;
findIndex: (predicate: (value: [label: string, timestamp: number], index: number, obj: ReadonlyArray<[label: string, timestamp: number]>) => unknown, thisArg?: any) => number;
entries: () => ArrayIterator<[number, [label: string, timestamp: number]]>;
keys: () => ArrayIterator<number>;
values: () => ArrayIterator<[label: string, timestamp: number]>;
includes: (searchElement: [label: string, timestamp: number], fromIndex?: number) => boolean;
flatMap: (callback: (this: This, value: [label: string, timestamp: number], index: number, array: Array<[label: string, timestamp: number]>) => U | ReadonlyArray<U>, thisArg?: This | undefined) => Array<U>;
flat: (this: A, depth?: D | undefined) => Array<FlatArray<A, D>>;
at: (index: number) => [label: string, timestamp: number] | undefined;
findLast: { (predicate: (value: [label: string, timestamp: number], index: number, array: ReadonlyArray<[label: string, timestamp: number]>) => value is S, thisArg?: any): S | undefined; (predicate: (value: [label: string, timestamp: number], index:…;
findLastIndex: (predicate: (value: [label: string, timestamp: number], index: number, array: ReadonlyArray<[label: string, timestamp: number]>) => unknown, thisArg?: any) => number;
toReversed: () => Array<[label: string, timestamp: number]>;
toSorted: (compareFn?: ((a: [label: string, timestamp: number], b: [label: string, timestamp: number]) => number) | undefined) => Array<[label: string, timestamp: number]>;
toSpliced: { (start: number, deleteCount: number, ...items: Array<[label: string, timestamp: number]>): Array<[label: string, timestamp: number]>; (start: number, deleteCount?: number): Array<[label: string, timestamp: number]> };
with: (index: number, value: [label: string, timestamp: number]) => Array<[label: string, timestamp: number]>;
};
defaultValue: () => Shape;
of: (this: void, self: ReadonlyArray<[label: string, timestamp: number]>) => ReadonlyArray<[label: string, timestamp: number]>;
context: (self: ReadonlyArray<[label: string, timestamp: number]>) => Context.Context<never>;
use: (f: (service: ReadonlyArray<[label: string, timestamp: number]>) => Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
useSync: (f: (service: ReadonlyArray<[label: string, timestamp: number]>) => A) => Effect.Effect<A, never, never>;
Identifier: Identifier;
stack: string | undefined;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
Context reference for managing log spans that track the duration and hierarchy of operations.
Each span represents a labeled time period for performance analysis and debugging.
When to use
Use to carry the active log span stack that should be included with log
entries in the current context.
Example (Tracking log spans)
import { Console, Effect, References } from "effect"
const logSpanExample = Effect.gen(function*() {
// Get current spans (empty by default)
const current = yield* References.CurrentLogSpans
console.log(current.length) // 0
// Add a log span manually
const databaseConnectionStartedAt = 0
yield* Effect.provideService(
Effect.gen(function*() {
// Simulate some work
yield* Effect.sleep("100 millis")
yield* Console.log("Database operation in progress")
const spans = yield* References.CurrentLogSpans
console.log("Active spans:", spans.map(([label]) => label)) // ["database-connection"]
}),
References.CurrentLogSpans,
[["database-connection", databaseConnectionStartedAt]]
)
// Add another span
const dataProcessingStartedAt = 100
yield* Effect.provideService(
Effect.gen(function*() {
const spans = yield* References.CurrentLogSpans
console.log("Active spans:", spans.map(([label]) => label)) // ["database-connection", "data-processing"]
yield* Console.log("Multiple operations in progress")
}),
References.CurrentLogSpans,
[
["database-connection", databaseConnectionStartedAt],
["data-processing", dataProcessingStartedAt]
]
)
// Clear spans when operations complete
yield* Effect.provideService(
Effect.gen(function*() {
const spans = yield* References.CurrentLogSpans
console.log("Active spans:", spans.length) // 0
}),
References.CurrentLogSpans,
[]
)
})
CurrentLogSpans)
for (const [const label: anylabel, const timestamp: anytimestamp] of const spans: anyspans) {
const spansObj: Record<string, number>spansObj[const label: anylabel] = const now: numbernow - const timestamp: anytimestamp
}
const const messageArr: anymessageArr = import ArrayArray.ensure(message: unknownmessage)
return {
message: anymessage: const messageArr: anymessageArr.length === 1
? import effecteffect.const structuredMessage: (
u: unknown
) => unknown
structuredMessage(const messageArr: anymessageArr[0])
: const messageArr: anymessageArr.map(import effecteffect.const structuredMessage: (
u: unknown
) => unknown
structuredMessage),
level: stringlevel: logLevel: LogLevel.LogLevellogLevel.String.toUpperCase(): stringConverts all the alphabetic characters in a string to uppercase.
toUpperCase(),
timestamp: stringtimestamp: date: Datedate.Date.toISOString(): stringReturns a date as a string value in ISO format.
toISOString(),
cause: string | undefinedcause: cause: Cause.Cause<unknown>(parameter) cause: {
reasons: ReadonlyArray<Reason<E>>;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
cause.reasons.length > 0 ? import effecteffect.const causePretty: <E>(
cause: Cause.Cause<E>
) => string
causePretty(cause: Cause.Cause<unknown>(parameter) cause: {
reasons: ReadonlyArray<Reason<E>>;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
cause) : var undefinedundefined,
annotations: Record<string, unknown>annotations: const annotationsObj: Record<
string,
unknown
>
annotationsObj,
spans: Record<string, number>spans: const spansObj: Record<string, number>spansObj,
fiberId: stringfiberId: const formatFiberId: (
fiberId: number
) => string
Formats the identifier of a Fiber by prefixing it with a hash tag.
formatFiberId(fiber: Fiber.Fiber<unknown, unknown>(parameter) fiber: {
id: number;
currentOpCount: number;
getRef: <A>(ref: Context.Reference<A>) => A;
context: Context.Context<never>;
setContext: (context: Context.Context<never>) => void;
currentScheduler: Scheduler;
currentDispatcher: SchedulerDispatcher;
currentSpan: AnySpan | undefined;
currentLogLevel: LogLevel;
minimumLogLevel: LogLevel;
currentStackFrame: StackFrame | undefined;
maxOpsBeforeYield: number;
currentPreventYield: boolean;
addObserver: (cb: (exit: Exit<A, E>) => void) => () => void;
interruptUnsafe: (fiberId?: number | undefined, annotations?: Context.Context<never> | undefined) => void;
pollUnsafe: () => Exit<A, E> | undefined;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
}
fiber.id)
}
})