Skip to content

Metrics in Effect

In complex and highly concurrent applications, managing various interconnected components can be quite challenging. Ensuring that everything runs smoothly and avoiding application downtime becomes crucial in such setups.

Now, let’s imagine we have a sophisticated infrastructure with numerous services. These services are replicated and distributed across our servers. However, we often lack insight into what’s happening across these services, including error rates, response times, and service uptime. This lack of visibility can make it challenging to identify and address issues effectively. This is where Effect Metrics comes into play; it allows us to capture and analyze various metrics, providing valuable data for later investigation.

Effect Metrics offers support for five different types of metrics:

  1. Counter: Counters are used to track values that increase over time, such as request counts. They help us keep tabs on how many times a specific event or action has occurred.

  2. Gauge: Gauges represent a single numerical value that can fluctuate up and down over time. They are often used to monitor metrics like memory usage, which can vary continuously.

  3. Histogram: Histograms are useful for tracking the distribution of observed values across different buckets. They are commonly used for metrics like request latencies, allowing us to understand how response times are distributed.

  4. Summary: Summaries provide insight into a sliding window of a time series and offer metrics for specific percentiles of the time series, often referred to as quantiles. This is particularly helpful for understanding latency-related metrics, such as request response times.

  5. Frequency: Frequency metrics count the occurrences of distinct string values. They are useful when you want to keep track of how often different events or conditions are happening in your application.

In the world of metrics, a Counter is a metric that represents a single numerical value that can be both incremented and decremented over time. Think of it like a tally that keeps track of changes, such as the number of a particular type of request received by your application, whether it’s increasing or decreasing.

Unlike some other types of metrics (like gauges), where we’re interested in the value at a specific moment, with counters, we care about the cumulative value over time. This means it provides a running total of changes, which can go up and down, reflecting the dynamic nature of certain metrics.

To create a counter, you can use the Metric.counter constructor in your code. You have the option to specify the type of the counter as either number or bigint. Here’s how you can do it:

1
import {
import Metric
Metric
} from "effect"
2
3
const
const numberCounter: Metric.Metric.Counter<number>
numberCounter
=
import Metric
Metric
.
const counter: (name: string, options?: { readonly description?: string | undefined; readonly bigint?: false | undefined; readonly incremental?: boolean | undefined; }) => Metric.Metric<in out Type, in In, out Out>.Counter<number> (+1 overload)

Represents a Counter metric that tracks cumulative numerical values over time. Counters can be incremented and decremented and provide a running total of changes.

counter
("request_count", {
4
(property) description?: string | undefined
description
: "A counter for tracking requests"
5
})
6
7
const
const bigintCounter: Metric.Metric.Counter<bigint>
bigintCounter
=
import Metric
Metric
.
const counter: (name: string, options: { readonly description?: string | undefined; readonly bigint: true; readonly incremental?: boolean | undefined; }) => Metric.Metric<in out Type, in In, out Out>.Counter<bigint> (+1 overload)

Represents a Counter metric that tracks cumulative numerical values over time. Counters can be incremented and decremented and provide a running total of changes.

counter
("error_count", {
8
(property) description?: string | undefined
description
: "A counter for tracking errors",
9
(property) bigint: true
bigint
: true
10
})

If you wish to create a counter that only increases its value, you can utilize the incremental: true option as follows:

1
import {
import Metric
Metric
} from "effect"
2
3
const
const incrementalCounter: Metric.Metric.Counter<number>
incrementalCounter
=
import Metric
Metric
.
const counter: (name: string, options?: { readonly description?: string | undefined; readonly bigint?: false | undefined; readonly incremental?: boolean | undefined; }) => Metric.Metric<in out Type, in In, out Out>.Counter<number> (+1 overload)

Represents a Counter metric that tracks cumulative numerical values over time. Counters can be incremented and decremented and provide a running total of changes.

counter
("count", {
4
(property) description?: string | undefined
description
: "a counter that only increases its value",
5
(property) incremental?: boolean | undefined
incremental
: true
6
})

With this configuration, Effect ensures that non-incremental updates have no impact on the counter, making it exclusively suitable for counting upwards.

Counters are incredibly useful when you need to keep track of cumulative values that can both increase and decrease over time. So, when should you use counters?

  1. Tracking a Value Over Time: If you need to monitor something that consistently increases over time, like the number of incoming requests, counters are your go-to choice.

  2. Measuring Growth Rates: Counters are also handy when you want to measure how fast something is growing. For instance, you can use them to keep tabs on request rates.

Counters find application in various scenarios, including:

  • Request Counts: Monitoring the number of incoming requests to your server.

  • Completed Tasks: Keeping track of how many tasks or processes have been successfully completed.

  • Error Counts: Counting the occurrences of errors in your application.

Here’s a practical example of creating and using a counter in your code:

1
import {
import Metric
Metric
,
import Effect
Effect
,
import Console
Console
} from "effect"
2
3
// Create a counter named 'task_count' and increment it by 1 every time
4
// it is invoked
5
const
const taskCount: Metric.Metric<MetricKeyType.Counter<number>, unknown, MetricState.Counter<number>>
taskCount
=
import Metric
Metric
.
const counter: (name: string, options?: { readonly description?: string | undefined; readonly bigint?: false | undefined; readonly incremental?: boolean | undefined; }) => Metric.Metric<in out Type, in In, out Out>.Counter<number> (+1 overload)

Represents a Counter metric that tracks cumulative numerical values over time. Counters can be incremented and decremented and provide a running total of changes.

