1import type * as cloudwatch from "@distilled.cloud/aws/cloudwatch";
2import type * as Effect from "effect/Effect";
3import type * as Sink from "effect/Sink";
4import * as Binding from "../../Binding.ts";
5import type { BatchRetryExhaustedError } from "../internal/BatchedSink.ts";
6
7/**
8 * A raw `MetricDatum` as accepted by `PutMetricData`. Callers stay in control
9 * of `MetricName`, `Dimensions`, `Timestamp`, `Value`/`Values`/`Counts`,
10 * `StatisticValues`, `Unit` and `StorageResolution` — no auto-marshalling.
11 */
12export type MetricSinkDatum = cloudwatch.MetricDatum;
13
14export interface MetricSinkProps {
15 /**
16 * The CloudWatch namespace every datum is published under
17 * (e.g. `"MyApp/Payments"`). Must not start with `AWS/`.
18 */
19 readonly Namespace: string;
20}
21
22export type MetricSinkError =
23 | cloudwatch.PutMetricDataError
24 | BatchRetryExhaustedError<MetricSinkDatum>;
25
26/**
27 * A batching sink over CloudWatch `PutMetricData` (1000 datums / ~1 MB per
28 * call). Each upstream chunk is greedily packed into order-preserving
29 * batches and sent sequentially.
30 *
31 * `PutMetricData` is all-or-nothing: there are no per-datum partial
32 * failures, so a failed call surfaces directly on the sink's error channel
33 * as the typed `PutMetricDataError` union.
34 *
35 * Provide `CloudWatch.MetricSinkHttp` (which itself needs
36 * `CloudWatch.PutMetricDataHttp`) on the hosting Lambda Function:
37 * `Effect.provide(Layer.provideMerge(AWS.CloudWatch.MetricSinkHttp, AWS.CloudWatch.PutMetricDataHttp))`.
38 *
39 * ### Streaming Metrics
40 * **Example:** Stream Datums into CloudWatch
41 * ```typescript
42 * // init — grants cloudwatch:PutMetricData; all datums publish under Namespace
43 * const sink = yield* AWS.CloudWatch.MetricSink({
44 * Namespace: "MyApp/Payments",
45 * });
46 *
47 * // runtime — datums are packed into 1000-datum PutMetricData batches
48 * yield* Stream.fromIterable(
49 * payments.map((payment) => ({
50 * MetricName: "PaymentProcessed",
51 * Dimensions: [{ Name: "Region", Value: payment.region }],
52 * Value: payment.amount,
53 * Unit: "Count",
54 * }) satisfies AWS.CloudWatch.MetricSinkDatum),
55 * ).pipe(Stream.run(sink));
56 * ```
57 *
58 * @binding
59 */
60export interface MetricSink extends Binding.Service<
61 MetricSink,
62 "AWS.CloudWatch.MetricSink",
63 (
64 props: MetricSinkProps,
65 ) => Effect.Effect<
66 Sink.Sink<
67 void,
68 MetricSinkDatum,
69 readonly MetricSinkDatum[],
70 MetricSinkError
71 >
72 >
73> {}
74
75export const MetricSink = Binding.Service<MetricSink>(
76 "AWS.CloudWatch.MetricSink",
77);