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 */
9import type * as Effect from "./Effect.ts"
10import * as Equ from "./Equivalence.ts"
11import * as core from "./internal/core.ts"
12import * as effect from "./internal/effect.ts"
13import * as Ord from "./Order.ts"
14import * 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 */
67export 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 */
87export 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 */
114export 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 */
141export 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 */
170export 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 */
198export 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 */
236export 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 */
269export 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 */
310export 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 */
343export 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 */
383export const isEnabled = (self: LogLevel): Effect.Effect<boolean> =>
384 core.withFiber((fiber) => effect.succeed(!isGreaterThan(fiber.getRef(References.MinimumLogLevel), self)))