@alchemy.run/axiom 0.0.0-stage → 2.0.0-beta.84
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +58 -2
- package/lib/Annotation.d.ts +51 -0
- package/lib/Annotation.d.ts.map +1 -0
- package/lib/Annotation.js +97 -0
- package/lib/Annotation.js.map +1 -0
- package/lib/ApiToken.d.ts +64 -0
- package/lib/ApiToken.d.ts.map +1 -0
- package/lib/ApiToken.js +123 -0
- package/lib/ApiToken.js.map +1 -0
- package/lib/AuthProvider.d.ts +40 -0
- package/lib/AuthProvider.d.ts.map +1 -0
- package/lib/AuthProvider.js +99 -0
- package/lib/AuthProvider.js.map +1 -0
- package/lib/Chart.d.ts +130 -0
- package/lib/Chart.d.ts.map +1 -0
- package/lib/Chart.js +14 -0
- package/lib/Chart.js.map +1 -0
- package/lib/Contributions.d.ts +7 -0
- package/lib/Contributions.d.ts.map +1 -0
- package/lib/Contributions.js +6 -0
- package/lib/Contributions.js.map +1 -0
- package/lib/Credentials.d.ts +10 -0
- package/lib/Credentials.d.ts.map +1 -0
- package/lib/Credentials.js +30 -0
- package/lib/Credentials.js.map +1 -0
- package/lib/Dashboard.d.ts +112 -0
- package/lib/Dashboard.d.ts.map +1 -0
- package/lib/Dashboard.js +157 -0
- package/lib/Dashboard.js.map +1 -0
- package/lib/Dataset.d.ts +120 -0
- package/lib/Dataset.d.ts.map +1 -0
- package/lib/Dataset.js +215 -0
- package/lib/Dataset.js.map +1 -0
- package/lib/Monitor.d.ts +76 -0
- package/lib/Monitor.d.ts.map +1 -0
- package/lib/Monitor.js +126 -0
- package/lib/Monitor.js.map +1 -0
- package/lib/Notifier.d.ts +65 -0
- package/lib/Notifier.d.ts.map +1 -0
- package/lib/Notifier.js +112 -0
- package/lib/Notifier.js.map +1 -0
- package/lib/OtelHeaders.d.ts +9 -0
- package/lib/OtelHeaders.d.ts.map +1 -0
- package/lib/OtelHeaders.js +9 -0
- package/lib/OtelHeaders.js.map +1 -0
- package/lib/Providers.d.ts +15 -0
- package/lib/Providers.d.ts.map +1 -0
- package/lib/Providers.js +34 -0
- package/lib/Providers.js.map +1 -0
- package/lib/Telemetry.d.ts +66 -0
- package/lib/Telemetry.d.ts.map +1 -0
- package/lib/Telemetry.js +68 -0
- package/lib/Telemetry.js.map +1 -0
- package/lib/View.d.ts +57 -0
- package/lib/View.d.ts.map +1 -0
- package/lib/View.js +100 -0
- package/lib/View.js.map +1 -0
- package/lib/VirtualField.d.ts +44 -0
- package/lib/VirtualField.d.ts.map +1 -0
- package/lib/VirtualField.js +97 -0
- package/lib/VirtualField.js.map +1 -0
- package/lib/index.d.ts +14 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +14 -0
- package/lib/index.js.map +1 -0
- package/package.json +68 -3
- package/src/Annotation.ts +121 -0
- package/src/ApiToken.ts +148 -0
- package/src/AuthProvider.ts +135 -0
- package/src/Chart.ts +156 -0
- package/src/Contributions.ts +9 -0
- package/src/Credentials.ts +68 -0
- package/src/Dashboard.ts +192 -0
- package/src/Dataset.ts +326 -0
- package/src/Monitor.ts +148 -0
- package/src/Notifier.ts +136 -0
- package/src/OtelHeaders.ts +9 -0
- package/src/Providers.ts +60 -0
- package/src/Telemetry.ts +116 -0
- package/src/View.ts +130 -0
- package/src/VirtualField.ts +123 -0
- package/src/index.ts +13 -0
package/lib/Notifier.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import * as Axiom from "@distilled.cloud/axiom";
|
|
2
|
+
import * as Provider from "alchemy/Provider";
|
|
3
|
+
import { Resource } from "alchemy/Resource";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
/**
|
|
6
|
+
* An Axiom notifier — an alert destination (Slack, email, PagerDuty,
|
|
7
|
+
* Opsgenie, Discord, Microsoft Teams, generic webhook, or a fully custom
|
|
8
|
+
* webhook with templated body/headers) that {@link Monitor monitors} target
|
|
9
|
+
* via `notifierIds`. Exactly one channel under `properties` should be set.
|
|
10
|
+
* @see https://axiom.co/docs/monitor-data/notifiers
|
|
11
|
+
*
|
|
12
|
+
* ### Creating a Notifier
|
|
13
|
+
* **Example:** Slack incoming webhook
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const slack = yield* Axiom.Notifier("ops-slack", {
|
|
16
|
+
* name: "ops-channel",
|
|
17
|
+
* properties: {
|
|
18
|
+
* slack: { slackUrl: process.env.SLACK_WEBHOOK_URL! },
|
|
19
|
+
* },
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* **Example:** Email distribution list
|
|
24
|
+
* ```typescript
|
|
25
|
+
* yield* Axiom.Notifier("ops-email", {
|
|
26
|
+
* name: "ops-team",
|
|
27
|
+
* properties: { email: { emails: ["sre@example.com", "oncall@example.com"] } },
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* **Example:** PagerDuty integration
|
|
32
|
+
* ```typescript
|
|
33
|
+
* yield* Axiom.Notifier("pagerduty", {
|
|
34
|
+
* name: "primary-oncall",
|
|
35
|
+
* properties: {
|
|
36
|
+
* pagerduty: { routingKey: process.env.PAGERDUTY_ROUTING_KEY!, token: "" },
|
|
37
|
+
* },
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* **Example:** Custom webhook with templated body
|
|
42
|
+
* ```typescript
|
|
43
|
+
* yield* Axiom.Notifier("incident-webhook", {
|
|
44
|
+
* name: "incident.io",
|
|
45
|
+
* properties: {
|
|
46
|
+
* customWebhook: {
|
|
47
|
+
* url: "https://api.incident.io/v2/alert_events",
|
|
48
|
+
* headers: { "Content-Type": "application/json" },
|
|
49
|
+
* secretHeaders: { Authorization: `Bearer ${process.env.INCIDENT_TOKEN}` },
|
|
50
|
+
* body: '{"title": "{{.Monitor.Name}}", "status": "firing"}',
|
|
51
|
+
* },
|
|
52
|
+
* },
|
|
53
|
+
* });
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @resource
|
|
57
|
+
* @product Monitor
|
|
58
|
+
*/
|
|
59
|
+
export const Notifier = Resource("Axiom.Notifier");
|
|
60
|
+
export const NotifierProvider = () => Provider.effect(Notifier, Effect.gen(function* () {
|
|
61
|
+
const create = yield* Axiom.createNotifier;
|
|
62
|
+
const update = yield* Axiom.updateNotifier;
|
|
63
|
+
const get = yield* Axiom.getNotifier;
|
|
64
|
+
const listNotifiers = yield* Axiom.getNotifiers;
|
|
65
|
+
const del = yield* Axiom.deleteNotifier;
|
|
66
|
+
return {
|
|
67
|
+
stables: ["id"],
|
|
68
|
+
// Enumerate every notifier in the org. Axiom exposes a single
|
|
69
|
+
// account-wide `GET /v2/notifiers` collection op (no pagination), so we
|
|
70
|
+
// fetch it once and hydrate each row into the exact `read` Attributes
|
|
71
|
+
// shape (`CreateNotifierOutput & { id: string }`) — directly usable by
|
|
72
|
+
// `delete` with no follow-up get.
|
|
73
|
+
list: () => Effect.gen(function* () {
|
|
74
|
+
const notifiers = yield* listNotifiers({});
|
|
75
|
+
return notifiers.map((n) => ({ ...n, id: n.id ?? "" }));
|
|
76
|
+
}),
|
|
77
|
+
reconcile: Effect.fn(function* ({ news, output }) {
|
|
78
|
+
// Observe — Axiom assigns the notifier id server-side, so the
|
|
79
|
+
// only handle to a previously-created notifier is the cached
|
|
80
|
+
// `output.id`. Probe for live state with that id; treat NotFound
|
|
81
|
+
// (deleted out-of-band) as "no observed state" so we converge
|
|
82
|
+
// by re-creating.
|
|
83
|
+
const observed = output?.id
|
|
84
|
+
? yield* get({ id: output.id }).pipe(Effect.catchTag("NotFound", () => Effect.succeed(undefined)))
|
|
85
|
+
: undefined;
|
|
86
|
+
// Ensure — POST mints a new notifier. The Axiom create response
|
|
87
|
+
// sometimes omits `id` on the body; default to "" to keep the
|
|
88
|
+
// attribute shape stable until the next read populates it.
|
|
89
|
+
if (observed === undefined) {
|
|
90
|
+
const result = yield* create(news);
|
|
91
|
+
return { ...result, id: result.id ?? "" };
|
|
92
|
+
}
|
|
93
|
+
// Sync — the notifier exists; PATCH against its id with the
|
|
94
|
+
// desired props. Preserve the cached id if the API response
|
|
95
|
+
// omits it.
|
|
96
|
+
const result = yield* update({ ...news, id: observed.id });
|
|
97
|
+
return { ...result, id: result.id ?? observed.id };
|
|
98
|
+
}),
|
|
99
|
+
delete: Effect.fn(function* ({ output }) {
|
|
100
|
+
yield* del({ id: output.id }).pipe(Effect.catchTag("NotFound", () => Effect.void));
|
|
101
|
+
}),
|
|
102
|
+
read: Effect.fn(function* ({ output }) {
|
|
103
|
+
if (!output?.id)
|
|
104
|
+
return undefined;
|
|
105
|
+
return yield* get({ id: output.id }).pipe(Effect.map((current) => ({
|
|
106
|
+
...current,
|
|
107
|
+
id: current.id ?? output.id,
|
|
108
|
+
})), Effect.catchTag("NotFound", () => Effect.succeed(undefined)));
|
|
109
|
+
}),
|
|
110
|
+
};
|
|
111
|
+
}));
|
|
112
|
+
//# sourceMappingURL=Notifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Notifier.js","sourceRoot":"","sources":["../src/Notifier.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Axiom routes OTLP ingest by a dataset header. Traces and logs use
|
|
3
|
+
* `X-Axiom-Dataset`; the metrics endpoint requires `X-Axiom-Metrics-Dataset`
|
|
4
|
+
* instead (https://axiom.co/docs/send-data/opentelemetry).
|
|
5
|
+
*
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
export declare const otelDatasetHeader: (metrics: boolean) => string;
|
|
9
|
+
//# sourceMappingURL=OtelHeaders.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OtelHeaders.d.ts","sourceRoot":"","sources":["../src/OtelHeaders.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,YAAa,OAAO,KAAG,MACI,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Axiom routes OTLP ingest by a dataset header. Traces and logs use
|
|
3
|
+
* `X-Axiom-Dataset`; the metrics endpoint requires `X-Axiom-Metrics-Dataset`
|
|
4
|
+
* instead (https://axiom.co/docs/send-data/opentelemetry).
|
|
5
|
+
*
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
export const otelDatasetHeader = (metrics) => metrics ? "X-Axiom-Metrics-Dataset" : "X-Axiom-Dataset";
|
|
9
|
+
//# sourceMappingURL=OtelHeaders.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OtelHeaders.js","sourceRoot":"","sources":["../src/OtelHeaders.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,OAAgB,EAAU,EAAE,CAC5D,OAAO,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as Provider from "alchemy/Provider";
|
|
2
|
+
import * as Layer from "effect/Layer";
|
|
3
|
+
import * as Credentials from "./Credentials.ts";
|
|
4
|
+
export { Credentials } from "@distilled.cloud/axiom/Credentials";
|
|
5
|
+
declare const Providers_base: Provider.ProviderCollection<Providers, "Axiom">;
|
|
6
|
+
export declare class Providers extends Providers_base {
|
|
7
|
+
}
|
|
8
|
+
export type ProviderRequirements = Layer.Services<ReturnType<typeof providers>>;
|
|
9
|
+
/**
|
|
10
|
+
* Axiom providers and credentials. Wires up Dataset, ApiToken, Notifier,
|
|
11
|
+
* Monitor, Dashboard, VirtualField, View and Annotation resources, and
|
|
12
|
+
* registers the Axiom AuthProvider so `alchemy profile edit` can configure it.
|
|
13
|
+
*/
|
|
14
|
+
export declare const providers: () => Layer.Layer<Credentials.Credentials | import("alchemy/Auth/Credentials").CredentialsStore | import("effect/http/HttpClient").HttpClient | import("alchemy/Auth/Profile").ProfileStore | Providers, never, import("alchemy/Auth/AuthProvider").AuthProviders | import("effect/FileSystem").FileSystem | import("effect/Path").Path | import("alchemy/Stack").Stack | import("alchemy/Stage").Stage>;
|
|
15
|
+
//# sourceMappingURL=Providers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Providers.d.ts","sourceRoot":"","sources":["../src/Providers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAItC,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAQhD,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;;AAEjE,qBAAa,SAAU,SAAQ,cAAiD;CAAG;AAEnF,MAAM,MAAM,oBAAoB,GAAG,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC;AAEhF;;;;GAIG;AACH,eAAO,MAAM,SAAS,0YAgCnB,CAAC"}
|
package/lib/Providers.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { CredentialsStoreLive } from "alchemy/Auth/Credentials";
|
|
2
|
+
import { ProfileStoreLive } from "alchemy/Auth/Profile";
|
|
3
|
+
import * as Provider from "alchemy/Provider";
|
|
4
|
+
import * as FetchHttpClient from "effect/http/FetchHttpClient";
|
|
5
|
+
import * as Layer from "effect/Layer";
|
|
6
|
+
import { Annotation, AnnotationProvider } from "./Annotation.js";
|
|
7
|
+
import { ApiToken, ApiTokenProvider } from "./ApiToken.js";
|
|
8
|
+
import { AxiomAuth } from "./AuthProvider.js";
|
|
9
|
+
import * as Credentials from "./Credentials.js";
|
|
10
|
+
import { Dashboard, DashboardProvider } from "./Dashboard.js";
|
|
11
|
+
import { Dataset, DatasetProvider } from "./Dataset.js";
|
|
12
|
+
import { Monitor, MonitorProvider } from "./Monitor.js";
|
|
13
|
+
import { Notifier, NotifierProvider } from "./Notifier.js";
|
|
14
|
+
import { View, ViewProvider } from "./View.js";
|
|
15
|
+
import { VirtualField, VirtualFieldProvider } from "./VirtualField.js";
|
|
16
|
+
export { Credentials } from "@distilled.cloud/axiom/Credentials";
|
|
17
|
+
export class Providers extends Provider.ProviderCollection()("Axiom") {
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Axiom providers and credentials. Wires up Dataset, ApiToken, Notifier,
|
|
21
|
+
* Monitor, Dashboard, VirtualField, View and Annotation resources, and
|
|
22
|
+
* registers the Axiom AuthProvider so `alchemy profile edit` can configure it.
|
|
23
|
+
*/
|
|
24
|
+
export const providers = () => Layer.effect(Providers, Provider.collection([
|
|
25
|
+
Annotation,
|
|
26
|
+
ApiToken,
|
|
27
|
+
Dashboard,
|
|
28
|
+
Dataset,
|
|
29
|
+
Monitor,
|
|
30
|
+
Notifier,
|
|
31
|
+
View,
|
|
32
|
+
VirtualField,
|
|
33
|
+
])).pipe(Layer.provide(Layer.mergeAll(AnnotationProvider(), ApiTokenProvider(), DashboardProvider(), DatasetProvider(), MonitorProvider(), NotifierProvider(), ViewProvider(), VirtualFieldProvider())), Layer.provideMerge(Credentials.fromAuthProvider()), Layer.provideMerge(FetchHttpClient.layer), Layer.provideMerge(AxiomAuth), Layer.provideMerge(ProfileStoreLive), Layer.provideMerge(CredentialsStoreLive), Layer.orDie);
|
|
34
|
+
//# sourceMappingURL=Providers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Providers.js","sourceRoot":"","sources":["../src/Providers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,eAAe,MAAM,6BAA6B,CAAC;AAC/D,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEvE,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AAEjE,MAAM,OAAO,SAAU,SAAQ,QAAQ,CAAC,kBAAkB,EAAa,CAAC,OAAO,CAAC;CAAG;AAInF;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,EAAE,CAC5B,KAAK,CAAC,MAAM,CACV,SAAS,EACT,QAAQ,CAAC,UAAU,CAAC;IAClB,UAAU;IACV,QAAQ;IACR,SAAS;IACT,OAAO;IACP,OAAO;IACP,QAAQ;IACR,IAAI;IACJ,YAAY;CACb,CAAC,CACH,CAAC,IAAI,CACJ,KAAK,CAAC,OAAO,CACX,KAAK,CAAC,QAAQ,CACZ,kBAAkB,EAAE,EACpB,gBAAgB,EAAE,EAClB,iBAAiB,EAAE,EACnB,eAAe,EAAE,EACjB,eAAe,EAAE,EACjB,gBAAgB,EAAE,EAClB,YAAY,EAAE,EACd,oBAAoB,EAAE,CACvB,CACF,EACD,KAAK,CAAC,YAAY,CAAC,WAAW,CAAC,gBAAgB,EAAE,CAAC,EAClD,KAAK,CAAC,YAAY,CAAC,eAAe,CAAC,KAAK,CAAC,EACzC,KAAK,CAAC,YAAY,CAAC,SAAS,CAAC,EAC7B,KAAK,CAAC,YAAY,CAAC,gBAAgB,CAAC,EACpC,KAAK,CAAC,YAAY,CAAC,oBAAoB,CAAC,EACxC,KAAK,CAAC,KAAK,CACZ,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Input } from "alchemy/Input";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Layer from "effect/Layer";
|
|
4
|
+
import type { ApiToken } from "./ApiToken.ts";
|
|
5
|
+
import type { Dataset } from "./Dataset.ts";
|
|
6
|
+
/**
|
|
7
|
+
* A resource passed to the layer: either the module-scope declaration (an
|
|
8
|
+
* Effect that resolves to the instance, same as what capability bindings
|
|
9
|
+
* accept) or an already-yielded instance.
|
|
10
|
+
*/
|
|
11
|
+
export type ResourceInput<T> = T | Effect.Effect<T, never, any>;
|
|
12
|
+
/**
|
|
13
|
+
* Options for {@link Telemetry}: an ingest {@link ApiToken} plus one
|
|
14
|
+
* {@link Dataset} per OTel signal to export.
|
|
15
|
+
*/
|
|
16
|
+
export interface AxiomTelemetryProps {
|
|
17
|
+
/**
|
|
18
|
+
* The ingest credential. Must have `ingest: ["create"]` capability on
|
|
19
|
+
* every dataset passed below.
|
|
20
|
+
*/
|
|
21
|
+
token: ResourceInput<ApiToken>;
|
|
22
|
+
/** Dataset (kind `otel:traces:v1`) to export traces into. */
|
|
23
|
+
traces?: ResourceInput<Dataset> | undefined;
|
|
24
|
+
/** Dataset (kind `otel:logs:v1`) to export logs into. */
|
|
25
|
+
logs?: ResourceInput<Dataset> | undefined;
|
|
26
|
+
/** Dataset (kind `otel:metrics:v1`) to export metrics into. */
|
|
27
|
+
metrics?: ResourceInput<Dataset> | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* The exported `service.name`.
|
|
30
|
+
* @default the deployed Function/Worker's physical name
|
|
31
|
+
*/
|
|
32
|
+
serviceName?: Input<string> | undefined;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Export a Function/Worker's telemetry to Axiom.
|
|
36
|
+
*
|
|
37
|
+
* A binding layer over {@link layerOtlp | Alchemy.Telemetry.layerOtlp}:
|
|
38
|
+
* building it binds each dataset's OTLP endpoint and the ingest token's
|
|
39
|
+
* `Authorization` header (as a secret) onto the host, and at runtime the
|
|
40
|
+
* built-in exporter ships each signal to its dataset, flushed per event.
|
|
41
|
+
*
|
|
42
|
+
* Compose it into the Function/Worker's single `Effect.provide`:
|
|
43
|
+
*
|
|
44
|
+
* ```ts
|
|
45
|
+
* import * as Axiom from "@alchemy.run/axiom";
|
|
46
|
+
* import { Bucket } from "./bucket.ts";
|
|
47
|
+
* import { Ingest, Logs, Traces } from "./observability.ts";
|
|
48
|
+
*
|
|
49
|
+
* export default Cloudflare.Worker(
|
|
50
|
+
* "Worker",
|
|
51
|
+
* { main: import.meta.url },
|
|
52
|
+
* Effect.gen(function* () {
|
|
53
|
+
* // ...
|
|
54
|
+
* }).pipe(
|
|
55
|
+
* Effect.provide(
|
|
56
|
+
* Layer.mergeAll(
|
|
57
|
+
* Cloudflare.R2.ReadWriteBucketBinding,
|
|
58
|
+
* Axiom.Telemetry({ token: Ingest, traces: Traces, logs: Logs }),
|
|
59
|
+
* ),
|
|
60
|
+
* ),
|
|
61
|
+
* ),
|
|
62
|
+
* );
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export declare const Telemetry: (props: AxiomTelemetryProps) => Layer.Layer<never>;
|
|
66
|
+
//# sourceMappingURL=Telemetry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Telemetry.d.ts","sourceRoot":"","sources":["../src/Telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAG3C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAEtC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAG5C;;;;GAIG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;AAEhE;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,KAAK,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;IAC/B,6DAA6D;IAC7D,MAAM,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC;IAC5C,yDAAyD;IACzD,IAAI,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC;IAC1C,+DAA+D;IAC/D,OAAO,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC;IAC7C;;;OAGG;IACH,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;CACzC;AA6BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,SAAS,UAAW,mBAAmB,KAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAiB/C,CAAC"}
|
package/lib/Telemetry.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import * as Output from "alchemy/Output";
|
|
2
|
+
import { layerOtlp } from "alchemy/Telemetry";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Layer from "effect/Layer";
|
|
5
|
+
import * as Redacted from "effect/Redacted";
|
|
6
|
+
import { otelDatasetHeader } from "./OtelHeaders.js";
|
|
7
|
+
/**
|
|
8
|
+
* Resource declarations are Effects — yield them to get the instance with
|
|
9
|
+
* attribute Output accessors, same as `Binding.Service`'s callable does for
|
|
10
|
+
* capability bindings.
|
|
11
|
+
*/
|
|
12
|
+
const instance = (resource) => Effect.isEffect(resource) ? resource : Effect.succeed(resource);
|
|
13
|
+
const signal = (token, dataset, urlAttr) => dataset === undefined
|
|
14
|
+
? undefined
|
|
15
|
+
: {
|
|
16
|
+
url: dataset[urlAttr],
|
|
17
|
+
headers: {
|
|
18
|
+
Authorization: Output.map(token.token, (bearer) => Redacted.make(`Bearer ${Redacted.value(bearer)}`)),
|
|
19
|
+
[otelDatasetHeader(urlAttr === "otelMetricsEndpoint")]: dataset.name,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Export a Function/Worker's telemetry to Axiom.
|
|
24
|
+
*
|
|
25
|
+
* A binding layer over {@link layerOtlp | Alchemy.Telemetry.layerOtlp}:
|
|
26
|
+
* building it binds each dataset's OTLP endpoint and the ingest token's
|
|
27
|
+
* `Authorization` header (as a secret) onto the host, and at runtime the
|
|
28
|
+
* built-in exporter ships each signal to its dataset, flushed per event.
|
|
29
|
+
*
|
|
30
|
+
* Compose it into the Function/Worker's single `Effect.provide`:
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* import * as Axiom from "@alchemy.run/axiom";
|
|
34
|
+
* import { Bucket } from "./bucket.ts";
|
|
35
|
+
* import { Ingest, Logs, Traces } from "./observability.ts";
|
|
36
|
+
*
|
|
37
|
+
* export default Cloudflare.Worker(
|
|
38
|
+
* "Worker",
|
|
39
|
+
* { main: import.meta.url },
|
|
40
|
+
* Effect.gen(function* () {
|
|
41
|
+
* // ...
|
|
42
|
+
* }).pipe(
|
|
43
|
+
* Effect.provide(
|
|
44
|
+
* Layer.mergeAll(
|
|
45
|
+
* Cloudflare.R2.ReadWriteBucketBinding,
|
|
46
|
+
* Axiom.Telemetry({ token: Ingest, traces: Traces, logs: Logs }),
|
|
47
|
+
* ),
|
|
48
|
+
* ),
|
|
49
|
+
* ),
|
|
50
|
+
* );
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export const Telemetry = (props) => Layer.unwrap(Effect.gen(function* () {
|
|
54
|
+
// Declarations are yielded to instances (registering them on the
|
|
55
|
+
// Stack if the enclosing host is the first to reference them), so
|
|
56
|
+
// attribute accessors below produce real Outputs.
|
|
57
|
+
const token = yield* instance(props.token);
|
|
58
|
+
const traces = props.traces && (yield* instance(props.traces));
|
|
59
|
+
const logs = props.logs && (yield* instance(props.logs));
|
|
60
|
+
const metrics = props.metrics && (yield* instance(props.metrics));
|
|
61
|
+
return layerOtlp({
|
|
62
|
+
serviceName: props.serviceName,
|
|
63
|
+
traces: signal(token, traces, "otelTracesEndpoint"),
|
|
64
|
+
logs: signal(token, logs, "otelLogsEndpoint"),
|
|
65
|
+
metrics: signal(token, metrics, "otelMetricsEndpoint"),
|
|
66
|
+
});
|
|
67
|
+
}));
|
|
68
|
+
//# sourceMappingURL=Telemetry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Telemetry.js","sourceRoot":"","sources":["../src/Telemetry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,SAAS,EAA0B,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAG5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAgCrD;;;;GAIG;AACH,MAAM,QAAQ,GAAG,CAAI,QAA0B,EAAoB,EAAE,CACnE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAE,QAA6B,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;AAExF,MAAM,MAAM,GAAG,CACb,KAAe,EACf,OAA4B,EAC5B,OAA0E,EAC3C,EAAE,CACjC,OAAO,KAAK,SAAS;IACnB,CAAC,CAAC,SAAS;IACX,CAAC,CAAC;QACE,GAAG,EAAE,OAAO,CAAC,OAAO,CAAC;QACrB,OAAO,EAAE;YACP,aAAa,EAAE,MAAM,CAAC,GAAG,CACvB,KAAK,CAAC,KAAK,EACX,CAAC,MAAM,EAAE,EAAE,CACT,QAAQ,CAAC,IAAI,CAAC,UAAU,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAA8B,CACjF;YACD,CAAC,iBAAiB,CAAC,OAAO,KAAK,qBAAqB,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI;SACrE;KACF,CAAC;AAER;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAA0B,EAAsB,EAAE,CAC1E,KAAK,CAAC,MAAM,CACV,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,iEAAiE;IACjE,kEAAkE;IAClE,kDAAkD;IAClD,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/D,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IACzD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;IAClE,OAAO,SAAS,CAAC;QACf,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,oBAAoB,CAAC;QACnD,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,kBAAkB,CAAC;QAC7C,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,qBAAqB,CAAC;KACvD,CAAC,CAAC;AACL,CAAC,CAAC,CACmB,CAAC"}
|
package/lib/View.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import * as Axiom from "@distilled.cloud/axiom";
|
|
2
|
+
import * as Provider from "alchemy/Provider";
|
|
3
|
+
import { Resource } from "alchemy/Resource";
|
|
4
|
+
import type { Providers } from "./Providers.ts";
|
|
5
|
+
export type ViewProps = Axiom.CreateViewRequest;
|
|
6
|
+
export type View = Resource<"Axiom.View", ViewProps, Axiom.View & {
|
|
7
|
+
/**
|
|
8
|
+
* Path identifier used by `updateView` / `getView` / `deleteView`.
|
|
9
|
+
* Currently derived from `name` because Axiom's view list/get responses
|
|
10
|
+
* don't expose a separate id field.
|
|
11
|
+
*/
|
|
12
|
+
id: string;
|
|
13
|
+
}, never, Providers>;
|
|
14
|
+
/**
|
|
15
|
+
* An Axiom saved view — a named, shareable APL query. Useful for building
|
|
16
|
+
* starter dashboards, providing canned "open in Axiom" links from your app,
|
|
17
|
+
* or pinning common investigations the team revisits.
|
|
18
|
+
*
|
|
19
|
+
* The path identifier is `name`. Renaming a view triggers a replacement
|
|
20
|
+
* (the old one is deleted, a new one is created).
|
|
21
|
+
* @see https://axiom.co/docs/query-data/datasets — APL query reference
|
|
22
|
+
*
|
|
23
|
+
* ### Creating a View
|
|
24
|
+
* **Example:** Recent errors across one dataset
|
|
25
|
+
* ```typescript
|
|
26
|
+
* yield* Axiom.View("recent-errors", {
|
|
27
|
+
* name: "recent-errors",
|
|
28
|
+
* description: "Last 100 5xx responses",
|
|
29
|
+
* datasets: ["my-app-traces"],
|
|
30
|
+
* aplQuery: `
|
|
31
|
+
* ['my-app-traces']
|
|
32
|
+
* | where status >= 500
|
|
33
|
+
* | order by _time desc
|
|
34
|
+
* | take 100
|
|
35
|
+
* `,
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* **Example:** Cross-dataset join (logs + traces by trace_id)
|
|
40
|
+
* ```typescript
|
|
41
|
+
* yield* Axiom.View("trace-with-logs", {
|
|
42
|
+
* name: "trace-with-logs",
|
|
43
|
+
* datasets: ["my-app-traces", "my-app-logs"],
|
|
44
|
+
* aplQuery: `
|
|
45
|
+
* ['my-app-traces']
|
|
46
|
+
* | where duration_ms > 1000
|
|
47
|
+
* | join kind=leftouter (['my-app-logs']) on trace_id
|
|
48
|
+
* `,
|
|
49
|
+
* });
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @resource
|
|
53
|
+
* @product Dashboard
|
|
54
|
+
*/
|
|
55
|
+
export declare const View: import("alchemy/Resource").ResourceClass<View>;
|
|
56
|
+
export declare const ViewProvider: () => import("effect/Layer").Layer<Provider.Provider<View>, never, Axiom.AxiomOpContext>;
|
|
57
|
+
//# sourceMappingURL=View.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"View.d.ts","sourceRoot":"","sources":["../src/View.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,wBAAwB,CAAC;AAEhD,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,MAAM,SAAS,GAAG,KAAK,CAAC,iBAAiB,CAAC;AAEhD,MAAM,MAAM,IAAI,GAAG,QAAQ,CACzB,YAAY,EACZ,SAAS,EACT,KAAK,CAAC,IAAI,GAAG;IACX;;;;OAIG;IACH,EAAE,EAAE,MAAM,CAAC;CACZ,EACD,KAAK,EACL,SAAS,CACV,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,eAAO,MAAM,IAAI,gDAA+B,CAAC;AAEjD,eAAO,MAAM,YAAY,0FA8DtB,CAAC"}
|
package/lib/View.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import * as Axiom from "@distilled.cloud/axiom";
|
|
2
|
+
import { isResolved } from "alchemy/Diff";
|
|
3
|
+
import * as Provider from "alchemy/Provider";
|
|
4
|
+
import { Resource } from "alchemy/Resource";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
/**
|
|
7
|
+
* An Axiom saved view — a named, shareable APL query. Useful for building
|
|
8
|
+
* starter dashboards, providing canned "open in Axiom" links from your app,
|
|
9
|
+
* or pinning common investigations the team revisits.
|
|
10
|
+
*
|
|
11
|
+
* The path identifier is `name`. Renaming a view triggers a replacement
|
|
12
|
+
* (the old one is deleted, a new one is created).
|
|
13
|
+
* @see https://axiom.co/docs/query-data/datasets — APL query reference
|
|
14
|
+
*
|
|
15
|
+
* ### Creating a View
|
|
16
|
+
* **Example:** Recent errors across one dataset
|
|
17
|
+
* ```typescript
|
|
18
|
+
* yield* Axiom.View("recent-errors", {
|
|
19
|
+
* name: "recent-errors",
|
|
20
|
+
* description: "Last 100 5xx responses",
|
|
21
|
+
* datasets: ["my-app-traces"],
|
|
22
|
+
* aplQuery: `
|
|
23
|
+
* ['my-app-traces']
|
|
24
|
+
* | where status >= 500
|
|
25
|
+
* | order by _time desc
|
|
26
|
+
* | take 100
|
|
27
|
+
* `,
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* **Example:** Cross-dataset join (logs + traces by trace_id)
|
|
32
|
+
* ```typescript
|
|
33
|
+
* yield* Axiom.View("trace-with-logs", {
|
|
34
|
+
* name: "trace-with-logs",
|
|
35
|
+
* datasets: ["my-app-traces", "my-app-logs"],
|
|
36
|
+
* aplQuery: `
|
|
37
|
+
* ['my-app-traces']
|
|
38
|
+
* | where duration_ms > 1000
|
|
39
|
+
* | join kind=leftouter (['my-app-logs']) on trace_id
|
|
40
|
+
* `,
|
|
41
|
+
* });
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @resource
|
|
45
|
+
* @product Dashboard
|
|
46
|
+
*/
|
|
47
|
+
export const View = Resource("Axiom.View");
|
|
48
|
+
export const ViewProvider = () => Provider.effect(View, Effect.gen(function* () {
|
|
49
|
+
const create = yield* Axiom.createView;
|
|
50
|
+
const update = yield* Axiom.updateView;
|
|
51
|
+
const get = yield* Axiom.getView;
|
|
52
|
+
const listViews = yield* Axiom.getViews;
|
|
53
|
+
const del = yield* Axiom.deleteView;
|
|
54
|
+
return {
|
|
55
|
+
stables: ["id"],
|
|
56
|
+
// Enumerate every view in the org. Axiom exposes a single account-wide
|
|
57
|
+
// `GET /v2/views` collection op (no pagination) that already returns
|
|
58
|
+
// the full view objects, so we fetch it once and hydrate each row into
|
|
59
|
+
// the exact `read` Attributes shape (`id` is derived from `name`, the
|
|
60
|
+
// path identifier) — directly usable by `delete` with no follow-up get.
|
|
61
|
+
list: () => Effect.gen(function* () {
|
|
62
|
+
const views = yield* listViews({});
|
|
63
|
+
return views.map((view) => ({ ...view, id: view.name }));
|
|
64
|
+
}),
|
|
65
|
+
diff: Effect.fn(function* ({ news, output }) {
|
|
66
|
+
if (!isResolved(news))
|
|
67
|
+
return undefined;
|
|
68
|
+
if (output && news.name !== output.name) {
|
|
69
|
+
return { action: "replace" };
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
}),
|
|
73
|
+
reconcile: Effect.fn(function* ({ news, output }) {
|
|
74
|
+
// Observe — `name` is the path identifier for views. Renames are
|
|
75
|
+
// forced to a replacement by `diff` above, so the cached
|
|
76
|
+
// `output.id` (set to `news.name` on first create) and the
|
|
77
|
+
// current `news.name` always agree by the time we land here.
|
|
78
|
+
const viewId = output?.id ?? news.name;
|
|
79
|
+
const observed = yield* get({ id: viewId }).pipe(Effect.catchTag("NotFound", () => Effect.succeed(undefined)));
|
|
80
|
+
// Ensure — POST creates the view under `news.name`.
|
|
81
|
+
if (observed === undefined) {
|
|
82
|
+
const result = yield* create(news);
|
|
83
|
+
return { ...result, id: news.name };
|
|
84
|
+
}
|
|
85
|
+
// Sync — the view exists; PUT against its id with the desired
|
|
86
|
+
// props.
|
|
87
|
+
const result = yield* update({ ...news, id: viewId });
|
|
88
|
+
return { ...result, id: viewId };
|
|
89
|
+
}),
|
|
90
|
+
delete: Effect.fn(function* ({ output }) {
|
|
91
|
+
yield* del({ id: output.id }).pipe(Effect.catchTag("NotFound", () => Effect.void));
|
|
92
|
+
}),
|
|
93
|
+
read: Effect.fn(function* ({ output }) {
|
|
94
|
+
if (!output?.id)
|
|
95
|
+
return undefined;
|
|
96
|
+
return yield* get({ id: output.id }).pipe(Effect.map((current) => ({ ...current, id: output.id })), Effect.catchTag("NotFound", () => Effect.succeed(undefined)));
|
|
97
|
+
}),
|
|
98
|
+
};
|
|
99
|
+
}));
|
|
100
|
+
//# sourceMappingURL=View.js.map
|
package/lib/View.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"View.js","sourceRoot":"","sources":["../src/View.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as Axiom from "@distilled.cloud/axiom";
|
|
2
|
+
import * as Provider from "alchemy/Provider";
|
|
3
|
+
import { Resource } from "alchemy/Resource";
|
|
4
|
+
import type { Providers } from "./Providers.ts";
|
|
5
|
+
export type VirtualFieldProps = Axiom.CreateVirtualFieldRequest;
|
|
6
|
+
export type VirtualField = Resource<"Axiom.VirtualField", VirtualFieldProps, Axiom.CreateVirtualFieldResponse, never, Providers>;
|
|
7
|
+
/**
|
|
8
|
+
* An Axiom virtual field — a saved APL expression that appears as a derived
|
|
9
|
+
* column on a dataset at query time. Use these to standardise common
|
|
10
|
+
* computations (status classes, latency buckets, parsed JSON paths) so
|
|
11
|
+
* dashboards and monitors don't have to redefine them.
|
|
12
|
+
*
|
|
13
|
+
* Bound to a single `dataset`; changing the dataset triggers a replacement.
|
|
14
|
+
* @see https://axiom.co/docs/query-data/virtual-fields
|
|
15
|
+
*
|
|
16
|
+
* ### Creating a Virtual Field
|
|
17
|
+
* **Example:** HTTP status class (e.g. 200 → "2xx")
|
|
18
|
+
* ```typescript
|
|
19
|
+
* yield* Axiom.VirtualField("status-class", {
|
|
20
|
+
* dataset: "my-app-traces",
|
|
21
|
+
* name: "status_class",
|
|
22
|
+
* description: "HTTP response class bucket",
|
|
23
|
+
* expression: 'strcat(tostring(toint(status / 100)), "xx")',
|
|
24
|
+
* type: "string",
|
|
25
|
+
* });
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* **Example:** Latency bucket in seconds
|
|
29
|
+
* ```typescript
|
|
30
|
+
* yield* Axiom.VirtualField("latency-bucket", {
|
|
31
|
+
* dataset: "my-app-traces",
|
|
32
|
+
* name: "latency_bucket_s",
|
|
33
|
+
* expression: "bin(duration_ms / 1000.0, 0.5)",
|
|
34
|
+
* type: "number",
|
|
35
|
+
* unit: "s",
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @resource
|
|
40
|
+
* @product Dataset
|
|
41
|
+
*/
|
|
42
|
+
export declare const VirtualField: import("alchemy/Resource").ResourceClass<VirtualField>;
|
|
43
|
+
export declare const VirtualFieldProvider: () => import("effect/Layer").Layer<Provider.Provider<VirtualField>, never, Axiom.AxiomOpContext>;
|
|
44
|
+
//# sourceMappingURL=VirtualField.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VirtualField.d.ts","sourceRoot":"","sources":["../src/VirtualField.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,wBAAwB,CAAC;AAEhD,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,MAAM,iBAAiB,GAAG,KAAK,CAAC,yBAAyB,CAAC;AAEhE,MAAM,MAAM,YAAY,GAAG,QAAQ,CACjC,oBAAoB,EACpB,iBAAiB,EACjB,KAAK,CAAC,0BAA0B,EAChC,KAAK,EACL,SAAS,CACV,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,YAAY,wDAA+C,CAAC;AAEzE,eAAO,MAAM,oBAAoB,kGAoE9B,CAAC"}
|