Hyperlinkv0.8.0-beta.28

Logger

Logger.toFileconsteffect/Logger.ts:1252
(
  path: string,
  options?:
    | {
        readonly flag?: FileSystem.OpenFlag | undefined
        readonly mode?: number | undefined
        readonly batchWindow?: Duration.Input | undefined
      }
    | undefined
): <Message>(
  self: Logger<Message, string>
) => Effect.Effect<
  Logger<Message, void>,
  PlatformError,
  Scope.Scope | FileSystem.FileSystem
>
<Message>(
  self: Logger<Message, string>,
  path: string,
  options?:
    | {
        readonly flag?: FileSystem.OpenFlag | undefined
        readonly mode?: number | undefined
        readonly batchWindow?: Duration.Input | undefined
      }
    | undefined
): Effect.Effect<
  Logger<Message, void>,
  PlatformError,
  Scope.Scope | FileSystem.FileSystem
>

Creates a scoped logger that writes string logger output to a file.

Details

The returned effect requires FileSystem and Scope. The file logger batches string output, writes each batch to the specified path, and flushes remaining entries when the scope closes.

Example (Writing JSON logs to a file)

import { Effect, Layer, Logger } from "effect"
import { NodeFileSystem, NodeRuntime } from "@effect/platform-node"

const fileLogger = Logger.formatJson.pipe(
  Logger.toFile("/tmp/log.txt")
)
const LoggerLive = Logger.layer([fileLogger]).pipe(
  Layer.provide(NodeFileSystem.layer)
)

Effect.log("a").pipe(
  Effect.andThen(Effect.log("b")),
  Effect.andThen(Effect.log("c")),
  Effect.provide(LoggerLive),
  NodeRuntime.runMain
)

Example (Writing logs to files)

import { Duration, Effect, Logger } from "effect"
import { NodeFileSystem } from "@effect/platform-node"

// Basic file logging. The scope keeps the file open while logs are emitted
// and flushes pending entries when it closes.
const basicFileLogger = Effect.scoped(
  Effect.gen(function*() {
    const fileLogger = yield* Logger.formatJson.pipe(
      Logger.toFile("/tmp/app.log")
    )

    yield* Effect.log("Application started").pipe(
      Effect.provide(Logger.layer([fileLogger]))
    )
  })
).pipe(
  Effect.provide(NodeFileSystem.layer)
)

// File logger with custom batch window
const batchedFileLogger = Effect.scoped(
  Effect.gen(function*() {
    const fileLogger = yield* Logger.formatLogFmt.pipe(
      Logger.toFile("/var/log/myapp.log", {
        flag: "a",
        batchWindow: Duration.seconds(5)
      })
    )

    yield* Effect.all([
      Effect.log("Event 1"),
      Effect.log("Event 2"),
      Effect.log("Event 3")
    ]).pipe(
      Effect.provide(Logger.layer([fileLogger]))
    )
  })
).pipe(
  Effect.provide(NodeFileSystem.layer)
)

// Multiple loggers: console + file
const multiLogger = Effect.scoped(
  Effect.gen(function*() {
    const fileLogger = yield* Logger.formatJson.pipe(
      Logger.toFile("/tmp/production.log")
    )

    const loggerLive = Logger.layer([
      Logger.consolePretty(),
      fileLogger
    ])

    yield* Effect.log("Production event").pipe(
      Effect.provide(loggerLive)
    )
  })
).pipe(
  Effect.provide(NodeFileSystem.layer)
)
file
Source effect/Logger.ts:125233 lines
export const toFile = dual<
  (
    path: string,
    options?: {
      readonly flag?: FileSystem.OpenFlag | undefined
      readonly mode?: number | undefined
      readonly batchWindow?: Duration.Input | undefined
    } | undefined
  ) => <Message>(
    self: Logger<Message, string>
  ) => Effect.Effect<Logger<Message, void>, PlatformError, Scope.Scope | FileSystem.FileSystem>,
  <Message>(
    self: Logger<Message, string>,
    path: string,
    options?: {
      readonly flag?: FileSystem.OpenFlag | undefined
      readonly mode?: number | undefined
      readonly batchWindow?: Duration.Input | undefined
    } | undefined
  ) => Effect.Effect<Logger<Message, void>, PlatformError, Scope.Scope | FileSystem.FileSystem>
>(
  (args) => isLogger(args[0]),
  (self, path, options) =>
    effect.gen(function*() {
      const fs = yield* FileSystem.FileSystem
      const logFile = yield* fs.open(path, { flag: "a+", ...options })
      const encoder = new TextEncoder()
      return yield* batched(self, {
        window: options?.batchWindow ?? 1000,
        flush: (output) => effect.ignore(logFile.write(encoder.encode(output.join("\n") + "\n")))
      })
    })
)