/**
* @since 2.0.0
*
* The `Metric` module provides a comprehensive system for collecting, aggregating, and observing
* application metrics in Effect applications. It offers type-safe, concurrent metrics that can
* be used to monitor performance, track business metrics, and gain insights into application behavior.
*
* ## Key Features
*
* - **Five Metric Types**: Counters, Gauges, Frequencies, Histograms, and Summaries
* - **Type Safety**: Fully typed metrics with compile-time guarantees
* - **Concurrency Safe**: Thread-safe metrics that work with Effect's concurrency model
* - **Attributes**: Tag metrics with key-value attributes for filtering and grouping
* - **Snapshots**: Take point-in-time snapshots of all metrics for reporting
* - **Runtime Integration**: Automatic fiber runtime metrics collection
*
* ## Metric Types
*
* ### Counter
* Tracks cumulative values that only increase or can be reset to zero.
* Perfect for counting events, requests, errors, etc.
*
* ### Gauge
* Represents a single numerical value that can go up or down.
* Ideal for current resource usage, temperature, queue sizes, etc.
*
* ### Frequency
* Counts occurrences of discrete string values.
* Useful for tracking categorical data like HTTP status codes, user actions, etc.
*
* ### Histogram
* Records observations in configurable buckets to analyze distribution.
* Great for response times, request sizes, and other measured values.
*
* ### Summary
* Calculates quantiles over a sliding time window.
* Provides statistical insights into value distributions over time.
*
* ## Basic Usage
*
* ```ts
* import { Effect, Metric } from "effect"
*
* // Create metrics
* const requestCount = Metric.counter("http_requests_total", {
* description: "Total number of HTTP requests"
* })
*
* const responseTime = Metric.histogram("http_response_time", {
* description: "HTTP response time in milliseconds",
* boundaries: Metric.linearBoundaries({ start: 0, width: 50, count: 20 })
* })
*
* // Use metrics in your application
* const handleRequest = Effect.gen(function*() {
* yield* Metric.update(requestCount, 1)
*
* const startTime = yield* Effect.clockWith((clock) => clock.currentTimeMillis)
*
* // Process request...
* yield* Effect.sleep("100 millis")
*
* const endTime = yield* Effect.clockWith((clock) => clock.currentTimeMillis)
* yield* Metric.update(responseTime, endTime - startTime)
* })
* ```
*
* ## Attributes and Tagging
*
* ```ts
* import { Effect, Metric } from "effect"
*
* const requestCount = Metric.counter("requests", {
* description: "Number of requests by endpoint and method"
* })
*
* const program = Effect.gen(function*() {
* // Add attributes to metrics
* yield* Metric.update(
* Metric.withAttributes(requestCount, {
* endpoint: "/api/users",
* method: "GET"
* }),
* 1
* )
*
* // Or use withAttributes for compile-time attributes
* const taggedCounter = Metric.withAttributes(requestCount, {
* endpoint: "/api/posts",
* method: "POST"
* })
* yield* Metric.update(taggedCounter, 1)
* })
* ```
*
* ## Advanced Examples
*
* ```ts
* import { Effect, Metric } from "effect"
*
* // Business metrics
* const userSignups = Metric.counter("user_signups_total")
* const activeUsers = Metric.gauge("active_users_current")
* const featureUsage = Metric.frequency("feature_usage")
*
* // Performance metrics
* const dbQueryTime = Metric.summary("db_query_duration", {
* maxAge: "5 minutes",
* maxSize: 1000,
* quantiles: [0.5, 0.9, 0.95, 0.99]
* })
*
* const program = Effect.gen(function*() {
* // Track user signup
* yield* Metric.update(userSignups, 1)
*
* // Update active user count
* yield* Metric.update(activeUsers, 1250)
*
* // Record feature usage
* yield* Metric.update(featureUsage, "dashboard_view")
*
* // Measure database query time
* yield* Effect.timed(performDatabaseQuery).pipe(
* Effect.tap(([duration]) => Metric.update(dbQueryTime, duration))
* )
* })
*
* // Get metric snapshots
* const getMetrics = Effect.gen(function*() {
* const snapshots = yield* Metric.snapshot
*
* for (const metric of snapshots) {
* console.log(`${metric.id}: ${JSON.stringify(metric.state)}`)
* }
* })
* ```
*/
import * as Context from "./Context.ts";
import * as Duration from "./Duration.ts";
import type { Effect } from "./Effect.ts";
import type { Exit } from "./Exit.ts";
import * as Layer from "./Layer.ts";
import type { Pipeable } from "./Pipeable.ts";
import type { Contravariant, Covariant } from "./Types.ts";
/**
* A `Metric` represents a concurrent metric which accepts update
* values of type `Input` and are aggregated to a value of type `State`.
*
* For example, a counter metric would have type `Metric`,
* representing the fact that the metric can be updated with numbers (the amount
* to increment or decrement the counter by), and the state of the counter is a
* number.
*
* There are five primitive metric types supported by Effect:
*
* - Counters
* - Frequencies
* - Gauges
* - Histograms
* - Summaries
*
* @example
* ```ts
* import { Data, Effect, Metric } from "effect"
*
* class MetricExample extends Data.TaggedError("MetricExample")<{
* readonly operation: string
* }> {}
*
* const program = Effect.gen(function*() {
* // Create different types of metrics
* const requestCounter: Metric.Counter = Metric.counter("requests", {
* description: "Total requests processed"
* })
*
* const memoryGauge: Metric.Gauge = Metric.gauge("memory_usage", {
* description: "Current memory usage in MB"
* })
*
* const statusFrequency: Metric.Frequency = Metric.frequency("status_codes", {
* description: "HTTP status code frequency"
* })
*
* // All metrics share the same interface for updates and reads
* yield* Metric.update(requestCounter, 1)
* yield* Metric.update(memoryGauge, 128)
* yield* Metric.update(statusFrequency, "200")
*
* // All metrics can be read with Metric.value
* const counterState = yield* Metric.value(requestCounter)
* const gaugeState = yield* Metric.value(memoryGauge)
* const frequencyState = yield* Metric.value(statusFrequency)
*
* // Metrics have common properties accessible through the interface:
* // - id: unique identifier
* // - type: metric type ("Counter", "Gauge", "Frequency", etc.)
* // - description: optional human-readable description
* // - attributes: optional key-value attributes for tagging
*
* return {
* counter: {
* id: requestCounter.id,
* type: requestCounter.type,
* state: counterState
* },
* gauge: { id: memoryGauge.id, type: memoryGauge.type, state: gaugeState },
* frequency: {
* id: statusFrequency.id,
* type: statusFrequency.type,
* state: frequencyState
* }
* }
* })
* ```
*
* @since 2.0.0
* @category Models
*/
export interface Metric extends Pipeable {
readonly [TypeId]: typeof TypeId;
readonly Input: Contravariant;
readonly State: Covariant;
readonly id: string;
readonly type: Metric.Type;
readonly description: string | undefined;
readonly attributes: Metric.AttributeSet | undefined;
readonly valueUnsafe: (context: Context.Context) => State;
readonly updateUnsafe: (input: Input, context: Context.Context) => void;
readonly modifyUnsafe: (input: Input, context: Context.Context) => void;
}
/**
* A Counter metric that tracks cumulative values that typically only increase.
*
* Counters are useful for tracking monotonically increasing values like request counts,
* bytes processed, errors encountered, or any value that accumulates over time.
*
* @example
* ```ts
* import { Data, Effect, Metric } from "effect"
*
* class CounterInterfaceError extends Data.TaggedError("CounterInterfaceError")<{
* readonly operation: string
* }> {}
*
* const program = Effect.gen(function*() {
* // Create different types of counters
* const requestCounter: Metric.Counter = Metric.counter(
* "http_requests",
* {
* description: "Total HTTP requests processed",
* incremental: true // Only allows increments
* }
* )
*
* const bytesCounter: Metric.Counter = Metric.counter(
* "bytes_processed",
* {
* description: "Total bytes processed",
* bigint: true,
* attributes: { service: "data-processor" }
* }
* )
*
* // Update counters
* yield* Metric.update(requestCounter, 1) // Increment by 1
* yield* Metric.update(requestCounter, 5) // Increment by 5 (total: 6)
* yield* Metric.update(bytesCounter, 1024n) // Add 1024 bytes
*
* // Read counter state
* const requestState: Metric.CounterState = yield* Metric.value(
* requestCounter
* )
* const bytesState: Metric.CounterState = yield* Metric.value(
* bytesCounter
* )
*
* // Counter state contains:
* // - count: current accumulated value
* // - incremental: whether only increments are allowed
*
* return {
* requests: {
* count: requestState.count,
* incremental: requestState.incremental
* },
* bytes: { count: bytesState.count, incremental: bytesState.incremental }
* }
* })
* ```
*
* @since 2.0.0
* @category Metrics
*/
export interface Counter extends Metric> {
}
/**
* State interface for Counter metrics containing the current count and increment mode.
*
* @example
* ```ts
* import { Data, Effect, Metric } from "effect"
*
* class CounterStateError extends Data.TaggedError("CounterStateError")<{
* readonly operation: string
* }> {}
*
* const program = Effect.gen(function*() {
* // Create different types of counters
* const requestCounter = Metric.counter("http_requests_total")
* const errorCounter = Metric.counter("errors_total", { incremental: true })
* const byteCounter = Metric.counter("bytes_processed", { bigint: true })
*
* // Update counters
* yield* Metric.update(requestCounter, 5) // Add 5 requests
* yield* Metric.update(requestCounter, -2) // Subtract 2 (allowed for non-incremental)
* yield* Metric.update(errorCounter, 3) // Add 3 errors
* yield* Metric.update(errorCounter, -1) // Attempt to subtract (ignored for incremental)
* yield* Metric.update(byteCounter, 1024000n) // Add bytes as bigint
*
* // Read counter states
* const requestState: Metric.CounterState = yield* Metric.value(
* requestCounter
* )
* const errorState: Metric.CounterState = yield* Metric.value(
* errorCounter
* )
* const byteState: Metric.CounterState = yield* Metric.value(
* byteCounter
* )
*
* // CounterState contains:
* // - count: current count value (number or bigint based on counter type)
* // - incremental: whether counter only allows increases
*
* return {
* requests: {
* total: requestState.count, // 3 (5 - 2, decrements allowed)
* canDecrease: !requestState.incremental // true
* },
* errors: {
* total: errorState.count, // 3 (subtract ignored)
* canDecrease: !errorState.incremental // false
* },
* bytes: {
* total: byteState.count, // 1024000n
* canDecrease: !byteState.incremental // true
* }
* }
* })
* ```
*
* @since 2.0.0
* @category Counter
*/
export interface CounterState {
readonly count: Input extends bigint ? bigint : number;
readonly incremental: boolean;
}
/**
* A Frequency metric interface that counts occurrences of discrete string values.
*
* Frequency metrics are ideal for tracking categorical data where you want to count
* how many times specific string values occur, such as HTTP status codes, user actions,
* error types, or any discrete string-based events.
*
* @example
* ```ts
* import { Data, Effect, Metric } from "effect"
*
* class FrequencyInterfaceError
* extends Data.TaggedError("FrequencyInterfaceError")<{
* readonly operation: string
* }>
* {}
*
* // Function that accepts any Frequency metric
* const logFrequencyMetric = (freq: Metric.Frequency) =>
* Effect.gen(function*() {
* const state = yield* Metric.value(freq)
*
* yield* Effect.log(`Frequency Metric: ${freq.id}`)
* yield* Effect.log(`Description: ${freq.description ?? "No description"}`)
* yield* Effect.log(`Type: ${freq.type}`) // "Frequency"
*
* // Access the frequency state
* const occurrences: ReadonlyMap = state.occurrences
* yield* Effect.log(`Total unique values: ${occurrences.size}`)
*
* // Iterate through all occurrences
* for (const [value, count] of occurrences) {
* yield* Effect.log(` "${value}": ${count} occurrences`)
* }
*
* // Find most frequent value
* let maxCount = 0
* let mostFrequent = ""
* for (const [value, count] of occurrences) {
* if (count > maxCount) {
* maxCount = count
* mostFrequent = value
* }
* }
*
* return { mostFrequent, maxCount, totalUniqueValues: occurrences.size }
* })
*
* const program = Effect.gen(function*() {
* // Create frequency metrics
* const statusCodes: Metric.Frequency = Metric.frequency("http_status", {
* description: "HTTP status code frequency"
* })
*
* const userActions: Metric.Frequency = Metric.frequency("user_actions", {
* description: "User action frequency"
* })
*
* // Record some occurrences
* yield* Metric.update(statusCodes, "200")
* yield* Metric.update(statusCodes, "200")
* yield* Metric.update(statusCodes, "404")
* yield* Metric.update(statusCodes, "500")
* yield* Metric.update(statusCodes, "200")
*
* yield* Metric.update(userActions, "login")
* yield* Metric.update(userActions, "view_dashboard")
* yield* Metric.update(userActions, "login")
*
* // Use the function with different frequency metrics
* const statusAnalysis = yield* logFrequencyMetric(statusCodes)
* const actionAnalysis = yield* logFrequencyMetric(userActions)
*
* return { statusAnalysis, actionAnalysis }
* })
* ```
*
* @since 2.0.0
* @category Metrics
*/
export interface Frequency extends Metric {
}
/**
* State interface for Frequency metrics containing occurrence counts for discrete string values.
*
* @example
* ```ts
* import { Data, Effect, Metric } from "effect"
*
* class FrequencyStateError extends Data.TaggedError("FrequencyStateError")<{
* readonly operation: string
* }> {}
*
* const program = Effect.gen(function*() {
* // Create frequency metrics for different categories
* const statusCodeFreq = Metric.frequency("http_status_codes", {
* description: "HTTP status code distribution"
* })
*
* const userActionFreq = Metric.frequency("user_actions", {
* description: "User action frequency"
* })
*
* // Record occurrences
* yield* Metric.update(statusCodeFreq, "200") // Success
* yield* Metric.update(statusCodeFreq, "200") // Another success
* yield* Metric.update(statusCodeFreq, "404") // Not found
* yield* Metric.update(statusCodeFreq, "500") // Server error
* yield* Metric.update(statusCodeFreq, "200") // Another success
*
* yield* Metric.update(userActionFreq, "login")
* yield* Metric.update(userActionFreq, "click")
* yield* Metric.update(userActionFreq, "login")
* yield* Metric.update(userActionFreq, "scroll")
* yield* Metric.update(userActionFreq, "click")
* yield* Metric.update(userActionFreq, "click")
*
* // Read frequency states
* const statusState: Metric.FrequencyState = yield* Metric.value(statusCodeFreq)
* const actionState: Metric.FrequencyState = yield* Metric.value(userActionFreq)
*
* // FrequencyState contains:
* // - occurrences: ReadonlyMap with string values and their counts
*
* // Analyze frequency distributions
* const getMostFrequent = (occurrences: ReadonlyMap) => {
* let maxKey = ""
* let maxCount = 0
* for (const [key, count] of occurrences) {
* if (count > maxCount) {
* maxKey = key
* maxCount = count
* }
* }
* return { key: maxKey, count: maxCount }
* }
*
* const topStatus = getMostFrequent(statusState.occurrences)
* const topAction = getMostFrequent(actionState.occurrences)
*
* return {
* statusCodes: {
* totalResponses: Array.from(statusState.occurrences.values()).reduce(
* (a, b) => a + b,
* 0
* ), // 5
* mostCommon: topStatus, // { key: "200", count: 3 }
* uniqueCodes: statusState.occurrences.size // 3
* },
* userActions: {
* totalActions: Array.from(actionState.occurrences.values()).reduce(
* (a, b) => a + b,
* 0
* ), // 6
* mostCommon: topAction, // { key: "click", count: 3 }
* uniqueActions: actionState.occurrences.size // 3
* }
* }
* })
* ```
*
* @since 2.0.0
* @category Metrics
*/
export interface FrequencyState {
readonly occurrences: ReadonlyMap;
}
/**
* A Gauge metric that tracks instantaneous values that can go up or down.
*
* Gauges are useful for tracking current state values like memory usage, CPU load,
* active connections, queue sizes, or any value that represents a current level.
*
* @example
* ```ts
* import { Data, Effect, Metric } from "effect"
*
* class GaugeInterfaceError extends Data.TaggedError("GaugeInterfaceError")<{
* readonly operation: string
* }> {}
*
* const program = Effect.gen(function*() {
* // Create different types of gauges
* const memoryGauge: Metric.Gauge = Metric.gauge("memory_usage_mb", {
* description: "Current memory usage in megabytes"
* })
*
* const diskSpaceGauge: Metric.Gauge = Metric.gauge("disk_free_bytes", {
* description: "Available disk space in bytes",
* bigint: true,
* attributes: { mount: "/var" }
* })
*
* // Set gauge values (absolute values)
* yield* Metric.update(memoryGauge, 512) // Set to 512 MB
* yield* Metric.update(memoryGauge, 640) // Set to 640 MB (replaces 512)
* yield* Metric.update(diskSpaceGauge, 5000000000n) // Set to ~5GB free
*
* // Modify gauge values (relative changes)
* yield* Metric.modify(memoryGauge, 128) // Add 128 MB (total: 768)
* yield* Metric.modify(memoryGauge, -64) // Subtract 64 MB (total: 704)
*
* // Read gauge state
* const memoryState: Metric.GaugeState = yield* Metric.value(
* memoryGauge
* )
* const diskState: Metric.GaugeState = yield* Metric.value(
* diskSpaceGauge
* )
*
* // Gauge state contains:
* // - value: current instantaneous value
*
* return {
* memory: { currentValue: memoryState.value }, // 704
* disk: { currentValue: diskState.value } // 5000000000n
* }
* })
* ```
*
* @since 2.0.0
* @category Metrics
*/
export interface Gauge extends Metric> {
}
/**
* State interface for Gauge metrics containing the current instantaneous value.
*
* @example
* ```ts
* import { Data, Effect, Metric } from "effect"
*
* class GaugeStateError extends Data.TaggedError("GaugeStateError")<{
* readonly operation: string
* }> {}
*
* const program = Effect.gen(function*() {
* // Create different types of gauges
* const temperatureGauge = Metric.gauge("room_temperature_celsius", {
* description: "Current room temperature"
* })
*
* const diskSpaceGauge = Metric.gauge("disk_usage_bytes", {
* description: "Current disk usage",
* bigint: true
* })
*
* const queueSizeGauge = Metric.gauge("queue_size", {
* description: "Current queue size"
* })
*
* // Set gauge values (absolute values)
* yield* Metric.update(temperatureGauge, 22.5) // Set to 22.5°C
* yield* Metric.update(diskSpaceGauge, 5000000000n) // Set to 5GB usage
* yield* Metric.update(queueSizeGauge, 10) // Set to 10 items
*
* // Update gauge values (new absolute values)
* yield* Metric.update(temperatureGauge, 23.1) // Temperature changed
* yield* Metric.update(queueSizeGauge, 15) // Queue grew
*
* // Read gauge states
* const tempState: Metric.GaugeState = yield* Metric.value(
* temperatureGauge
* )
* const diskState: Metric.GaugeState = yield* Metric.value(
* diskSpaceGauge
* )
* const queueState: Metric.GaugeState = yield* Metric.value(
* queueSizeGauge
* )
*
* // GaugeState contains:
* // - value: current instantaneous value (number or bigint based on gauge type)
*
* return {
* environment: {
* temperature: tempState.value, // 23.1
* temperatureUnit: "°C"
* },
* system: {
* diskUsage: diskState.value, // 5000000000n
* diskUsageGB: Number(diskState.value) / 1_000_000_000, // 5
* queueSize: queueState.value // 15
* }
* }
* })
* ```
*
* @since 2.0.0
* @category Metrics
*/
export interface GaugeState {
readonly value: Input extends bigint ? bigint : number;
}
/**
* A Histogram metric that records observations in configurable buckets to analyze value distributions.
*
* Histograms are ideal for measuring request durations, response sizes, and other continuous values
* where you need to understand the distribution of values rather than just aggregates.
*
* @example
* ```ts
* import { Data, Effect, Metric } from "effect"
*
* class HistogramInterfaceError
* extends Data.TaggedError("HistogramInterfaceError")<{
* readonly operation: string
* }>
* {}
*
* const program = Effect.gen(function*() {
* // Create histograms with different boundary strategies
* const responseTimeHistogram: Metric.Histogram = Metric.histogram(
* "http_response_time_ms",
* {
* description: "HTTP response time distribution in milliseconds",
* boundaries: Metric.linearBoundaries({ start: 0, width: 50, count: 20 }) // 0, 50, 100, ..., 950
* }
* )
*
* const fileSizeHistogram: Metric.Histogram = Metric.histogram(
* "file_size_bytes",
* {
* description: "File size distribution in bytes",
* boundaries: Metric.exponentialBoundaries({
* start: 1,
* factor: 2,
* count: 10
* }) // 1, 2, 4, 8, ..., 512
* }
* )
*
* // Record observations (values get placed into appropriate buckets)
* yield* Metric.update(responseTimeHistogram, 125) // Goes into 100-150ms bucket
* yield* Metric.update(responseTimeHistogram, 75) // Goes into 50-100ms bucket
* yield* Metric.update(responseTimeHistogram, 200) // Goes into 150-200ms bucket
* yield* Metric.update(responseTimeHistogram, 45) // Goes into 0-50ms bucket
*
* yield* Metric.update(fileSizeHistogram, 3) // Goes into 2-4 bytes bucket
* yield* Metric.update(fileSizeHistogram, 15) // Goes into 8-16 bytes bucket
* yield* Metric.update(fileSizeHistogram, 100) // Goes into 64-128 bytes bucket
*
* // Read histogram state
* const responseTimeState: Metric.HistogramState = yield* Metric.value(
* responseTimeHistogram
* )
* const fileSizeState: Metric.HistogramState = yield* Metric.value(
* fileSizeHistogram
* )
*
* // Histogram state contains:
* // - buckets: Array of [boundary, cumulativeCount] pairs
* // - count: total number of observations
* // - min: smallest observed value
* // - max: largest observed value
* // - sum: sum of all observed values
*
* return {
* responseTime: {
* totalRequests: responseTimeState.count, // 4
* fastestRequest: responseTimeState.min, // 45
* slowestRequest: responseTimeState.max, // 200
* totalTime: responseTimeState.sum, // 445
* averageTime: responseTimeState.sum / responseTimeState.count // 111.25
* },
* fileSize: {
* totalFiles: fileSizeState.count, // 3
* smallestFile: fileSizeState.min, // 3
* largestFile: fileSizeState.max, // 100
* totalBytes: fileSizeState.sum // 118
* }
* }
* })
* ```
*
* @since 2.0.0
* @category Metrics
*/
export interface Histogram extends Metric {
}
/**
* State interface for Histogram metrics containing bucket distributions and aggregate statistics.
*
* @example
* ```ts
* import { Data, Effect, Metric } from "effect"
*
* class HistogramStateError extends Data.TaggedError("HistogramStateError")<{
* readonly operation: string
* }> {}
*
* const program = Effect.gen(function*() {
* // Create histogram with linear boundaries
* const responseTimeHistogram = Metric.histogram("api_response_time_ms", {
* description: "API response time distribution",
* boundaries: Metric.linearBoundaries({ start: 0, width: 100, count: 10 }) // 0, 100, 200, ..., 900
* })
*
* // Record observations
* yield* Metric.update(responseTimeHistogram, 50) // Fast response
* yield* Metric.update(responseTimeHistogram, 150) // Average response
* yield* Metric.update(responseTimeHistogram, 750) // Slow response
* yield* Metric.update(responseTimeHistogram, 250) // Average response
* yield* Metric.update(responseTimeHistogram, 95) // Fast response
*
* // Read histogram state
* const state: Metric.HistogramState = yield* Metric.value(
* responseTimeHistogram
* )
*
* // HistogramState contains:
* // - buckets: Array of [boundary, cumulativeCount] pairs showing distribution
* // - count: total number of observations
* // - min: smallest observed value
* // - max: largest observed value
* // - sum: sum of all observed values
*
* // Analyze bucket distribution
* const analyzeBuckets = (buckets: ReadonlyArray<[number, number]>) => {
* const analysis: Array<
* { range: string; count: number; percentage: number }
* > = []
* let previousCount = 0
* const totalCount = buckets[buckets.length - 1]?.[1] ?? 0
*
* for (let i = 0; i < buckets.length; i++) {
* const [boundary, cumulativeCount] = buckets[i]
* const bucketCount = cumulativeCount - previousCount
* const percentage = totalCount > 0 ? (bucketCount / totalCount) * 100 : 0
* const prevBoundary = i === 0 ? 0 : buckets[i - 1][0]
*
* analysis.push({
* range: `${prevBoundary}-${boundary}ms`,
* count: bucketCount,
* percentage: Math.round(percentage * 10) / 10
* })
* previousCount = cumulativeCount
* }
* return analysis
* }
*
* const bucketAnalysis = analyzeBuckets(state.buckets)
*
* return {
* responseTime: {
* totalRequests: state.count, // 5
* fastestResponse: state.min, // 50
* slowestResponse: state.max, // 750
* averageResponse: state.sum / state.count, // 268
* totalTime: state.sum, // 1340
* distribution: bucketAnalysis
* // Example distribution:
* // [{ range: "0-100ms", count: 2, percentage: 40.0 },
* // { range: "100-200ms", count: 1, percentage: 20.0 },
* // { range: "200-300ms", count: 1, percentage: 20.0 },
* // { range: "700-800ms", count: 1, percentage: 20.0 }]
* }
* }
* })
* ```
*
* @since 2.0.0
* @category Metrics
*/
export interface HistogramState {
readonly buckets: ReadonlyArray<[number, number]>;
readonly count: number;
readonly min: number;
readonly max: number;
readonly sum: number;
}
/**
* A Summary metric that calculates quantiles over a sliding time window of observations.
*
* 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
* ```ts
* 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 = 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 = 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,
* 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
* }
* }
* })
* ```
*
* @since 2.0.0
* @category Metrics
*/
export interface Summary extends Metric {
}
/**
* State interface for Summary metrics containing quantile calculations and aggregate statistics.
*
* @example
* ```ts
* import { Data, Effect, Metric } from "effect"
*
* class SummaryStateError extends Data.TaggedError("SummaryStateError")<{
* readonly operation: string
* }> {}
*
* const program = Effect.gen(function*() {
* // Create summary with specific quantiles
* const responseTimeSummary = Metric.summary("api_response_latency", {
* description: "API response time distribution with quantiles",
* maxAge: "5 minutes",
* maxSize: 1000,
* quantiles: [0.5, 0.95, 0.99] // Track median, 95th, and 99th percentiles
* })
*
* // Record observations over time
* 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(responseTimeSummary, 78) // Fast response
* yield* Metric.update(responseTimeSummary, 340) // Slower response
*
* // Read summary state
* const state: Metric.SummaryState = yield* Metric.value(responseTimeSummary)
*
* // SummaryState contains:
* // - quantiles: Array of [quantile, optionalValue] pairs showing percentile values
* // - count: total number of observations in current window
* // - min: smallest observed value in window
* // - max: largest observed value in window
* // - sum: sum of all observed values in window
*
* // Extract quantile information safely
* const extractQuantiles = (
* quantiles: ReadonlyArray
* ) => {
* const result: Record = {}
* for (const [quantile, valueOption] of quantiles) {
* const percentile = Math.round(quantile * 100)
* result[`p${percentile}`] = valueOption ?? null
* }
* return result
* }
*
* const quantileValues = extractQuantiles(state.quantiles)
*
* return {
* latencyAnalysis: {
* totalRequests: state.count, // 7
* fastestResponse: state.min, // 45
* slowestResponse: state.max, // 890
* averageResponse: state.sum / state.count, // ~268.4
* totalLatency: state.sum, // 1879
* percentiles: quantileValues,
* // Example percentiles:
* // { p50: 156, p95: 890, p99: 890 }
* performance: {
* fast: quantileValues.p50 !== null && quantileValues.p50 < 200
* ? "Good"
* : "Needs improvement",
* reliability: quantileValues.p95 !== null && quantileValues.p95 < 500
* ? "Reliable"
* : "Concerning"
* }
* }
* }
* })
* ```
*
* @since 2.0.0
* @category Metrics
*/
export interface SummaryState {
readonly quantiles: ReadonlyArray;
readonly count: number;
readonly min: number;
readonly max: number;
readonly sum: number;
}
/**
* The `Metric` namespace provides a comprehensive system for collecting, aggregating, and observing
* application metrics in Effect applications.
*
* @example
* ```ts
* import { Data, Effect, Metric } from "effect"
*
* class MetricsError extends Data.TaggedError("MetricsError")<{
* readonly operation: string
* }> {}
*
* const program = Effect.gen(function*() {
* // Create different types of metrics
* const requestCounter = Metric.counter("http_requests_total")
* const responseTimeHistogram = Metric.histogram("http_response_time", {
* boundaries: Metric.linearBoundaries({ start: 0, width: 10, count: 10 })
* })
* const activeConnectionsGauge = Metric.gauge("active_connections")
* const statusFrequency = Metric.frequency("http_status_codes")
*
* // Update metrics
* yield* Metric.update(requestCounter, 1)
* yield* Metric.update(responseTimeHistogram, 45.2)
* yield* Metric.update(activeConnectionsGauge, 12)
* yield* Metric.update(statusFrequency, "200")
*
* // Get metric values
* const counterValue = yield* Metric.value(requestCounter)
* const histogramValue = yield* Metric.value(responseTimeHistogram)
* const gaugeValue = yield* Metric.value(activeConnectionsGauge)
* const frequencyValue = yield* Metric.value(statusFrequency)
*
* return {
* counter: counterValue,
* histogram: histogramValue,
* gauge: gaugeValue,
* frequency: frequencyValue
* }
* })
* ```
*
* @since 2.0.0
* @category models
*/
export declare namespace Metric {
/**
* Union type representing all available metric types in the Effect metrics system.
*
* @example
* ```ts
* import { Data, Effect, Metric } from "effect"
*
* class MetricTypeError extends Data.TaggedError("MetricTypeError")<{
* readonly operation: string
* }> {}
*
* const program = Effect.gen(function*() {
* // Create different metric types
* const counter = Metric.counter("requests_total")
* const gauge = Metric.gauge("cpu_usage")
* const frequency = Metric.frequency("status_codes")
* const histogram = Metric.histogram("response_time", {
* boundaries: Metric.linearBoundaries({ start: 0, width: 50, count: 10 })
* })
* const summary = Metric.summary("latency", {
* maxAge: "5 minutes",
* maxSize: 1000,
* quantiles: [0.5, 0.95, 0.99]
* })
*
* // Function that checks metric type
* const getMetricInfo = (metric: Metric.Metric) => ({
* name: metric.id,
* type: metric.type
* })
*
* // Get type information for each metric
* const counterInfo = getMetricInfo(counter) // { name: "requests_total", type: "Counter" }
* const gaugeInfo = getMetricInfo(gauge) // { name: "cpu_usage", type: "Gauge" }
* const frequencyInfo = getMetricInfo(frequency) // { name: "status_codes", type: "Frequency" }
* const histogramInfo = getMetricInfo(histogram) // { name: "response_time", type: "Histogram" }
* const summaryInfo = getMetricInfo(summary) // { name: "latency", type: "Summary" }
*
* // Pattern match on metric type
* const describeMetric = (type: string): string => {
* switch (type) {
* case "Counter":
* return "Cumulative values that increase over time"
* case "Gauge":
* return "Instantaneous values that can go up or down"
* case "Frequency":
* return "Counts of discrete string occurrences"
* case "Histogram":
* return "Distribution of values across buckets"
* case "Summary":
* return "Quantile calculations over time windows"
* default:
* return "Unknown metric type"
* }
* }
*
* return {
* metrics: [
* counterInfo,
* gaugeInfo,
* frequencyInfo,
* histogramInfo,
* summaryInfo
* ],
* descriptions: {
* Counter: describeMetric("Counter"),
* Gauge: describeMetric("Gauge"),
* Frequency: describeMetric("Frequency"),
* Histogram: describeMetric("Histogram"),
* Summary: describeMetric("Summary")
* }
* }
* })
* ```
*
* @since 2.0.0
* @category types
*/
type Type = "Counter" | "Frequency" | "Gauge" | "Histogram" | "Summary";
/**
* Union type for metric attributes that can be provided as either an object or array of tuples.
*
* @example
* ```ts
* 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
* }
* }
* })
* ```
*
* @since 2.0.0
* @category types
*/
type Attributes = AttributeSet | ReadonlyArray<[string, string]>;
/**
* Type for metric attributes as a readonly record of string key-value pairs.
*
* @example
* ```ts
* import { Data, Effect, Metric } from "effect"
*
* class AttributeSetError extends Data.TaggedError("AttributeSetError")<{
* readonly operation: string
* }> {}
*
* const program = Effect.gen(function*() {
* // Define attribute sets for different contexts
* const serviceAttributes = {
* service: "user-api",
* version: "2.1.0",
* environment: "production"
* }
*
* const operationAttributes = {
* operation: "create_user",
* method: "POST",
* endpoint: "/api/users"
* }
*
* const infrastructureAttributes = {
* region: "us-east-1",
* datacenter: "dc1",
* host: "api-server-01"
* }
*
* // Create metrics with predefined attribute sets
* const requestCounter = Metric.counter("http_requests_total", {
* description: "Total HTTP requests",
* attributes: serviceAttributes
* })
*
* // Combine attribute sets
* const combineAttributes = (...attributeSets: Array>) =>
* Object.assign({}, ...attributeSets)
*
* const fullAttributes = combineAttributes(
* serviceAttributes,
* operationAttributes,
* infrastructureAttributes
* )
*
* // Create metric with combined attributes
* const detailedCounter = Metric.withAttributes(requestCounter, fullAttributes)
*
* // Helper to validate attribute keys (all must be strings)
* const validateAttributeSet = (attrs: Record): boolean => {
* return Object.entries(attrs).every(([key, value]) =>
* typeof key === "string" && typeof value === "string"
* )
* }
*
* yield* Metric.update(detailedCounter, 1)
*
* return {
* attributes: {
* service: serviceAttributes,
* operation: operationAttributes,
* infrastructure: infrastructureAttributes,
* combined: fullAttributes,
* isValid: validateAttributeSet(fullAttributes), // true
* totalKeys: Object.keys(fullAttributes).length // 9
* }
* }
* })
* ```
*
* @since 2.0.0
* @category types
*/
type AttributeSet = Readonly>;
/**
* Utility type to extract the Input type from a Metric type.
*
* @example
* ```ts
* import { Metric } from "effect"
*
* // Create various metric types
* const numberCounter = Metric.counter("requests")
* const bigintCounter = Metric.counter("bytes", { bigint: true })
* const stringFrequency = Metric.frequency("status_codes")
* const numberGauge = Metric.gauge("cpu_usage")
* const numberHistogram = Metric.histogram("response_time", {
* boundaries: Metric.linearBoundaries({ start: 0, width: 50, count: 10 })
* })
*
* // The Input utility type extracts the input type from metric types:
* // - Counter: number
* // - Counter: bigint
* // - Frequency: string
* // - Gauge: number
* // - Histogram: number
*
* // Helper function that works with any metric
* const createMetricInfo = (metric: Metric.Metric) => ({
* id: metric.id,
* type: metric.type
* })
*
* const metrics = [
* createMetricInfo(numberCounter), // { id: "requests", type: "Counter" }
* createMetricInfo(bigintCounter), // { id: "bytes", type: "Counter" }
* createMetricInfo(stringFrequency), // { id: "status_codes", type: "Frequency" }
* createMetricInfo(numberGauge), // { id: "cpu_usage", type: "Gauge" }
* createMetricInfo(numberHistogram) // { id: "response_time", type: "Histogram" }
* ]
*
* // Type safety is enforced at compile time:
* // Metric.update(numberCounter, 123) // ✓ Valid (number)
* // Metric.update(numberCounter, "abc") // ✗ Type error
* // Metric.update(stringFrequency, "ok") // ✓ Valid (string)
* // Metric.update(stringFrequency, 404) // ✗ Type error
* ```
*
* @since 2.0.0
* @category types
*/
type Input = A extends Metric ? _Input : never;
/**
* Utility type to extract the State type from a Metric type.
*
* @example
* ```ts
* import { Effect, Metric } from "effect"
*
* // Create various metric types
* const requestCounter = Metric.counter("requests")
* const cpuGauge = Metric.gauge("cpu_usage")
* const statusFrequency = Metric.frequency("status_codes")
* const responseHistogram = Metric.histogram("response_time", {
* boundaries: Metric.linearBoundaries({ start: 0, width: 50, count: 10 })
* })
* const latencySummary = Metric.summary("latency", {
* maxAge: "5 minutes",
* maxSize: 1000,
* quantiles: [0.5, 0.95, 0.99]
* })
*
* // The State utility type extracts the state type from metric types:
* // - Counter: CounterState
* // - Gauge: GaugeState
* // - Frequency: FrequencyState
* // - Histogram: HistogramState
* // - Summary: SummaryState
*
* // Type-safe state analysis functions
* const program = Effect.gen(function*() {
* // Update metrics first
* yield* Metric.update(requestCounter, 10)
* yield* Metric.update(cpuGauge, 85.5)
* yield* Metric.update(statusFrequency, "200")
* yield* Metric.update(responseHistogram, 150)
* yield* Metric.update(latencySummary, 120)
*
* // Extract states with proper typing
* const counterState = yield* Metric.value(requestCounter)
* const gaugeState = yield* Metric.value(cpuGauge)
* const frequencyState = yield* Metric.value(statusFrequency)
* const histogramState = yield* Metric.value(responseHistogram)
* const summaryState = yield* Metric.value(latencySummary)
*
* return {
* counter: { count: counterState.count }, // { count: 10 }
* gauge: { value: gaugeState.value }, // { value: 85.5 }
* frequency: { uniqueValues: frequencyState.occurrences.size }, // { uniqueValues: 1 }
* histogram: { totalObservations: histogramState.count }, // { totalObservations: 1 }
* summary: { observations: summaryState.count } // { observations: 1 }
* }
* })
* ```
*
* @since 2.0.0
* @category types
*/
type State = A extends Metric ? _State : never;
/**
* Interface defining the core hooks for metric operations: get, update, and modify.
*
* @example
* ```ts
* import { Data, Effect, Metric } from "effect"
*
* class HooksError extends Data.TaggedError("HooksError")<{
* readonly operation: string
* }> {}
*
* const program = Effect.gen(function*() {
* // Create a counter metric
* const requestCounter = Metric.counter("requests_total", {
* description: "Total number of requests"
* })
*
* // The Hooks interface provides three core operations for metrics:
* // 1. get: retrieve current state
* // 2. update: add/set a value
* // 3. modify: transform the current state
*
* // These are low-level APIs. Most users should use high-level APIs:
* // - Metric.value() for getting state
* // - Metric.update() for updating values
* // - Metric.modify() for modifying values
*
* // Example using high-level APIs (recommended)
* yield* Metric.update(requestCounter, 1)
* yield* Metric.update(requestCounter, 5)
* const state = yield* Metric.value(requestCounter)
*
* return {
* currentCount: state.count, // 6
* isIncremental: state.incremental // false
* }
* })
* ```
*
* @since 2.0.0
* @category interfaces
*/
interface Hooks {
readonly get: (context: Context.Context) => State;
readonly update: (input: Input, context: Context.Context) => void;
readonly modify: (input: Input, context: Context.Context) => void;
}
/**
* Interface containing complete metadata information about a metric.
*
* @example
* ```ts
* import { Data, Effect, Metric } from "effect"
*
* class MetadataError extends Data.TaggedError("MetadataError")<{
* readonly operation: string
* }> {}
*
* const program = Effect.gen(function*() {
* // Create metrics with different configurations
* const requestCounter = Metric.counter("http_requests_total", {
* description: "Total number of HTTP requests",
* attributes: { service: "api", version: "1.0" }
* })
*
* const memoryGauge = Metric.gauge("memory_usage_bytes", {
* description: "Current memory usage in bytes"
* })
*
* const statusFrequency = Metric.frequency("http_status_codes")
*
* // The Metadata interface contains complete information about a metric:
* // - id: metric identifier
* // - type: metric type ("Counter", "Gauge", etc.)
* // - description: optional description
* // - attributes: optional key-value attributes
* // - hooks: low-level operations interface
*
* // Each metric has associated metadata that can be inspected
* yield* Metric.update(requestCounter, 10)
* yield* Metric.update(memoryGauge, 256000000)
* yield* Metric.update(statusFrequency, "200")
*
* return {
* counter: {
* id: requestCounter.id, // "http_requests_total"
* type: requestCounter.type, // "Counter"
* description: requestCounter.description // "Total number of HTTP requests"
* },
* gauge: {
* id: memoryGauge.id, // "memory_usage_bytes"
* type: memoryGauge.type, // "Gauge"
* description: memoryGauge.description // "Current memory usage in bytes"
* },
* frequency: {
* id: statusFrequency.id, // "http_status_codes"
* type: statusFrequency.type, // "Frequency"
* description: statusFrequency.description // undefined
* }
* }
* })
* ```
*
* @since 4.0.0
* @category interfaces
*/
interface Metadata {
readonly id: string;
readonly type: Type;
readonly description: string | undefined;
readonly attributes: Metric.AttributeSet | undefined;
readonly hooks: Hooks;
}
/**
* Protocol interface for metric snapshots containing metadata and current state.
*
* @example
* ```ts
* import { Data, Effect, Metric } from "effect"
*
* class SnapshotProtoError extends Data.TaggedError("SnapshotProtoError")<{
* readonly operation: string
* }> {}
*
* const program = Effect.gen(function*() {
* // Create and update metrics
* const requestCounter = Metric.counter("requests", {
* description: "Request count",
* attributes: { service: "api" }
* })
*
* const responseTimeHistogram = Metric.histogram("response_time", {
* description: "Response time distribution",
* boundaries: Metric.linearBoundaries({ start: 0, width: 50, count: 10 })
* })
*
* yield* Metric.update(requestCounter, 25)
* yield* Metric.update(responseTimeHistogram, 150)
* yield* Metric.update(responseTimeHistogram, 75)
*
* // Take snapshot of all metrics
* const snapshots = yield* Metric.snapshot
*
* // Each snapshot follows the SnapshotProto interface:
* // - id: metric identifier
* // - type: specific metric type
* // - description: optional description
* // - attributes: optional attributes
* // - state: current metric state
*
* const counterSnapshot = snapshots.find((s) => s.id === "requests")
* const histogramSnapshot = snapshots.find((s) => s.id === "response_time")
*
* return {
* counter: counterSnapshot ?
* {
* id: counterSnapshot.id, // "requests"
* type: counterSnapshot.type, // "Counter"
* description: counterSnapshot.description, // "Request count"
* hasAttributes: counterSnapshot.attributes !== undefined, // true
* count: (counterSnapshot.state as any).count // 25
* } :
* null,
* histogram: histogramSnapshot ?
* {
* id: histogramSnapshot.id, // "response_time"
* type: histogramSnapshot.type, // "Histogram"
* observations: (histogramSnapshot.state as any).count // 2
* } :
* null
* }
* })
* ```
*
* @since 4.0.0
* @category interfaces
*/
interface SnapshotProto {
readonly id: string;
readonly type: T;
readonly description: string | undefined;
readonly attributes: Metric.AttributeSet | undefined;
readonly state: State;
}
/**
* Union type representing all possible metric snapshot types with their corresponding states.
*
* @example
* ```ts
* import { Data, Effect, Metric } from "effect"
*
* class SnapshotError extends Data.TaggedError("SnapshotError")<{
* readonly operation: string
* }> {}
*
* const program = Effect.gen(function*() {
* // Create different types of metrics
* const requestCounter = Metric.counter("requests_total")
* const cpuGauge = Metric.gauge("cpu_usage_percent")
* const statusFrequency = Metric.frequency("http_status")
* const responseHistogram = Metric.histogram("response_time_ms", {
* boundaries: Metric.linearBoundaries({ start: 0, width: 100, count: 10 })
* })
* const latencySummary = Metric.summary("request_latency", {
* maxAge: "1 minute",
* maxSize: 100,
* quantiles: [0.5, 0.95, 0.99]
* })
*
* // Update all metrics
* yield* Metric.update(requestCounter, 150)
* yield* Metric.update(cpuGauge, 45.7)
* yield* Metric.update(statusFrequency, "200")
* yield* Metric.update(statusFrequency, "404")
* yield* Metric.update(responseHistogram, 250)
* yield* Metric.update(latencySummary, 120)
*
* // Take snapshot of all metrics
* const allSnapshots = yield* Metric.snapshot
*
* // Type-safe snapshot analysis using discriminated union
* const analyzeSnapshot = (snapshot: any) => {
* switch (snapshot.type) {
* case "Counter":
* return { type: "Counter", count: snapshot.state.count }
* case "Gauge":
* return { type: "Gauge", value: snapshot.state.value }
* case "Frequency":
* return {
* type: "Frequency",
* uniqueValues: snapshot.state.occurrences.size
* }
* case "Histogram":
* return { type: "Histogram", observations: snapshot.state.count }
* case "Summary":
* return { type: "Summary", observations: snapshot.state.count }
* }
* }
*
* const analysis = allSnapshots.map(analyzeSnapshot)
*
* return {
* totalMetrics: allSnapshots.length, // 5
* metricTypes: allSnapshots.map((s) => s.type), // ["Counter", "Gauge", "Frequency", "Histogram", "Summary"]
* analysis
* }
* })
* ```
*
* @since 4.0.0
* @category types
*/
type Snapshot = SnapshotProto<"Counter", CounterState> | SnapshotProto<"Gauge", GaugeState> | SnapshotProto<"Frequency", FrequencyState> | SnapshotProto<"Histogram", HistogramState> | SnapshotProto<"Summary", SummaryState>;
}
/**
* Service key for the current metric attributes context.
*
* @example
* ```ts
* import { Data, Effect, Metric } from "effect"
*
* class AttributesKeyError extends Data.TaggedError("AttributesKeyError")<{
* readonly operation: string
* }> {}
*
* const program = Effect.gen(function*() {
* // The key is used internally by the Effect runtime to manage metric attributes
* const key = Metric.CurrentMetricAttributesKey
*
* // Create metrics with base attributes
* const requestCounter = Metric.counter("requests_total", {
* description: "Total HTTP requests"
* })
*
* // The CurrentMetricAttributes service provides default attributes
* // that get applied to all metrics in the current context
* const baseAttributes = { service: "api", version: "1.0" }
*
* // Use withAttributes to apply attributes to metrics
* const taggedCounter1 = Metric.withAttributes(requestCounter, baseAttributes)
* const program1 = Metric.update(taggedCounter1, 1)
*
* const taggedCounter2 = Metric.withAttributes(requestCounter, {
* ...baseAttributes,
* endpoint: "/users"
* })
* const program2 = Metric.update(taggedCounter2, 5)
*
* yield* program1
* yield* program2
*
* return {
* keyValue: key, // "effect/Metric/CurrentMetricAttributes"
* keyType: typeof key, // "string"
* isConstant: key === "effect/Metric/CurrentMetricAttributes" // true
* }
* })
* ```
*
* @since 4.0.0
* @category References
*/
export declare const CurrentMetricAttributesKey: "effect/Metric/CurrentMetricAttributes";
/**
* Service class for managing the current metric attributes context.
*
* @example
* ```ts
* import { Data, Effect, Metric } from "effect"
*
* class AttributesError extends Data.TaggedError("AttributesError")<{
* readonly operation: string
* }> {}
*
* const program = Effect.gen(function*() {
* // Access current metric attributes
* const attributes = yield* Metric.CurrentMetricAttributes
* console.log("Current attributes:", attributes)
*
* // Set new attributes context
* const newAttributes = { service: "api", version: "1.0" }
* const result = yield* Effect.provideService(
* Effect.gen(function*() {
* const updatedAttributes = yield* Metric.CurrentMetricAttributes
* return updatedAttributes
* }),
* Metric.CurrentMetricAttributes,
* newAttributes
* )
*
* return result
* })
* ```
*
* @since 4.0.0
* @category References
*/
export declare const CurrentMetricAttributes: Context.Reference>>;
/**
* Service class for accessing the current metric registry.
*
* @since 4.0.0
* @category References
*/
export declare const MetricRegistry: Context.Reference