| 1 | /** |
| 2 | * @title Customizing logging |
| 3 | * |
| 4 | * Configure loggers & log-level filtering for production applications. |
| 5 | */ |
| 6 | import { NodeFileSystem } from "@effect/platform-node" |
| 7 | import { Config, Effect, Layer, Logger, References } from "effect" |
| 8 | |
| 9 | // Build a logger layer that emits one JSON line per log entry. |
| 10 | export const JsonLoggerLayer = Logger.layer([Logger.consoleJson]) |
| 11 | |
| 12 | // Raise the minimum level to "Warn" to skip debug/info logs. |
| 13 | export const WarnAndAbove = Layer.succeed(References.MinimumLogLevel, "Warn") |
| 14 | |
| 15 | // There is a built-in logger for writing to a file |
| 16 | export const FileLoggerLayer = Logger.layer([ |
| 17 | Logger.toFile(Logger.formatSimple, "app.log") |
| 18 | ]).pipe( |
| 19 | Layer.provide(NodeFileSystem.layer) |
| 20 | ) |
| 21 | |
| 22 | // Define a custom logger for app-specific formatting and routing. |
| 23 | export const appLogger = Effect.gen(function*() { |
| 24 | // Here you could initialize a connection to an external logging service, set |
| 25 | // up log file rotation, etc. |
| 26 | yield* Effect.logDebug("initializing app logger") |
| 27 | |
| 28 | return yield* Logger.batched(Logger.formatStructured, { |
| 29 | window: "1 second", |
| 30 | flush: Effect.fn(function*(batch) { |
| 31 | // In a real implementation, this is where you would send the batch of log entries to an external logging service or write them to a file. |
| 32 | console.log(`Flushing ${batch.length} log entries`) |
| 33 | }) |
| 34 | }) |
| 35 | }) |
| 36 | |
| 37 | export const AppLoggerLayer = Logger.layer([appLogger]).pipe( |
| 38 | Layer.provideMerge(WarnAndAbove) // Start with "Warn" level for the app logger. |
| 39 | ) |
| 40 | |
| 41 | // Create a logger layer that uses the default logger for development, and the |
| 42 | // custom logger for production |
| 43 | export const LoggerLayer = Layer.unwrap(Effect.gen(function*() { |
| 44 | const env = yield* Config.String("NODE_ENV").pipe(Config.withDefault("development")) |
| 45 | if (env === "production") { |
| 46 | return AppLoggerLayer |
| 47 | } |
| 48 | return Logger.layer([Logger.defaultLogger]) |
| 49 | })) |
| 50 | |
| 51 | // Example effect that logs at various levels during a checkout flow. |
| 52 | export const logCheckoutFlow = Effect.gen(function*() { |
| 53 | yield* Effect.logDebug("loading checkout state") |
| 54 | |
| 55 | yield* Effect.logInfo("validating cart") |
| 56 | yield* Effect.logWarning("inventory is low for one line item") |
| 57 | yield* Effect.logError("payment provider timeout") |
| 58 | }).pipe( |
| 59 | // Attach structured metadata to all log lines emitted by this effect. |
| 60 | Effect.annotateLogs({ |
| 61 | service: "checkout-api", |
| 62 | route: "POST /checkout" |
| 63 | }), |
| 64 | // Add a duration span so each log line includes checkout=<N>ms metadata. |
| 65 | Effect.withLogSpan("checkout") |
| 66 | ) |