counter
("task_count").
(method) Pipeable.pipe<Metric.Metric<in out Type, in In, out Out>.Counter<number>, Metric.Metric<MetricKeyType<in In, out Out>.Counter<number>, unknown, MetricState<in A>.Counter<number>>>(this: Metric.Metric.Counter<...>, ab: (_: Metric.Metric.Counter<number>) => Metric.Metric<...>): Metric.Metric<...> (+21 overloads)
pipe
(
6
import Metric
Metric
.
const withConstantInput: <number>(input: number) => <Type, Out>(self: Metric.Metric<Type, number, Out>) => Metric.Metric<Type, unknown, Out> (+1 overload)

Returns a new metric that is powered by this one, but which accepts updates of any type, and translates them to updates with the specified constant update value.

withConstantInput
(1)
7
)
8
9
const
const task1: Effect.Effect<number, never, never>
task1
=
import Effect
Effect
.
const succeed: <number>(value: number) => Effect.Effect<number, never, never>
succeed
(1).
(method) Pipeable.pipe<Effect.Effect<number, never, never>, Effect.Effect<number, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<number, never, never>) => Effect.Effect<number, never, never>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect
Effect
.
const delay: (duration: DurationInput) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> (+1 overload)

Returns an effect that is delayed from this effect by the specified `Duration`.

delay
("100 millis"))
10
const
const task2: Effect.Effect<number, never, never>
task2
=
import Effect
Effect
.
const succeed: <number>(value: number) => Effect.Effect<number, never, never>
succeed
(2).
(method) Pipeable.pipe<Effect.Effect<number, never, never>, Effect.Effect<number, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<number, never, never>) => Effect.Effect<number, never, never>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect
Effect
.
const delay: (duration: DurationInput) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> (+1 overload)

Returns an effect that is delayed from this effect by the specified `Duration`.

delay
("200 millis"))
11
12
const
const program: Effect.Effect<number, never, never>
program
=
import Effect
Effect
.
const gen: <YieldWrap<Effect.Effect<number, never, never>>, number>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<number, never, never>>, number, never>) => Effect.Effect<...> (+1 overload)
gen
(function* () {
13
const
const a: number
a
= yield*
const taskCount: Metric.Metric <number, never, never>(effect: Effect.Effect<number, never, never>) => Effect.Effect<number, never, never>
taskCount
(
const task1: Effect.Effect<number, never, never>
task1
)
14
const
const b: number
b
= yield*
const taskCount: Metric.Metric <number, never, never>(effect: Effect.Effect<number, never, never>) => Effect.Effect<number, never, never>
taskCount
(
const task2: Effect.Effect<number, never, never>
task2
)
15
return
const a: number
a
+
const b: number
b
16
})
17
18
const
const showMetric: Effect.Effect<void, never, never>
showMetric
=
import Metric
Metric
.
const value: <MetricKeyType<in In, out Out>.Counter<number>, unknown, MetricState<in A>.Counter<number>>(self: Metric.Metric<MetricKeyType.Counter<number>, unknown, MetricState.Counter<number>>) => Effect.Effect<...>

Retrieves a snapshot of the value of the metric at this moment in time.

value
(
const taskCount: Metric.Metric<MetricKeyType.Counter<number>, unknown, MetricState.Counter<number>>
taskCount
).
(method) Pipeable.pipe<Effect.Effect<MetricState<in A>.Counter<number>, never, never>, Effect.Effect<void, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<MetricState.Counter<number>, never, never>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
19
import Effect
Effect
.
const andThen: <MetricState<in A>.Counter<number>, Effect.Effect<void, never, never>>(f: (a: MetricState.Counter<number>) => Effect.Effect<void, never, never>) => <E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+3 overloads)

Executes a sequence of two actions, typically two `Effect`s, where the second action can depend on the result of the first action. The `that` action can take various forms: - a value - a function returning a value - a promise - a function returning a promise - an effect - a function returning an effect

andThen
(
import Console
Console
.
const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>
log
)
20
)
21
22
import Effect
Effect
.
const runPromise: <number, never>(effect: Effect.Effect<number, never, never>, options?: { readonly signal?: AbortSignal; } | undefined) => Promise<number>

Runs an `Effect` workflow, returning a `Promise` which resolves with the result of the workflow or rejects with an error.

