(
name: string,
options: {
readonly description?: string | undefined
readonly attributes?: Metric.Attributes | undefined
readonly maxAge: Duration.Input
readonly maxSize: number
readonly quantiles: ReadonlyArray<number>
}
): Summary<number>Creates a Summary metric that records observations and calculates quantiles
which takes a value as input and uses the current time.
When to use
Use when you need a metric that records statistical information about a set of values, including quantiles.
Details
The optional description describes the summary, and attributes attach
dimensions to it. maxAge controls how long observations are retained,
maxSize controls how many observations are kept, and quantiles lists the
quantiles to calculate, such as [0.5, 0.9].
Example (Creating summary metrics)
import { Data, Duration, Effect, Metric } from "effect"
class SummaryError extends Data.TaggedError("SummaryError")<{
readonly operation: string
}> {}
const program = Effect.gen(function*() {
// Create a summary for API response times
const responseTimeSummary = Metric.summary("api_response_time", {
description: "API response time quantiles over 5-minute windows",
maxAge: Duration.minutes(5), // Keep observations for 5 minutes
maxSize: 1000, // Maximum 1000 observations in memory
quantiles: [0.5, 0.9, 0.95, 0.99] // 50th, 90th, 95th, 99th percentiles
})
// Create a summary for request payload sizes
const payloadSizeSummary = Metric.summary("request_payload_size", {
description: "Request payload size distribution over 2-minute windows",
maxAge: Duration.minutes(2), // Shorter window for recent trends
maxSize: 500, // Smaller buffer for memory efficiency
quantiles: [0.5, 0.75, 0.9], // Median, 75th, 90th percentiles
attributes: { service: "upload-service" }
})
// Record deterministic response times
const responseTimes = [82, 96, 104, 118, 135, 170, 210, 240]
for (const responseTime of responseTimes) {
yield* Metric.update(responseTimeSummary, responseTime)
}
// Record some payload sizes
yield* Metric.update(payloadSizeSummary, 1.2) // 1.2KB
yield* Metric.update(payloadSizeSummary, 5.8) // 5.8KB
yield* Metric.update(payloadSizeSummary, 15.6) // 15.6KB
yield* Metric.update(payloadSizeSummary, 3.4) // 3.4KB
// Get summary statistics with quantiles
const responseStats = yield* Metric.value(responseTimeSummary)
const payloadStats = yield* Metric.value(payloadSizeSummary)
console.log({
count: responseStats.count,
min: responseStats.min,
max: responseStats.max,
sum: responseStats.sum
}) // { count: 8, min: 82, max: 240, sum: 1155 }
console.log({
count: payloadStats.count,
min: payloadStats.min,
max: payloadStats.max,
sum: payloadStats.sum
}) // { count: 4, min: 1.2, max: 15.6, sum: 26 }
// Both summaries include quantile information for their configured windows.
return { responseStats, payloadStats }
})export const const summary: (
name: string,
options: {
readonly description?: string | undefined
readonly attributes?:
| Metric.Attributes
| undefined
readonly maxAge: Duration.Input
readonly maxSize: number
readonly quantiles: ReadonlyArray<number>
}
) => Summary<number>
Creates a Summary metric that records observations and calculates quantiles
which takes a value as input and uses the current time.
When to use
Use when you need a metric that records statistical information about a set
of values, including quantiles.
Details
The optional description describes the summary, and attributes attach
dimensions to it. maxAge controls how long observations are retained,
maxSize controls how many observations are kept, and quantiles lists the
quantiles to calculate, such as [0.5, 0.9].
Example (Creating summary metrics)
import { Data, Duration, Effect, Metric } from "effect"
class SummaryError extends Data.TaggedError("SummaryError")<{
readonly operation: string
}> {}
const program = Effect.gen(function*() {
// Create a summary for API response times
const responseTimeSummary = Metric.summary("api_response_time", {
description: "API response time quantiles over 5-minute windows",
maxAge: Duration.minutes(5), // Keep observations for 5 minutes
maxSize: 1000, // Maximum 1000 observations in memory
quantiles: [0.5, 0.9, 0.95, 0.99] // 50th, 90th, 95th, 99th percentiles
})
// Create a summary for request payload sizes
const payloadSizeSummary = Metric.summary("request_payload_size", {
description: "Request payload size distribution over 2-minute windows",
maxAge: Duration.minutes(2), // Shorter window for recent trends
maxSize: 500, // Smaller buffer for memory efficiency
quantiles: [0.5, 0.75, 0.9], // Median, 75th, 90th percentiles
attributes: { service: "upload-service" }
})
// Record deterministic response times
const responseTimes = [82, 96, 104, 118, 135, 170, 210, 240]
for (const responseTime of responseTimes) {
yield* Metric.update(responseTimeSummary, responseTime)
}
// Record some payload sizes
yield* Metric.update(payloadSizeSummary, 1.2) // 1.2KB
yield* Metric.update(payloadSizeSummary, 5.8) // 5.8KB
yield* Metric.update(payloadSizeSummary, 15.6) // 15.6KB
yield* Metric.update(payloadSizeSummary, 3.4) // 3.4KB
// Get summary statistics with quantiles
const responseStats = yield* Metric.value(responseTimeSummary)
const payloadStats = yield* Metric.value(payloadSizeSummary)
console.log({
count: responseStats.count,
min: responseStats.min,
max: responseStats.max,
sum: responseStats.sum
}) // { count: 8, min: 82, max: 240, sum: 1155 }
console.log({
count: payloadStats.count,
min: payloadStats.min,
max: payloadStats.max,
sum: payloadStats.sum
}) // { count: 4, min: 1.2, max: 15.6, sum: 26 }
// Both summaries include quantile information for their configured windows.
return { responseStats, payloadStats }
})
summary = (name: stringname: string, options: {
readonly description?: string | undefined
readonly attributes?:
| Metric.Attributes
| undefined
readonly maxAge: Duration.Input
readonly maxSize: number
readonly quantiles: ReadonlyArray<number>
}
options: {
readonly description?: string | undefineddescription?: string | undefined
readonly attributes?: Metric.Attributes | undefinedattributes?: Metric.type Metric<in Input, out State>.Attributes = Readonly<Record<string, string>> | readonly [string, string][]Union type for metric attributes that can be provided as either an object or array of tuples.
Example (Providing attributes in different formats)
import { Data, Effect, Metric } from "effect"
class AttributesError extends Data.TaggedError("AttributesError")<{
readonly operation: string
}> {}
const program = Effect.gen(function*() {
// Different ways to specify attributes
const attributesAsObject = {
service: "api",
environment: "production",
version: "1.2.3"
}
const attributesAsArray: ReadonlyArray<[string, string]> = [
["service", "api"],
["environment", "production"],
["version", "1.2.3"]
]
// Create metrics with different attribute formats
const requestCounter1 = Metric.counter("requests", {
description: "Total requests",
attributes: attributesAsObject // Using object format
})
const requestCounter2 = Metric.counter("requests", {
description: "Total requests",
attributes: attributesAsArray // Using array format
})
// Function to normalize attributes to object format
const normalizeAttributes = (
attrs: typeof attributesAsObject | ReadonlyArray<[string, string]>
) => {
if (Array.isArray(attrs)) {
return Object.fromEntries(attrs)
}
return attrs
}
// Add runtime attributes using withAttributes
const contextualCounter = Metric.withAttributes(requestCounter1, {
method: "GET",
endpoint: "/api/users"
})
// Update metrics with different attribute combinations
yield* Metric.update(contextualCounter, 1)
// Both formats result in the same internal representation
const normalizedObject = normalizeAttributes(attributesAsObject)
const normalizedArray = normalizeAttributes(attributesAsArray)
return {
attributeFormats: {
object: normalizedObject, // { service: "api", environment: "production", version: "1.2.3" }
array: normalizedArray, // { service: "api", environment: "production", version: "1.2.3" }
areEqual:
JSON.stringify(normalizedObject) === JSON.stringify(normalizedArray) // true
}
}
})
Attributes | undefined
readonly maxAge: Duration.InputmaxAge: import DurationDuration.type Duration.Input = /*unresolved*/ anyInput
readonly maxSize: numbermaxSize: number
readonly quantiles: readonly number[]quantiles: interface ReadonlyArray<T>ReadonlyArray<number>
}): interface Summary<Input>A Summary metric that calculates quantiles over a sliding time window of observations.
When to use
Use when summaries provide statistical insights into value distributions by tracking specific quantiles
(percentiles) such as median (50th), 95th percentile, 99th percentile, etc. They're ideal for
understanding performance characteristics like response time distributions.
Example (Using summary metrics)
import { Data, Effect, Metric } from "effect"
class SummaryInterfaceError extends Data.TaggedError("SummaryInterfaceError")<{
readonly operation: string
}> {}
const program = Effect.gen(function*() {
// Create summaries with different quantile configurations
const responseTimeSummary: Metric.Summary<number> = Metric.summary(
"api_response_time_ms",
{
description: "API response time distribution in milliseconds",
maxAge: "5 minutes", // Keep observations for 5 minutes
maxSize: 1000, // Keep up to 1000 observations
quantiles: [0.5, 0.95, 0.99] // Track median, 95th, and 99th percentiles
}
)
const requestSizeSummary: Metric.Summary<number> = Metric.summary(
"request_size_bytes",
{
description: "Request payload size distribution",
maxAge: "10 minutes",
maxSize: 500,
quantiles: [0.25, 0.5, 0.75, 0.9] // Track quartiles and 90th percentile
}
)
// Record observations (values are stored in time-based sliding window)
yield* Metric.update(responseTimeSummary, 120) // Fast response
yield* Metric.update(responseTimeSummary, 250) // Average response
yield* Metric.update(responseTimeSummary, 45) // Very fast response
yield* Metric.update(responseTimeSummary, 890) // Slow response
yield* Metric.update(responseTimeSummary, 156) // Average response
yield* Metric.update(requestSizeSummary, 1024) // 1KB request
yield* Metric.update(requestSizeSummary, 512) // 512B request
yield* Metric.update(requestSizeSummary, 2048) // 2KB request
// Read summary state
const responseTimeState: Metric.SummaryState = yield* Metric.value(
responseTimeSummary
)
const requestSizeState: Metric.SummaryState = yield* Metric.value(
requestSizeSummary
)
// Summary state contains:
// - quantiles: Array of [quantile, optionalValue] pairs
// - count: total number of observations in window
// - min: smallest observed value in window
// - max: largest observed value in window
// - sum: sum of all observed values in window
// Extract quantile values safely
const getQuantileValue = (
quantiles: ReadonlyArray<readonly [number, number | undefined]>,
q: number
) => quantiles.find(([quantile]) => quantile === q)?.[1]
const median = getQuantileValue(responseTimeState.quantiles, 0.5)
const p95 = getQuantileValue(responseTimeState.quantiles, 0.95)
const p99 = getQuantileValue(responseTimeState.quantiles, 0.99)
return {
responseTime: {
totalRequests: responseTimeState.count, // 5
fastestResponse: responseTimeState.min, // 45
slowestResponse: responseTimeState.max, // 890
totalTime: responseTimeState.sum, // 1461
averageTime: responseTimeState.sum / responseTimeState.count, // 292.2
medianTime: median ?? null, // ~156
p95Time: p95 ?? null, // ~890
p99Time: p99 ?? null // ~890
},
requestSize: {
totalRequests: requestSizeState.count, // 3
averageSize: requestSizeState.sum / requestSizeState.count // ~1194.7
}
}
})
Summary<number> =>
const mapInput: {
<Input, Input2 extends Input>(
f: (
input: Input2,
context: Context.Context<never>
) => Input
): <State>(
self: Metric<Input, State>
) => Metric<Input2, State>
<Input, State, Input2>(
self: Metric<Input, State>,
f: (
input: Input2,
context: Context.Context<never>
) => Input
): Metric<Input2, State>
}
mapInput(const summaryWithTimestamp: (
name: string,
options: {
readonly description?: string | undefined
readonly attributes?:
| Metric.Attributes
| undefined
readonly maxAge: Duration.Input
readonly maxSize: number
readonly quantiles: ReadonlyArray<number>
}
) => Summary<[value: number, timestamp: number]>
Creates a Summary metric that records observations with explicit
timestamps and calculates quantiles.
When to use
Use when you need a metric that records statistical information about a set
of values together with timestamps.
Details
Inputs to this metric are [value, timestamp] pairs; the current clock is
used when reading quantiles against the configured maxAge.
The optional description describes the summary, and attributes attach
dimensions to it. maxAge controls how long observations are retained,
maxSize controls how many observations are kept, and quantiles lists the
quantiles to calculate, such as [0.5, 0.9].
Example (Creating summaries with explicit timestamps)
import { Metric } from "effect"
const responseTimesSummary = Metric.summaryWithTimestamp(
"response_times_summary",
{
description: "Measures the distribution of response times",
maxAge: "60 seconds", // Retain observations for 60 seconds.
maxSize: 1000, // Keep a maximum of 1000 observations.
quantiles: [0.5, 0.9, 0.99] // Calculate 50th, 90th, and 99th quantiles.
}
)
summaryWithTimestamp(name: stringname, options: {
readonly description?: string | undefined
readonly attributes?:
| Metric.Attributes
| undefined
readonly maxAge: Duration.Input
readonly maxSize: number
readonly quantiles: ReadonlyArray<number>
}
options), (input: numberinput, context: Context.Context<never>(parameter) context: {
mapUnsafe: ReadonlyMap<string, any>;
mutable: boolean;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
context) =>
[
input: numberinput,
import ContextContext.get(context: Context.Context<never>(parameter) context: {
mapUnsafe: ReadonlyMap<string, any>;
mutable: boolean;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
context, import InternalEffectInternalEffect.const ClockRef: Context.Reference<Clock>const ClockRef: {
key: string;
Service: {
currentTimeMillisUnsafe: () => number;
currentTimeMillis: Effect<number>;
currentTimeNanosUnsafe: () => bigint;
currentTimeNanos: Effect<bigint>;
sleep: (duration: Duration.Duration) => Effect<void>;
};
defaultValue: () => Shape;
of: (this: void, self: Clock) => Clock;
context: (self: Clock) => Context.Context<never>;
use: (f: (service: Clock) => Effect<A, E, R>) => Effect<A, E, R>;
useSync: (f: (service: Clock) => A) => Effect<A, never, never>;
Identifier: Identifier;
stack: string | undefined;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
ClockRef).currentTimeMillisUnsafe()
] as [number, number])