diff --git a/.changeset/dirty-ways-dress.md b/.changeset/dirty-ways-dress.md new file mode 100644 index 00000000000..a313566c9f9 --- /dev/null +++ b/.changeset/dirty-ways-dress.md @@ -0,0 +1,5 @@ +--- +"effect": minor +--- + +Add `Effect.whenLogLevel`, which conditionally executes an effect if the specified log level is enabled diff --git a/packages/effect/src/Effect.ts b/packages/effect/src/Effect.ts index a98bbc7ca4c..9808200325e 100644 --- a/packages/effect/src/Effect.ts +++ b/packages/effect/src/Effect.ts @@ -40,7 +40,7 @@ import * as runtime_ from "./internal/runtime.js" import * as schedule_ from "./internal/schedule.js" import * as internalTracer from "./internal/tracer.js" import type * as Layer from "./Layer.js" -import type { LogLevel } from "./LogLevel.js" +import type * as LogLevel from "./LogLevel.js" import type * as ManagedRuntime from "./ManagedRuntime.js" import type * as Metric from "./Metric.js" import type * as MetricLabel from "./MetricLabel.js" @@ -10680,7 +10680,7 @@ export const log: (...message: ReadonlyArray) => Effect * @category Logging */ export const logWithLevel = ( - level: LogLevel, + level: LogLevel.LogLevel, ...message: ReadonlyArray ): Effect => effect.logWithLevel(level)(...message) @@ -10974,10 +10974,46 @@ export const logAnnotations: Effect> = effect.l * @category Logging */ export const withUnhandledErrorLogLevel: { - (level: Option.Option): (self: Effect) => Effect - (self: Effect, level: Option.Option): Effect + (level: Option.Option): (self: Effect) => Effect + (self: Effect, level: Option.Option): Effect } = core.withUnhandledErrorLogLevel +/** + * Conditionally executes an effect based on the specified log level and currently enabled log level. + * + * **Details** + * + * This function runs the provided effect only if the specified log level is + * enabled. If the log level is enabled, the effect is executed and its result + * is wrapped in `Some`. If the log level is not enabled, the effect is not + * executed and `None` is returned. + * + * This function is useful for conditionally executing logging-related effects + * or other operations that depend on the current log level configuration. + * + * @example + * ```ts + * import { Effect, Logger, LogLevel } from "effect" + * + * const program = Effect.gen(function* () { + * yield* Effect.whenLogLevel(Effect.logTrace("message1"), LogLevel.Trace); // returns `None` + * yield* Effect.whenLogLevel(Effect.logDebug("message2"), LogLevel.Debug); // returns `Some` + * }).pipe(Logger.withMinimumLogLevel(LogLevel.Debug)); + * + * // Effect.runFork(program) + * // timestamp=... level=DEBUG fiber=#0 message=message2 + * ``` + * + * @see {@link FiberRef.minimumLogLevel} to retrieve the current minimum log level. + * + * @since 3.13.0 + * @category Logging + */ +export const whenLogLevel: { + (level: LogLevel.LogLevel | LogLevel.Literal): (self: Effect) => Effect, E, R> + (self: Effect, level: LogLevel.LogLevel | LogLevel.Literal): Effect, E, R> +} = fiberRuntime.whenLogLevel + /** * Converts an effect's failure into a fiber termination, removing the error * from the effect's type. diff --git a/packages/effect/src/internal/fiberRuntime.ts b/packages/effect/src/internal/fiberRuntime.ts index baec9ad2083..4580f352b52 100644 --- a/packages/effect/src/internal/fiberRuntime.ts +++ b/packages/effect/src/internal/fiberRuntime.ts @@ -1631,6 +1631,30 @@ export const annotateLogsScoped: { ) } +/** @internal */ +export const whenLogLevel = dual< + ( + level: LogLevel.LogLevel | LogLevel.Literal + ) => (effect: Effect.Effect) => Effect.Effect, E, R>, + ( + effect: Effect.Effect, + level: LogLevel.LogLevel | LogLevel.Literal + ) => Effect.Effect, E, R> +>(2, (effect, level) => { + const requiredLogLevel = typeof level === "string" ? LogLevel.fromLiteral(level) : level + + return core.withFiberRuntime((fiberState) => { + const minimumLogLevel = fiberState.getFiberRef(currentMinimumLogLevel) + + // Imitate the behaviour of `FiberRuntime.log` + if (LogLevel.greaterThan(minimumLogLevel, requiredLogLevel)) { + return core.succeed(Option.none()) + } + + return core.map(effect, Option.some) + }) +}) + // circular with Effect /* @internal */