runPromise
(
const program: Effect.Effect<number, never, never>
program
.
(method) Pipeable.pipe<Effect.Effect<number, never, never>, Effect.Effect<number, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<number, never, never>) => Effect.Effect<number, never, never>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect
Effect
.
const tap: <number, Effect.Effect<void, never, never>>(f: (a: number) => Effect.Effect<void, never, never>) => <E, R>(self: Effect.Effect<number, E, R>) => Effect.Effect<...> (+7 overloads)
tap
(() =>
const showMetric: Effect.Effect<void, never, never>
showMetric
))).
(method) Promise<number>.then<void, never>(onfulfilled?: ((value: number) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

then
(
23
namespace console var console: Console

The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```

console
.
(method) globalThis.Console.log(message?: any, ...optionalParams: any[]): void

Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.

log
24
)
25
/*
26
Output:
27
CounterState {
28
count: 2,
29
...
30
}
31
3
32
*/

In this example, we create a counter called taskCount, which is incremented by 1 each time it’s invoked. We then use it to monitor the number of times certain tasks are executed. The result provides valuable insights into the cumulative count of these tasks.

It’s worth noting that applying the taskCount metric to an effect doesn’t change its type. So, if task1 has a type of Effect<number>, then taskCount(task1) still has the same type, Effect<number>.

In the world of metrics, a Gauge is a metric that represents a single numerical value that can be set or adjusted. Think of it as a dynamic variable that can change over time. One common use case for a gauge is to monitor something like the current memory usage of your application.

Unlike counters, where we’re interested in cumulative values over time, with gauges, our focus is on the current value at a specific point in time.

To create a gauge, you can use the Metric.gauge constructor in your code. You can specify the type of the gauge as either number or bigint. Here’s how you can do it:

1
import {
import Metric
Metric
} from "effect"
2
3
const
const numberGauge: Metric.Metric.Gauge<number>
numberGauge
=
import Metric
Metric
.
const gauge: (name: string, options?: { readonly description?: string | undefined; readonly bigint?: false | undefined; }) => Metric.Metric<in out Type, in In, out Out>.Gauge<number> (+1 overload)

Represents a Gauge metric that tracks and reports a single numerical value at a specific moment. Gauges are suitable for metrics that represent instantaneous values, such as memory usage or CPU load.

gauge
("memory_usage", {
4
(property) description?: string | undefined
description
: "A gauge for memory usage"
5
})
6
7
const
const bigintGauge: Metric.Metric.Gauge<bigint>
bigintGauge
=
import Metric
Metric
.
const gauge: (name: string, options: { readonly description?: string | undefined; readonly bigint: true; }) => Metric.Metric<in out Type, in In, out Out>.Gauge<bigint> (+1 overload)

Represents a Gauge metric that tracks and reports a single numerical value at a specific moment. Gauges are suitable for metrics that represent instantaneous values, such as memory usage or CPU load.

gauge
("cpu_load", {
8
(property) description?: string | undefined
description
: "A gauge for CPU load",
9
(property) bigint: true
bigint
: true
10
})

Gauges are the best choice when you want to monitor values that can both increase and decrease, and you’re not interested in tracking their rates of change. In other words, gauges help us measure things that have a specific value at a particular moment:

  • Memory Usage: Keeping an eye on how much memory your application is using right now.

  • Queue Size: Monitoring the current size of a queue where tasks are waiting to be processed.

  • In-Progress Request Counts: Tracking the number of requests currently being handled by your server.

  • Temperature: Measuring the current temperature, which can fluctuate up and down.

Let’s look at a practical example of creating and using a gauge in your code:

1
import {
import Metric
Metric
,
import Effect
Effect
,
import Random
Random
,
import Console
Console
} from "effect"
2
3
const
const temperature: Metric.Metric.Gauge<number>
temperature
=
import Metric
Metric
.
const gauge: (name: string, options?: { readonly description?: string | undefined; readonly bigint?: false | undefined; }) => Metric.Metric<in out Type, in In, out Out>.Gauge<number> (+1 overload)

Represents a Gauge metric that tracks and reports a single numerical value at a specific moment. Gauges are suitable for metrics that represent instantaneous values, such as memory usage or CPU load.

gauge
("temperature")
4
5
const
const getTemperature: Effect.Effect<number, never, never>
getTemperature
=
import Effect
Effect
.
const gen: <YieldWrap<Effect.Effect<number, never, never>>, number>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<number, never, never>>, number, never>) => Effect.Effect<...> (+1 overload)
gen
(function* () {
6
const
const n: number
n
= yield*
import Random
Random
.
const nextIntBetween: (min: number, max: number) => Effect.Effect<number>

Returns the next integer value in the specified range from the pseudo-random number generator.

nextIntBetween
(-10, 10)
7
namespace console var console: Console

The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```

console
.
(method) globalThis.Console.log(message?: any, ...optionalParams: any[]): void

Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.

log
(`variation: ${
const n: number
n
}`)
8
return
const n: number
n
9
})
10
11
const
const program: Effect.Effect<number[], never, never>
program
=
import Effect
Effect
.
const gen: <YieldWrap<Effect.Effect<number, never, never>>, number[]>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<number, never, never>>, number[], never>) => Effect.Effect<...> (+1 overload)
gen
(function* () {
12
const
const series: number[]
series
:
interface Array<T>
Array
<number> = []
13
const series: number[]
series
.
(method) Array<number>.push(...items: number[]): number

Appends new elements to the end of an array, and returns the new length of the array.

push
(yield*
const temperature: Metric.Metric<in out Type, in In, out Out>.Gauge <number, never, never>(effect: Effect.Effect<number, never, never>) => Effect.Effect<number, never, never>
temperature
(
const getTemperature: Effect.Effect<number, never, never>
getTemperature
))
14
const series: number[]
series
.
(method) Array<number>.push(...items: number[]): number

Appends new elements to the end of an array, and returns the new length of the array.

push
(yield*
const temperature: Metric.Metric<in out Type, in In, out Out>.Gauge <number, never, never>(effect: Effect.Effect<number, never, never>) => Effect.Effect<number, never, never>
temperature
(
const getTemperature: Effect.Effect<number, never, never>
getTemperature
))
15
const series: number[]
series
.
(method) Array<number>.push(...items: number[]): number

Appends new elements to the end of an array, and returns the new length of the array.

push
(yield*
const temperature: Metric.Metric<in out Type, in In, out Out>.Gauge <number, never, never>(effect: Effect.Effect<number, never, never>) => Effect.Effect<number, never, never>
temperature
(
const getTemperature: Effect.Effect<number, never, never>
getTemperature
))
16
return
const series: number[]
series
17
})
18
19
const
const showMetric: Effect.Effect<void, never, never>
showMetric
=
import Metric
Metric
.
const value: <MetricKeyType<in In, out Out>.Gauge<number>, number, MetricState<in A>.Gauge<number>>(self: Metric.Metric<MetricKeyType.Gauge<number>, number, MetricState.Gauge<number>>) => Effect.Effect<...>

Retrieves a snapshot of the value of the metric at this moment in time.

value
(
const temperature: Metric.Metric.Gauge<number>
temperature
).
(method) Pipeable.pipe<Effect.Effect<MetricState<in A>.Gauge<number>, never, never>, Effect.Effect<void, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<MetricState.Gauge<number>, never, never>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
20
import Effect
Effect
.
const andThen: <MetricState<in A>.Gauge<number>, Effect.Effect<void, never, never>>(f: (a: MetricState.Gauge<number>) => Effect.Effect<void, never, never>) => <E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+3 overloads)

Executes a sequence of two actions, typically two `Effect`s, where the second action can depend on the result of the first action. The `that` action can take various forms: - a value - a function returning a value - a promise - a function returning a promise - an effect - a function returning an effect

