| 1 | /** |
| 2 | * Log-level types and helpers used by Effect logging. The module defines all |
| 3 | * accepted log levels, the concrete emitted severities, the ordered level list, |
| 4 | * equality and ordering instances, threshold comparison helpers, and an effect |
| 5 | * for checking whether a level is enabled by the current logging settings. |
| 6 | * |
| 7 | * @since 2.0.0 |
| 8 | */ |
| 9 | import type * as Effect from "./Effect.ts" |
| 10 | import * as Equ from "./Equivalence.ts" |
| 11 | import * as core from "./internal/core.ts" |
| 12 | import * as effect from "./internal/effect.ts" |
| 13 | import * as Ord from "./Order.ts" |
| 14 | import * as References from "./References.ts" |
| 15 | |
| 16 | /** |
| 17 | * Represents every level used by Effect logging, including concrete message |
| 18 | * severities and the `All` and `None` sentinel levels. |
| 19 | * |
| 20 | * **When to use** |
| 21 | * |
| 22 | * Use to type values that may be either concrete log message severities or |
| 23 | * logging configuration sentinels. |
| 24 | * |
| 25 | * **Details** |
| 26 | * |
| 27 | * The levels are ordered from most severe to least severe: |
| 28 | * - `All` - Special level that allows all messages |
| 29 | * - `Fatal` - System is unusable, immediate attention required |
| 30 | * - `Error` - Error conditions that should be investigated |
| 31 | * - `Warn` - Warning conditions that may indicate problems |
| 32 | * - `Info` - Informational messages about normal operation |
| 33 | * - `Debug` - Debug information useful during development |
| 34 | * - `Trace` - Very detailed trace information |
| 35 | * - `None` - Special level that suppresses all messages |
| 36 | * |
| 37 | * **Example** (Using log levels) |
| 38 | * |
| 39 | * ```ts import.meta.vitest |
| 40 | * import { Effect, References } from "effect" |
| 41 | * |
| 42 | * // Using log levels with Effect logging |
| 43 | * const program = Effect.gen(function*() { |
| 44 | * yield* Effect.logFatal("System failure") |
| 45 | * yield* Effect.logError("Database error") |
| 46 | * yield* Effect.logWarning("High memory usage") |
| 47 | * yield* Effect.logInfo("User logged in") |
| 48 | * yield* Effect.logDebug("Processing request") |
| 49 | * yield* Effect.logTrace("Variable state") |
| 50 | * }) |
| 51 | * |
| 52 | * // Type-safe log level variables |
| 53 | * const errorLevel = "Error" // LogLevel |
| 54 | * const debugLevel = "Debug" // LogLevel |
| 55 | * |
| 56 | * await Effect.runPromise( |
| 57 | * Effect.provideService(program, References.MinimumLogLevel, "None") |
| 58 | * ) |
| 59 | * |
| 60 | * const levels = [errorLevel, debugLevel] |
| 61 | * levels // => ["Error", "Debug"] |
| 62 | * ``` |
| 63 | * |
| 64 | * @category models |
| 65 | * @since 2.0.0 |
| 66 | */ |
| 67 | export type LogLevel = "All" | "Fatal" | "Error" | "Warn" | "Info" | "Debug" | "Trace" | "None" |
| 68 | |
| 69 | /** |
| 70 | * Log levels that represent actual message severities, excluding the `All` and |
| 71 | * `None` sentinel levels. |
| 72 | * |
| 73 | * **When to use** |
| 74 | * |
| 75 | * Use when typing emitted log message severities, such as explicit log calls, |
| 76 | * current log level references, or error-report severity annotations, where |
| 77 | * `All` and `None` are not valid values. |
| 78 | * |
| 79 | * @see {@link LogLevel} for the wider log-level type that also accepts the |
| 80 | * `All` and `None` sentinel levels |
| 81 | * @see {@link values} for the runtime list of all accepted `LogLevel` values, |
| 82 | * including sentinels |
| 83 | * |
| 84 | * @category models |
| 85 | * @since 4.0.0 |
| 86 | */ |
| 87 | export type Severity = "Fatal" | "Error" | "Warn" | "Info" | "Debug" | "Trace" |
| 88 | |
| 89 | /** |
| 90 | * Returns all `LogLevel` values in order from `All` through the concrete severities to |
| 91 | * `None`. |
| 92 | * |
| 93 | * **When to use** |
| 94 | * |
| 95 | * Use to enumerate or validate all accepted `LogLevel` string values, including |
| 96 | * the `All` and `None` sentinel levels. |
| 97 | * |
| 98 | * **Details** |
| 99 | * |
| 100 | * The array order matches the module severity order: `All`, concrete |
| 101 | * severities from `Fatal` to `Trace`, then `None`. |
| 102 | * |
| 103 | * **Gotchas** |
| 104 | * |
| 105 | * This list includes `All` and `None`, so it is not limited to concrete emitted |
| 106 | * severities. |
| 107 | * |
| 108 | * @see {@link Severity} for the concrete message severity type that excludes `All` and `None` |
| 109 | * @see {@link Order} for comparing these levels by severity order |
| 110 | * |
| 111 | * @category constants |
| 112 | * @since 4.0.0 |
| 113 | */ |
| 114 | export const values: ReadonlyArray<LogLevel> = ["All", "Fatal", "Error", "Warn", "Info", "Debug", "Trace", "None"] |
| 115 | |
| 116 | /** |
| 117 | * Order instance for `LogLevel` that defines the severity ordering. |
| 118 | * |
| 119 | * **When to use** |
| 120 | * |
| 121 | * Use to sort or compare log levels according to Effect's severity order. |
| 122 | * |
| 123 | * **Details** |
| 124 | * |
| 125 | * This order treats "All" as the least restrictive level and "None" as the most restrictive, |
| 126 | * with Fatal being the most severe actual log level. |
| 127 | * |
| 128 | * **Example** (Ordering log levels) |
| 129 | * |
| 130 | * ```ts import.meta.vitest |
| 131 | * import { LogLevel } from "effect" |
| 132 | * |
| 133 | * LogLevel.Order("Error", "Info") // => 1 |
| 134 | * LogLevel.Order("Debug", "Error") // => -1 |
| 135 | * LogLevel.Order("Info", "Info") // => 0 |
| 136 | * ``` |
| 137 | * |
| 138 | * @category ordering |
| 139 | * @since 2.0.0 |
| 140 | */ |
| 141 | export const Order: Ord.Order<LogLevel> = effect.LogLevelOrder |
| 142 | |
| 143 | /** |
| 144 | * Equivalence instance for log levels using strict equality (`===`). |
| 145 | * |
| 146 | * **When to use** |
| 147 | * |
| 148 | * Use to compare two `LogLevel` values when only the exact same level should |
| 149 | * match. |
| 150 | * |
| 151 | * **Details** |
| 152 | * |
| 153 | * Each log level string, including `All` and `None`, only matches itself. |
| 154 | * |
| 155 | * **Example** (Comparing log levels) |
| 156 | * |
| 157 | * ```ts import.meta.vitest |
| 158 | * import { LogLevel } from "effect" |
| 159 | * |
| 160 | * LogLevel.Equivalence("Error", "Error") // => true |
| 161 | * LogLevel.Equivalence("Error", "Info") // => false |
| 162 | * ``` |
| 163 | * |
| 164 | * @see {@link Order} for severity ordering rather than exact level equality |
| 165 | * @see {@link isGreaterThanOrEqualTo} for minimum-threshold checks |
| 166 | * |
| 167 | * @category instances |
| 168 | * @since 4.0.0 |
| 169 | */ |
| 170 | export const Equivalence: Equ.Equivalence<LogLevel> = Equ.strictEqual<LogLevel>() |
| 171 | |
| 172 | /** |
| 173 | * Returns the ordinal value of the log level. |
| 174 | * |
| 175 | * **When to use** |
| 176 | * |
| 177 | * Use to project a `LogLevel` into the numeric sort key used by |
| 178 | * `LogLevel.Order` when custom ordering code or an integration needs a number |
| 179 | * instead of an `Order` comparison. |
| 180 | * |
| 181 | * **Details** |
| 182 | * |
| 183 | * The mapping is `All` to `Number.MIN_SAFE_INTEGER`, `Trace` to `0`, `Debug` to |
| 184 | * `10000`, `Info` to `20000`, `Warn` to `30000`, `Error` to `40000`, `Fatal` to |
| 185 | * `50000`, and `None` to `Number.MAX_SAFE_INTEGER`. |
| 186 | * |
| 187 | * **Gotchas** |
| 188 | * |
| 189 | * These ordinals are internal sort keys; do not treat them as external severity |
| 190 | * numbers. |
| 191 | * |
| 192 | * @see {@link Order} for comparing log levels without exposing numeric keys |
| 193 | * @see {@link isGreaterThanOrEqualTo} for minimum-threshold filtering |
| 194 | * |
| 195 | * @category ordering |
| 196 | * @since 4.0.0 |
| 197 | */ |
| 198 | export const getOrdinal = (self: LogLevel): number => effect.logLevelToOrder(self) |
| 199 | |
| 200 | /** |
| 201 | * Determines if the first log level is more severe than the second. |
| 202 | * |
| 203 | * **When to use** |
| 204 | * |
| 205 | * Use to check whether one log level is strictly more severe than another. |
| 206 | * |
| 207 | * **Details** |
| 208 | * |
| 209 | * Returns `true` if `self` represents a more severe level than `that`. |
| 210 | * |
| 211 | * **Example** (Checking higher severity) |
| 212 | * |
| 213 | * ```ts import.meta.vitest |
| 214 | * import { LogLevel } from "effect" |
| 215 | * |
| 216 | * LogLevel.isGreaterThan("Error", "Info") // => true |
| 217 | * LogLevel.isGreaterThan("Debug", "Error") // => false |
| 218 | * |
| 219 | * // Use with filtering |
| 220 | * const isFatal = LogLevel.isGreaterThan("Fatal", "Warn") |
| 221 | * const isError = LogLevel.isGreaterThan("Error", "Warn") |
| 222 | * const isDebug = LogLevel.isGreaterThan("Debug", "Warn") |
| 223 | * isFatal // => true |
| 224 | * isError // => true |
| 225 | * isDebug // => false |
| 226 | * |
| 227 | * // Curried usage |
| 228 | * const isMoreSevereThanInfo = LogLevel.isGreaterThan("Info") |
| 229 | * isMoreSevereThanInfo("Error") // => true |
| 230 | * isMoreSevereThanInfo("Debug") // => false |
| 231 | * ``` |
| 232 | * |
| 233 | * @category ordering |
| 234 | * @since 4.0.0 |
| 235 | */ |
| 236 | export const isGreaterThan: { |
| 237 | (that: LogLevel): (self: LogLevel) => boolean |
| 238 | (self: LogLevel, that: LogLevel): boolean |
| 239 | } = effect.isLogLevelGreaterThan |
| 240 | |
| 241 | /** |
| 242 | * Determines if the first log level is more severe than or equal to the second. |
| 243 | * |
| 244 | * **When to use** |
| 245 | * |
| 246 | * Use to implement minimum log-level filtering by checking whether a message |
| 247 | * level meets a threshold. |
| 248 | * |
| 249 | * **Details** |
| 250 | * |
| 251 | * Returns `true` if `self` represents a level that is more severe than or equal to `that`. |
| 252 | * |
| 253 | * **Example** (Filtering by minimum log level) |
| 254 | * |
| 255 | * ```ts import.meta.vitest |
| 256 | * import { LogLevel } from "effect" |
| 257 | * |
| 258 | * LogLevel.isGreaterThanOrEqualTo("Error", "Error") // => true |
| 259 | * LogLevel.isGreaterThanOrEqualTo("Error", "Info") // => true |
| 260 | * LogLevel.isGreaterThanOrEqualTo("Debug", "Info") // => false |
| 261 | * |
| 262 | * const isInfoOrAbove = LogLevel.isGreaterThanOrEqualTo("Info") |
| 263 | * isInfoOrAbove("Error") // => true |
| 264 | * ``` |
| 265 | * |
| 266 | * @category ordering |
| 267 | * @since 4.0.0 |
| 268 | */ |
| 269 | export const isGreaterThanOrEqualTo: { |
| 270 | (that: LogLevel): (self: LogLevel) => boolean |
| 271 | (self: LogLevel, that: LogLevel): boolean |
| 272 | } = Ord.isGreaterThanOrEqualTo(Order) |
| 273 | |
| 274 | /** |
| 275 | * Determines if the first log level is less severe than the second. |
| 276 | * |
| 277 | * **When to use** |
| 278 | * |
| 279 | * Use to check whether one log level is strictly less severe than another. |
| 280 | * |
| 281 | * **Details** |
| 282 | * |
| 283 | * Returns `true` if `self` represents a less severe level than `that`. |
| 284 | * |
| 285 | * **Example** (Checking lower severity) |
| 286 | * |
| 287 | * ```ts import.meta.vitest |
| 288 | * import { LogLevel } from "effect" |
| 289 | * |
| 290 | * LogLevel.isLessThan("Debug", "Info") // => true |
| 291 | * LogLevel.isLessThan("Error", "Info") // => false |
| 292 | * |
| 293 | * // Filter out verbose logs |
| 294 | * const isFatalVerbose = LogLevel.isLessThan("Fatal", "Info") |
| 295 | * const isErrorVerbose = LogLevel.isLessThan("Error", "Info") |
| 296 | * const isTraceVerbose = LogLevel.isLessThan("Trace", "Info") |
| 297 | * isFatalVerbose // => false |
| 298 | * isErrorVerbose // => false |
| 299 | * isTraceVerbose // => true |
| 300 | * |
| 301 | * // Curried usage |
| 302 | * const isLessSevereThanError = LogLevel.isLessThan("Error") |
| 303 | * isLessSevereThanError("Info") // => true |
| 304 | * isLessSevereThanError("Fatal") // => false |
| 305 | * ``` |
| 306 | * |
| 307 | * @category ordering |
| 308 | * @since 4.0.0 |
| 309 | */ |
| 310 | export const isLessThan: { |
| 311 | (that: LogLevel): (self: LogLevel) => boolean |
| 312 | (self: LogLevel, that: LogLevel): boolean |
| 313 | } = Ord.isLessThan(Order) |
| 314 | |
| 315 | /** |
| 316 | * Determines if the first log level is less severe than or equal to the second. |
| 317 | * |
| 318 | * **When to use** |
| 319 | * |
| 320 | * Use to implement maximum log-level filtering by checking whether a level is |
| 321 | * at or below a threshold. |
| 322 | * |
| 323 | * **Details** |
| 324 | * |
| 325 | * Returns `true` if `self` represents a level that is less severe than or equal to `that`. |
| 326 | * |
| 327 | * **Example** (Filtering by maximum log level) |
| 328 | * |
| 329 | * ```ts import.meta.vitest |
| 330 | * import { LogLevel } from "effect" |
| 331 | * |
| 332 | * LogLevel.isLessThanOrEqualTo("Info", "Info") // => true |
| 333 | * LogLevel.isLessThanOrEqualTo("Debug", "Info") // => true |
| 334 | * LogLevel.isLessThanOrEqualTo("Error", "Info") // => false |
| 335 | * |
| 336 | * const isInfoOrBelow = LogLevel.isLessThanOrEqualTo("Info") |
| 337 | * isInfoOrBelow("Debug") // => true |
| 338 | * ``` |
| 339 | * |
| 340 | * @category ordering |
| 341 | * @since 4.0.0 |
| 342 | */ |
| 343 | export const isLessThanOrEqualTo: { |
| 344 | (that: LogLevel): (self: LogLevel) => boolean |
| 345 | (self: LogLevel, that: LogLevel): boolean |
| 346 | } = Ord.isLessThanOrEqualTo(Order) |
| 347 | |
| 348 | /** |
| 349 | * Checks whether a given log level is enabled for the current fiber. |
| 350 | * |
| 351 | * **When to use** |
| 352 | * |
| 353 | * Use to check whether a log level would be emitted under the current fiber's |
| 354 | * minimum log level. |
| 355 | * |
| 356 | * **Details** |
| 357 | * |
| 358 | * A log level is enabled when it is greater than or equal to |
| 359 | * `References.MinimumLogLevel`. |
| 360 | * |
| 361 | * **Example** (Checking current fiber log level) |
| 362 | * |
| 363 | * ```ts import.meta.vitest |
| 364 | * import { Effect, LogLevel, References } from "effect" |
| 365 | * |
| 366 | * const program = Effect.gen(function*() { |
| 367 | * const debugEnabled = yield* LogLevel.isEnabled("Debug") |
| 368 | * const errorEnabled = yield* LogLevel.isEnabled("Error") |
| 369 | * |
| 370 | * return { debugEnabled, errorEnabled } |
| 371 | * }) |
| 372 | * |
| 373 | * const warnOnly = program.pipe( |
| 374 | * Effect.provideService(References.MinimumLogLevel, "Warn") |
| 375 | * ) |
| 376 | * |
| 377 | * await Effect.runPromise(warnOnly) // => { debugEnabled: false, errorEnabled: true } |
| 378 | * ``` |
| 379 | * |
| 380 | * @category predicates |
| 381 | * @since 4.0.0 |
| 382 | */ |
| 383 | export const isEnabled = (self: LogLevel): Effect.Effect<boolean> => |
| 384 | core.withFiber((fiber) => effect.succeed(!isGreaterThan(fiber.getRef(References.MinimumLogLevel), self))) |