andThen
(
import Console
Console
.
const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>
log
)
21
)
22
23
import Effect
Effect
.
const runPromise: <number[], never>(effect: Effect.Effect<number[], never, never>, options?: { readonly signal?: AbortSignal; } | undefined) => Promise<number[]>

Runs an `Effect` workflow, returning a `Promise` which resolves with the result of the workflow or rejects with an error.

runPromise
(
const program: Effect.Effect<number[], never, never>
program
.
(method) Pipeable.pipe<Effect.Effect<number[], never, never>, Effect.Effect<number[], never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<number[], never, never>) => Effect.Effect<number[], never, never>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect
Effect
.
const tap: <number[], Effect.Effect<void, never, never>>(f: (a: number[]) => Effect.Effect<void, never, never>) => <E, R>(self: Effect.Effect<number[], E, R>) => Effect.Effect<...> (+7 overloads)
tap
(() =>
const showMetric: Effect.Effect<void, never, never>
showMetric
))).
(method) Promise<number[]>.then<void, never>(onfulfilled?: ((value: number[]) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

then
(
24
namespace console var console: Console

The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```

console
.
(method) globalThis.Console.log(message?: any, ...optionalParams: any[]): void

Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.

log
25
)
26
/*
27
Output:
28
variation: 6
29
variation: -4
30
variation: -9
31
GaugeState {
32
value: -9,
33
...
34
}
35
[ 6, -4, -9 ]
36
*/

A Histogram is a metric that helps us understand how a collection of numerical values is distributed over time. Instead of just focusing on the individual values, histograms organize these values into distinct intervals, called buckets, and record the frequency of values within each bucket.

Histograms are valuable because they not only represent the actual values but also provide insights into their distribution. They are like a summary of a dataset, breaking down the data into buckets and showing how many data points fall into each one.

In a histogram, each incoming sample is assigned to a predefined bucket. When a data point arrives, it increases the count for the corresponding bucket, and then the individual sample is discarded. This bucketed approach allows us to aggregate data across multiple instances. Histograms are especially useful for measuring percentiles, helping us estimate specific percentiles by looking at bucket counts.

  • Observing Values: Histograms observe numerical values and count how many observations fall into specific buckets. Each bucket has an upper boundary, and the count for a bucket increases by 1 if an observed value is less than or equal to the bucket’s upper boundary.

  • Overall Count: A histogram also keeps track of the total count of observed values and the sum of all observed values.

  • Inspired by Prometheus: The concept of histograms is inspired by Prometheus, a popular monitoring and alerting toolkit.

Histograms are widely used in software metrics for various purposes, especially in analyzing the performance of software systems. They are valuable for metrics such as response times, latencies, and throughput. By visualizing the distribution of these metrics in a histogram, developers can identify performance bottlenecks, outliers, or variations. This information helps in optimizing code, infrastructure, and system configurations to improve overall performance.

Histograms are the best choice in the following situations:

  • When you want to observe many values and later calculate percentiles of those observed values.

  • When you can estimate the range of values in advance, as histograms organize observations into predefined buckets.

  • When you don’t require exact values due to the inherent lossy nature of bucketing data in histograms.

  • When you need to aggregate histograms across multiple instances.

In this example, we create a histogram with linear buckets, ranging from 0 to 100 in increments of 10, and an “Infinity” bucket. It’s suitable for effects yielding a number. The program then generates random values, records them in the histogram, and displays the histogram’s state.

1
import {
import Effect
Effect
,
import Metric
Metric
,
import MetricBoundaries
MetricBoundaries
,
import Random
Random
} from "effect"
2
3
const
const latencyHistogram: Metric.Metric<MetricKeyType.Histogram, number, MetricState.Histogram>
latencyHistogram
=
import Metric
Metric
.
const histogram: (name: string, boundaries: MetricBoundaries.MetricBoundaries, description?: string) => Metric.Metric<MetricKeyType<in In, out Out>.Histogram, number, MetricState<in A>.Histogram>

Represents a Histogram metric that records observations in specified value boundaries. Histogram metrics are useful for measuring the distribution of values within a range.

histogram
(
4
"request_latency",
5
import MetricBoundaries
MetricBoundaries
.
const linear: (options: { readonly start: number; readonly width: number; readonly count: number; }) => MetricBoundaries.MetricBoundaries

A helper method to create histogram bucket boundaries for a histogram with linear increasing values.

linear
({
(property) start: number
start
: 0,
(property) width: number
width
: 10,
(property) count: number
count
: 11 })
6
)
7
8
const
const program: Effect.Effect<number, never, never>
program
=
const latencyHistogram: Metric.Metric <number, never, never>(effect: Effect.Effect<number, never, never>) => Effect.Effect<number, never, never>
latencyHistogram
(
import Random
Random
.
const nextIntBetween: (min: number, max: number) => Effect.Effect<number>

Returns the next integer value in the specified range from the pseudo-random number generator.

nextIntBetween
(1, 120)).
(method) Pipeable.pipe<Effect.Effect<number, never, never>, Effect.Effect<number, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<number, never, never>) => Effect.Effect<number, never, never>): Effect.Effect<...> (+21 overloads)
pipe
(
9
import Effect
Effect
.
const repeatN: (n: number) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> (+1 overload)

The `repeatN` function returns a new effect that repeats the specified effect a given number of times or until the first failure. The repeats are in addition to the initial execution, so `Effect.repeatN(action, 1)` executes `action` once initially and then repeats it one additional time if it succeeds.

repeatN
(99)
10
)
11
12
import Effect
Effect
.
const runPromise: <MetricState<in A>.Histogram, never>(effect: Effect.Effect<MetricState.Histogram, never, never>, options?: { readonly signal?: AbortSignal; } | undefined) => Promise<...>

Runs an `Effect` workflow, returning a `Promise` which resolves with the result of the workflow or rejects with an error.

runPromise
(
13
const program: Effect.Effect<number, never, never>
program
.
(method) Pipeable.pipe<Effect.Effect<number, never, never>, Effect.Effect<MetricState<in A>.Histogram, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<number, never, never>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect
Effect
.
const andThen: <Effect.Effect<MetricState<in A>.Histogram, never, never>>(f: Effect.Effect<MetricState.Histogram, never, never>) => <A, E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+3 overloads)

Executes a sequence of two actions, typically two `Effect`s, where the second action can depend on the result of the first action. The `that` action can take various forms: - a value - a function returning a value - a promise - a function returning a promise - an effect - a function returning an effect

andThen
(
import Metric
Metric
.
const value: <MetricKeyType<in In, out Out>.Histogram, number, MetricState<in A>.Histogram>(self: Metric.Metric<MetricKeyType.Histogram, number, MetricState.Histogram>) => Effect.Effect<...>

Retrieves a snapshot of the value of the metric at this moment in time.

value
(
const latencyHistogram: Metric.Metric<MetricKeyType.Histogram, number, MetricState.Histogram>
latencyHistogram
)))
14
).
(method) Promise<MetricState<in A>.Histogram>.then<void, never>(onfulfilled?: ((value: MetricState<in A>.Histogram) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

then
((
(parameter) histogramState: MetricState.Histogram
histogramState
) =>
namespace console var console: Console

The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```

console
.
(method) Console.log(message?: any, ...optionalParams: any[]): void

Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.

log
("%o",
(parameter) histogramState: MetricState.Histogram
histogramState
))
15
/*
16
Output:
17
HistogramState {
18
buckets: [
19
[ 0, 0 ],
20
[ 10, 7 ],
21
[ 20, 11 ],
22
[ 30, 20 ],
23
[ 40, 27 ],
24
[ 50, 38 ],
25
[ 60, 53 ],
26
[ 70, 64 ],
27
[ 80, 73 ],
28
[ 90, 84 ],
29
[ Infinity, 100 ],
30
[length]: 11
31
],
32
count: 100,
33
min: 1,
34
max: 119,
35
sum: 5980,
36
...
37
}
38
*/

This example demonstrates the use of a timer metric to track workflow durations. It generates random values, simulates waiting times, records durations in the timer metric, and displays the histogram’s state.

1
import {
import Metric
Metric
,
import Array
Array
,
import Random
Random
,
import Effect
Effect
} from "effect"
2
3
// Metric<Histogram, Duration, Histogram>
4
const
const timer: Metric.Metric<MetricKeyType.Histogram, Duration, MetricState.Histogram>
timer
=
import Metric
Metric
.
const timerWithBoundaries: (name: string, boundaries: ReadonlyArray<number>, description?: string) => Metric.Metric<MetricKeyType<in In, out Out>.Histogram, Duration, MetricState<in A>.Histogram>

Creates a timer metric, based on a histogram created from the provided boundaries, which keeps track of durations in milliseconds. The unit of time will automatically be added to the metric as a tag (i.e. `"time_unit: milliseconds"`).

timerWithBoundaries
("timer",
import Array
Array
.
const range: (start: number, end: number) => Array.NonEmptyArray<number>

Return a `NonEmptyArray` containing a range of integers, including both endpoints.

range
(1, 10))
5
6
const
const program: Effect.Effect<void, never, never>
program
=
import Random
Random
.
const nextIntBetween: (min: number, max: number) => Effect.Effect<number>

Returns the next integer value in the specified range from the pseudo-random number generator.

nextIntBetween
(1, 10).
(method) Pipeable.pipe<Effect.Effect<number, never, never>, Effect.Effect<void, never, never>, Effect.Effect<void, never, never>, Effect.Effect<void, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>, bc: (_: Effect.Effect<...>) => Effect.Effect<...>, cd: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
7
import Effect
Effect
.
const andThen: <number, Effect.Effect<void, never, never>>(f: (a: number) => Effect.Effect<void, never, never>) => <E, R>(self: Effect.Effect<number, E, R>) => Effect.Effect<...> (+3 overloads)

Executes a sequence of two actions, typically two `Effect`s, where the second action can depend on the result of the first action. The `that` action can take various forms: - a value - a function returning a value - a promise - a function returning a promise - an effect - a function returning an effect

andThen
((
(parameter) n: number
n
) =>
import Effect
Effect
.
const sleep: (duration: DurationInput) => Effect.Effect<void>

Returns an effect that suspends for the specified duration. This method is asynchronous, and does not actually block the fiber executing the effect.

sleep
(`${
(parameter) n: number
n
} millis`)),
8
import Metric
Metric
.
const trackDuration: <MetricKeyType<in In, out Out>.Histogram, MetricState<in A>.Histogram>(metric: Metric.Metric<MetricKeyType.Histogram, Duration, MetricState.Histogram>) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> (+1 overload)

Returns an aspect that will update this metric with the duration that the effect takes to execute. To call this method, the input type of the metric must be `Duration`.

trackDuration
(
const timer: Metric.Metric<MetricKeyType.Histogram, Duration, MetricState.Histogram>
timer
),
9
import Effect
Effect
.
const repeatN: (n: number) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> (+1 overload)

The `repeatN` function returns a new effect that repeats the specified effect a given number of times or until the first failure. The repeats are in addition to the initial execution, so `Effect.repeatN(action, 1)` executes `action` once initially and then repeats it one additional time if it succeeds.

repeatN
(99)
10
)
11
12
import Effect
Effect
.
const runPromise: <MetricState<in A>.Histogram, never>(effect: Effect.Effect<MetricState.Histogram, never, never>, options?: { readonly signal?: AbortSignal; } | undefined) => Promise<...>

Runs an `Effect` workflow, returning a `Promise` which resolves with the result of the workflow or rejects with an error.

runPromise
(
const program: Effect.Effect<void, never, never>
program
.
(method) Pipeable.pipe<Effect.Effect<void, never, never>, Effect.Effect<MetricState<in A>.Histogram, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<void, never, never>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect
Effect
.
const andThen: <Effect.Effect<MetricState<in A>.Histogram, never, never>>(f: Effect.Effect<MetricState.Histogram, never, never>) => <A, E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+3 overloads)

Executes a sequence of two actions, typically two `Effect`s, where the second action can depend on the result of the first action. The `that` action can take various forms: - a value - a function returning a value - a promise - a function returning a promise - an effect - a function returning an effect

andThen
(
import Metric
Metric
.
const value: <MetricKeyType<in In, out Out>.Histogram, Duration, MetricState<in A>.Histogram>(self: Metric.Metric<MetricKeyType.Histogram, Duration, MetricState.Histogram>) => Effect.Effect<...>

Retrieves a snapshot of the value of the metric at this moment in time.

value
(
const timer: Metric.Metric<MetricKeyType.Histogram, Duration, MetricState.Histogram>
timer
)))).
(method) Promise<MetricState<in A>.Histogram>.then<void, never>(onfulfilled?: ((value: MetricState<in A>.Histogram) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

then
(
13
(
(parameter) histogramState: MetricState.Histogram
histogramState
) =>
namespace console var console: Console

The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```

console
.
(method) Console.log(message?: any, ...optionalParams: any[]): void

Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.

log
("%o",
(parameter) histogramState: MetricState.Histogram
histogramState
)
14
)
15
/*
16
Output:
17
HistogramState {
18
buckets: [
19
[ 1, 3 ],
20
[ 2, 13 ],
21
[ 3, 17 ],
22
[ 4, 26 ],
23
[ 5, 35 ],
24
[ 6, 43 ],
25
[ 7, 53 ],
26
[ 8, 56 ],
27
[ 9, 65 ],
28
[ 10, 72 ],
29
[ Infinity, 100 ],
30
[length]: 11
31
],
32
count: 100,
33
min: 0.25797,
34
max: 12.25421,
35
sum: 683.0266810000002,
36
...
37
}
38
*/

These examples showcase how histograms can be used to analyze and understand the distribution of data in various scenarios, making them a valuable tool in software metrics.

A Summary is a metric that provides valuable insights into a time series by calculating specific percentiles. These percentiles help us understand the distribution of values within the time series. Imagine you’re tracking response times for requests over the past hour; you might be interested in percentiles like the 50th, 90th, 95th, and 99th to analyze performance.

Summaries, much like histograms, observe number values. However, instead of directly modifying bucket counters and discarding samples, summaries retain the observed samples in their internal state. To prevent uncontrolled growth of the sample set, a summary is configured with a maximum age maxAge and a maximum size maxSize. When calculating statistics, it uses a maximum of maxSize samples, all of which are not older than maxAge.

Think of the set of samples as a sliding window over the most recent observations that meet the specified conditions.

Summaries are primarily used to calculate quantiles over the current set of samples. A quantile is defined by a number value q with 0 <= q <= 1 and results in a number as well.

The value of a specific quantile q is determined as the maximum value v from the current sample buffer (with size n) where at most q * n values from the sample buffer are less than or equal to v.

Common quantiles for observation include 0.5 (the median) and 0.95. Quantiles are particularly useful for monitoring Service Level Agreements (SLAs).

The Effect Metrics API also allows summaries to be configured with an error margin error. This margin is applied to the count of values, so a quantile q for a set of size s resolves to value v if the count n of values less than or equal to v falls within the range (1 - error)q * s <= n <= (1 + error)q.

Summaries are excellent for monitoring latencies when histograms are not the right fit due to accuracy concerns. They shine in situations where:

  • The range of values is not well-estimated, making histograms less suitable.

  • There’s no need for aggregation or averaging across multiple instances, as summary calculations are performed on the application side.

Let’s create a summary to hold 100 samples, with a maximum sample age of 1 day, and an error margin of 3%. This summary should report the 10%, 50%, and 90% quantiles. It can be applied to effects yielding integers:

1
import {
import Metric
Metric
,
import Random
Random
,
import Effect
Effect
} from "effect"
2
3
const
const responseTimeSummary: Metric.Metric.Summary<number>
responseTimeSummary
=
import Metric
Metric
.
const summary: (options: { readonly name: string; readonly maxAge: DurationInput; readonly maxSize: number; readonly error: number; readonly quantiles: ReadonlyArray<number>; readonly description?: string | undefined; }) => Metric.Metric<in out Type, in In, out Out>.Summary<number>

Creates a Summary metric that records observations and calculates quantiles. Summary metrics provide statistical information about a set of values, including quantiles.

summary
({
4
(property) name: string
name
: "response_time_summary",
5
(property) maxAge: DurationInput
maxAge
: "1 day",
6
(property) maxSize: number
maxSize
: 100,
7
(property) error: number
error
: 0.03,
8
(property) quantiles: readonly number[]
quantiles
: [0.1, 0.5, 0.9]
9
})
10
11
const
const program: Effect.Effect<number, never, never>
program
=
const responseTimeSummary: Metric.Metric<in out Type, in In, out Out>.Summary <number, never, never>(effect: Effect.Effect<number, never, never>) => Effect.Effect<number, never, never>
responseTimeSummary
(
import Random
Random
.
const nextIntBetween: (min: number, max: number) => Effect.Effect<number>

Returns the next integer value in the specified range from the pseudo-random number generator.

nextIntBetween
(1, 120)).
(method) Pipeable.pipe<Effect.Effect<number, never, never>, Effect.Effect<number, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<number, never, never>) => Effect.Effect<number, never, never>): Effect.Effect<...> (+21 overloads)
pipe
(
12
import Effect
Effect
.
const repeatN: (n: number) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> (+1 overload)

The `repeatN` function returns a new effect that repeats the specified effect a given number of times or until the first failure. The repeats are in addition to the initial execution, so `Effect.repeatN(action, 1)` executes `action` once initially and then repeats it one additional time if it succeeds.

repeatN
(99)
13
)
14
15
import Effect
Effect
.
const runPromise: <MetricState<in A>.Summary, never>(effect: Effect.Effect<MetricState.Summary, never, never>, options?: { readonly signal?: AbortSignal; } | undefined) => Promise<...>

Runs an `Effect` workflow, returning a `Promise` which resolves with the result of the workflow or rejects with an error.

runPromise
(
16
const program: Effect.Effect<number, never, never>
program
.
(method) Pipeable.pipe<Effect.Effect<number, never, never>, Effect.Effect<MetricState<in A>.Summary, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<number, never, never>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect
Effect
.
const andThen: <Effect.Effect<MetricState<in A>.Summary, never, never>>(f: Effect.Effect<MetricState.Summary, never, never>) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<...> (+3 overloads)

Executes a sequence of two actions, typically two `Effect`s, where the second action can depend on the result of the first action. The `that` action can take various forms: - a value - a function returning a value - a promise - a function returning a promise - an effect - a function returning an effect

andThen
(
import Metric
Metric
.
const value: <MetricKeyType<in In, out Out>.Summary, number, MetricState<in A>.Summary>(self: Metric.Metric<MetricKeyType.Summary, number, MetricState.Summary>) => Effect.Effect<...>

Retrieves a snapshot of the value of the metric at this moment in time.

value
(
const responseTimeSummary: Metric.Metric.Summary<number>
responseTimeSummary
)))
17
).
(method) Promise<MetricState<in A>.Summary>.then<void, never>(onfulfilled?: ((value: MetricState<in A>.Summary) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

then
((
(parameter) summaryState: MetricState.Summary
summaryState
) =>
namespace console var console: Console

The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```

console
.
(method) Console.log(message?: any, ...optionalParams: any[]): void

Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.

log
("%o",
(parameter) summaryState: MetricState.Summary
summaryState
))
18
/*
19
Output:
20
SummaryState {
21
error: 0.03,
22
quantiles: [
23
[ 0.1, { _id: 'Option', _tag: 'Some', value: 17 } ],
24
[ 0.5, { _id: 'Option', _tag: 'Some', value: 62 } ],
25
[ 0.9, { _id: 'Option', _tag: 'Some', value: 109 } ]
26
],
27
count: 100,
28
min: 4,
29
max: 119,
30
sum: 6058,
31
...
32
}
33
*/

Frequencies are metrics that help us count the occurrences of specific values. Think of them as a set of counters, each associated with a unique value. When new values are observed, frequencies automatically create new counters for them.

Frequencies are invaluable for counting the occurrences of distinct string values. Consider using frequencies in scenarios like:

  • Tracking the number of invocations for each service in an application that uses logical names for its services.

  • Monitoring the frequency of different types of failures.

Let’s create a Frequency to observe the occurrences of unique strings. This example can be applied to effects that yield a string:

1
import {
import Metric
Metric
,
import Random
Random
,
import Effect
Effect
} from "effect"
2
3
const
const errorFrequency: Metric.Metric.Frequency<string>
errorFrequency
=
import Metric
Metric
.
const frequency: (name: string, options?: { readonly description?: string | undefined; readonly preregisteredWords?: ReadonlyArray<string> | undefined; } | undefined) => Metric.Metric<in out Type, in In, out Out>.Frequency<string>

Creates a Frequency metric to count occurrences of events. Frequency metrics are used to count the number of times specific events or incidents occur.

frequency
("error_frequency")
4
5
const
const program: Effect.Effect<string, never, never>
program
=
const errorFrequency: Metric.Metric<in out Type, in In, out Out>.Frequency <string, never, never>(effect: Effect.Effect<string, never, never>) => Effect.Effect<string, never, never>
errorFrequency
(
6
import Random
Random
.
const nextIntBetween: (min: number, max: number) => Effect.Effect<number>

Returns the next integer value in the specified range from the pseudo-random number generator.

nextIntBetween
(1, 10).
(method) Pipeable.pipe<Effect.Effect<number, never, never>, Effect.Effect<string, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<number, never, never>) => Effect.Effect<string, never, never>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect
Effect
.
const andThen: <number, string>(f: (a: number) => string) => <E, R>(self: Effect.Effect<number, E, R>) => Effect.Effect<string, E, R> (+3 overloads)

Executes a sequence of two actions, typically two `Effect`s, where the second action can depend on the result of the first action. The `that` action can take various forms: - a value - a function returning a value - a promise - a function returning a promise - an effect - a function returning an effect

andThen
((
(parameter) n: number
n
) => `Error-${
(parameter) n: number
n
}`))
7
).
(method) Pipeable.pipe<Effect.Effect<string, never, never>, Effect.Effect<string, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<string, never, never>) => Effect.Effect<string, never, never>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect
Effect
.
const repeatN: (n: number) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> (+1 overload)

The `repeatN` function returns a new effect that repeats the specified effect a given number of times or until the first failure. The repeats are in addition to the initial execution, so `Effect.repeatN(action, 1)` executes `action` once initially and then repeats it one additional time if it succeeds.

repeatN
(99))
8
9
import Effect
Effect
.
const runPromise: <MetricState<in A>.Frequency, never>(effect: Effect.Effect<MetricState.Frequency, never, never>, options?: { readonly signal?: AbortSignal; } | undefined) => Promise<...>

Runs an `Effect` workflow, returning a `Promise` which resolves with the result of the workflow or rejects with an error.

runPromise
(
10
const program: Effect.Effect<string, never, never>
program
.
(method) Pipeable.pipe<Effect.Effect<string, never, never>, Effect.Effect<MetricState<in A>.Frequency, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<string, never, never>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect
Effect
.
const andThen: <Effect.Effect<MetricState<in A>.Frequency, never, never>>(f: Effect.Effect<MetricState.Frequency, never, never>) => <A, E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+3 overloads)

Executes a sequence of two actions, typically two `Effect`s, where the second action can depend on the result of the first action. The `that` action can take various forms: - a value - a function returning a value - a promise - a function returning a promise - an effect - a function returning an effect

andThen
(
import Metric
Metric
.
const value: <MetricKeyType<in In, out Out>.Frequency, string, MetricState<in A>.Frequency>(self: Metric.Metric<MetricKeyType.Frequency, string, MetricState.Frequency>) => Effect.Effect<...>

Retrieves a snapshot of the value of the metric at this moment in time.

value
(
const errorFrequency: Metric.Metric.Frequency<string>
errorFrequency
)))
11
).
(method) Promise<MetricState<in A>.Frequency>.then<void, never>(onfulfilled?: ((value: MetricState<in A>.Frequency) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

then
((
(parameter) frequencyState: MetricState.Frequency
frequencyState
) =>
namespace console var console: Console

The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```

console
.
(method) Console.log(message?: any, ...optionalParams: any[]): void

Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.

log
("%o",
(parameter) frequencyState: MetricState.Frequency
frequencyState
))
12
/*
13
Output:
14
FrequencyState {
15
occurrences: Map(9) {
16
'Error-7' => 12,
17
'Error-2' => 12,
18
'Error-4' => 14,
19
'Error-1' => 14,
20
'Error-9' => 8,
21
'Error-6' => 11,
22
'Error-5' => 9,
23
'Error-3' => 14,
24
'Error-8' => 6
25
},
26
...
27
}
28
*/

When creating metrics, you can add tags to them. Tags are key-value pairs that provide additional context, helping in categorizing and filtering metrics. This makes it easier to analyze and monitor specific aspects of your application.

Use Effect.tagMetrics to apply tags to all metrics created in the same context. This is useful for adding common tags that apply to multiple metrics.

1
import {
import Metric
Metric
,
import Effect
Effect
} from "effect"
2
3
const
const taskCount: Metric.Metric.Counter<number>
taskCount
=
import Metric
Metric
.
const counter: (name: string, options?: { readonly description?: string | undefined; readonly bigint?: false | undefined; readonly incremental?: boolean | undefined; }) => Metric.Metric<in out Type, in In, out Out>.Counter<number> (+1 overload)

Represents a Counter metric that tracks cumulative numerical values over time. Counters can be incremented and decremented and provide a running total of changes.

counter
("task_count")
4
const
const task1: Effect.Effect<number, never, never>
task1
=
import Effect
Effect
.
const succeed: <number>(value: number) => Effect.Effect<number, never, never>
succeed
(1).
(method) Pipeable.pipe<Effect.Effect<number, never, never>, Effect.Effect<number, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<number, never, never>) => Effect.Effect<number, never, never>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect
Effect
.
const delay: (duration: DurationInput) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> (+1 overload)

Returns an effect that is delayed from this effect by the specified `Duration`.

delay
("100 millis"))
5
6
import Effect
Effect
.
const gen: <YieldWrap<Effect.Effect<number, never, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<number, never, never>>, void, never>) => Effect.Effect<...> (+1 overload)
gen
(function* () {
7
yield*
const taskCount: Metric.Metric<in out Type, in In, out Out>.Counter <number, never, never>(effect: Effect.Effect<number, never, never>) => Effect.Effect<number, never, never>
taskCount
(
const task1: Effect.Effect<number, never, never>
task1
)
8
}).
(method) Pipeable.pipe<Effect.Effect<void, never, never>, Effect.Effect<void, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<void, never, never>) => Effect.Effect<void, never, never>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect
Effect
.
const tagMetrics: (key: string, value: string) => <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> (+3 overloads)

Tags each metric in this effect with the specific tag.

tagMetrics
("environment", "production"))

Alternatively, use Effect.tagMetricsScoped to apply tags within a specific scope.

For individual metrics, use Metric.tagged. This method allows you to apply tags to a specific metric.

1
import {
import Metric
Metric
} from "effect"
2
3
const
const counter: Metric.Metric<MetricKeyType<number, MetricState.Counter<number>> & { readonly incremental: boolean; readonly bigint: boolean; readonly [CounterKeyTypeTypeId]: CounterKeyTypeTypeId; }, number, MetricState.Counter<...>>
counter
=
import Metric
Metric
.
const counter: (name: string, options?: { readonly description?: string | undefined; readonly bigint?: false | undefined; readonly incremental?: boolean | undefined; }) => Metric.Metric<in out Type, in In, out Out>.Counter<number> (+1 overload)

Represents a Counter metric that tracks cumulative numerical values over time. Counters can be incremented and decremented and provide a running total of changes.

counter
("request_count").
(method) Pipeable.pipe<Metric.Metric<in out Type, in In, out Out>.Counter<number>, Metric.Metric<MetricKeyType<number, MetricState<in A>.Counter<number>> & { readonly incremental: boolean; readonly bigint: boolean; readonly [CounterKeyTypeTypeId]: CounterKeyTypeTypeId; }, number, MetricState.Counter<...>>>(this: Metric.Metric.Counter<...>, ab: (_: Metric.Metric.Counter<...>) => Metric.Metric<...>): Metric.Metric<...> (+21 overloads)
pipe
(
4
import Metric
Metric
.
const tagged: <MetricKeyType<number, MetricState<in A>.Counter<number>> & { readonly incremental: boolean; readonly bigint: boolean; readonly [CounterKeyTypeTypeId]: CounterKeyTypeTypeId; }, number, MetricState.Counter<...>>(key: string, value: string) => (self: Metric.Metric<...>) => Metric.Metric<...> (+1 overload)

Returns a new metric, which is identical in every way to this one, except the specified tags have been added to the tags of this metric.

tagged
("environment", "production")
5
)