@uploadista/observability 0.0.3
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/.turbo/turbo-build.log +5 -0
- package/.turbo/turbo-check.log +0 -0
- package/LICENSE +21 -0
- package/dist/core/errors.d.ts +8 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +108 -0
- package/dist/core/index.d.ts +8 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +8 -0
- package/dist/core/layers.d.ts +104 -0
- package/dist/core/layers.d.ts.map +1 -0
- package/dist/core/layers.js +110 -0
- package/dist/core/logging.d.ts +18 -0
- package/dist/core/logging.d.ts.map +1 -0
- package/dist/core/logging.js +41 -0
- package/dist/core/metrics.d.ts +37 -0
- package/dist/core/metrics.d.ts.map +1 -0
- package/dist/core/metrics.js +72 -0
- package/dist/core/testing.d.ts +43 -0
- package/dist/core/testing.d.ts.map +1 -0
- package/dist/core/testing.js +93 -0
- package/dist/core/tracing.d.ts +19 -0
- package/dist/core/tracing.d.ts.map +1 -0
- package/dist/core/tracing.js +43 -0
- package/dist/core/utilities.d.ts +11 -0
- package/dist/core/utilities.d.ts.map +1 -0
- package/dist/core/utilities.js +41 -0
- package/dist/flow/errors.d.ts +15 -0
- package/dist/flow/errors.d.ts.map +1 -0
- package/dist/flow/errors.js +66 -0
- package/dist/flow/index.d.ts +6 -0
- package/dist/flow/index.d.ts.map +1 -0
- package/dist/flow/index.js +6 -0
- package/dist/flow/layers.d.ts +40 -0
- package/dist/flow/layers.d.ts.map +1 -0
- package/dist/flow/layers.js +94 -0
- package/dist/flow/metrics.d.ts +52 -0
- package/dist/flow/metrics.d.ts.map +1 -0
- package/dist/flow/metrics.js +89 -0
- package/dist/flow/testing.d.ts +11 -0
- package/dist/flow/testing.d.ts.map +1 -0
- package/dist/flow/testing.js +27 -0
- package/dist/flow/tracing.d.ts +35 -0
- package/dist/flow/tracing.d.ts.map +1 -0
- package/dist/flow/tracing.js +42 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/service/metrics.d.ts +23 -0
- package/dist/service/metrics.d.ts.map +1 -0
- package/dist/service/metrics.js +17 -0
- package/dist/storage/azure.d.ts +47 -0
- package/dist/storage/azure.d.ts.map +1 -0
- package/dist/storage/azure.js +89 -0
- package/dist/storage/filesystem.d.ts +47 -0
- package/dist/storage/filesystem.d.ts.map +1 -0
- package/dist/storage/filesystem.js +70 -0
- package/dist/storage/gcs.d.ts +47 -0
- package/dist/storage/gcs.d.ts.map +1 -0
- package/dist/storage/gcs.js +90 -0
- package/dist/storage/index.d.ts +5 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +5 -0
- package/dist/storage/s3.d.ts +47 -0
- package/dist/storage/s3.d.ts.map +1 -0
- package/dist/storage/s3.js +67 -0
- package/dist/test-observability.d.ts +12 -0
- package/dist/test-observability.d.ts.map +1 -0
- package/dist/test-observability.js +153 -0
- package/dist/upload/errors.d.ts +16 -0
- package/dist/upload/errors.d.ts.map +1 -0
- package/dist/upload/errors.js +107 -0
- package/dist/upload/index.d.ts +6 -0
- package/dist/upload/index.d.ts.map +1 -0
- package/dist/upload/index.js +6 -0
- package/dist/upload/layers.d.ts +32 -0
- package/dist/upload/layers.d.ts.map +1 -0
- package/dist/upload/layers.js +63 -0
- package/dist/upload/metrics.d.ts +46 -0
- package/dist/upload/metrics.d.ts.map +1 -0
- package/dist/upload/metrics.js +80 -0
- package/dist/upload/testing.d.ts +32 -0
- package/dist/upload/testing.d.ts.map +1 -0
- package/dist/upload/testing.js +52 -0
- package/dist/upload/tracing.d.ts +25 -0
- package/dist/upload/tracing.d.ts.map +1 -0
- package/dist/upload/tracing.js +35 -0
- package/package.json +37 -0
- package/src/core/errors.ts +187 -0
- package/src/core/index.ts +9 -0
- package/src/core/layers.ts +205 -0
- package/src/core/logging.ts +81 -0
- package/src/core/metrics.ts +108 -0
- package/src/core/testing.ts +142 -0
- package/src/core/tracing.ts +67 -0
- package/src/core/utilities.ts +133 -0
- package/src/flow/errors.ts +95 -0
- package/src/flow/index.ts +17 -0
- package/src/flow/layers.ts +131 -0
- package/src/flow/metrics.ts +130 -0
- package/src/flow/testing.ts +33 -0
- package/src/flow/tracing.ts +72 -0
- package/src/index.ts +31 -0
- package/src/service/metrics.ts +31 -0
- package/src/storage/azure.ts +163 -0
- package/src/storage/filesystem.ts +153 -0
- package/src/storage/gcs.ts +161 -0
- package/src/storage/index.ts +5 -0
- package/src/storage/s3.ts +136 -0
- package/src/test-observability.ts +234 -0
- package/src/upload/errors.ts +166 -0
- package/src/upload/index.ts +12 -0
- package/src/upload/layers.ts +88 -0
- package/src/upload/metrics.ts +118 -0
- package/src/upload/testing.ts +60 -0
- package/src/upload/tracing.ts +58 -0
- package/tsconfig.json +10 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Effect, Layer, Metric } from "effect";
|
|
2
|
+
import { FlowObservability, StorageObservability, UploadObservability, } from "./layers.js";
|
|
3
|
+
import { createStorageMetrics } from "./metrics.js";
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// Test Observability Layers
|
|
6
|
+
// ============================================================================
|
|
7
|
+
/**
|
|
8
|
+
* Mock storage observability for testing
|
|
9
|
+
*/
|
|
10
|
+
export const makeTestStorageObservability = (storageType) => {
|
|
11
|
+
const metrics = createStorageMetrics(storageType);
|
|
12
|
+
const service = {
|
|
13
|
+
serviceName: `test-${storageType}-store`,
|
|
14
|
+
storageType,
|
|
15
|
+
metrics,
|
|
16
|
+
enabled: true,
|
|
17
|
+
};
|
|
18
|
+
return Layer.succeed(StorageObservability, service);
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Mock upload observability for testing
|
|
22
|
+
*/
|
|
23
|
+
export const makeTestUploadObservability = () => {
|
|
24
|
+
const service = {
|
|
25
|
+
serviceName: "test-upload-server",
|
|
26
|
+
enabled: true,
|
|
27
|
+
metrics: {
|
|
28
|
+
uploadCreated: Effect.void,
|
|
29
|
+
uploadCompleted: Effect.void,
|
|
30
|
+
uploadFailed: Effect.void,
|
|
31
|
+
chunkUploaded: Effect.void,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
return Layer.succeed(UploadObservability, service);
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Mock flow observability for testing
|
|
38
|
+
*/
|
|
39
|
+
export const makeTestFlowObservability = () => {
|
|
40
|
+
const service = {
|
|
41
|
+
serviceName: "test-flow-engine",
|
|
42
|
+
enabled: true,
|
|
43
|
+
metrics: {
|
|
44
|
+
flowStarted: Effect.void,
|
|
45
|
+
flowCompleted: Effect.void,
|
|
46
|
+
flowFailed: Effect.void,
|
|
47
|
+
nodeExecuted: Effect.void,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
return Layer.succeed(FlowObservability, service);
|
|
51
|
+
};
|
|
52
|
+
// ============================================================================
|
|
53
|
+
// Test Utilities
|
|
54
|
+
// ============================================================================
|
|
55
|
+
/**
|
|
56
|
+
* Capture metrics snapshot from an effect for testing
|
|
57
|
+
* Note: Metric snapshots are simplified - for full metric testing,
|
|
58
|
+
* use Effect's built-in metric testing utilities
|
|
59
|
+
*/
|
|
60
|
+
export const captureMetrics = (effect) => Effect.gen(function* () {
|
|
61
|
+
const result = yield* effect;
|
|
62
|
+
// Metrics are automatically captured by Effect runtime
|
|
63
|
+
yield* Metric.snapshot;
|
|
64
|
+
return result;
|
|
65
|
+
});
|
|
66
|
+
/**
|
|
67
|
+
* Test helper to capture metrics around effect execution
|
|
68
|
+
* This is a simplified version - for production testing,
|
|
69
|
+
* use Effect's metric testing utilities
|
|
70
|
+
*/
|
|
71
|
+
export const withMetricTracking = (effect) => Effect.gen(function* () {
|
|
72
|
+
// Track metric before execution
|
|
73
|
+
yield* Metric.snapshot;
|
|
74
|
+
const result = yield* effect;
|
|
75
|
+
// Track metric after execution
|
|
76
|
+
yield* Metric.snapshot;
|
|
77
|
+
return result;
|
|
78
|
+
});
|
|
79
|
+
/**
|
|
80
|
+
* Create a complete test fixture with all observability layers
|
|
81
|
+
*/
|
|
82
|
+
export const createTestFixture = (storageType = "test-storage") => ({
|
|
83
|
+
storageObservability: makeTestStorageObservability(storageType),
|
|
84
|
+
uploadObservability: makeTestUploadObservability(),
|
|
85
|
+
flowObservability: makeTestFlowObservability(),
|
|
86
|
+
});
|
|
87
|
+
/**
|
|
88
|
+
* Run an effect with test observability layers
|
|
89
|
+
*/
|
|
90
|
+
export const runWithTestObservability = (effect, storageType = "test-storage") => {
|
|
91
|
+
const fixture = createTestFixture(storageType);
|
|
92
|
+
return effect.pipe(Effect.provide(fixture.storageObservability), Effect.provide(fixture.uploadObservability), Effect.provide(fixture.flowObservability));
|
|
93
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
export declare const TracingService: Context.Tag<{
|
|
3
|
+
serviceName: string;
|
|
4
|
+
}, {
|
|
5
|
+
serviceName: string;
|
|
6
|
+
}>;
|
|
7
|
+
export declare const createTracingLayer: (options?: {
|
|
8
|
+
serviceName?: string;
|
|
9
|
+
}) => Layer.Layer<{
|
|
10
|
+
serviceName: string;
|
|
11
|
+
}, never, never>;
|
|
12
|
+
export declare const createStorageTracingLayer: (storageType: string) => Layer.Layer<{
|
|
13
|
+
serviceName: string;
|
|
14
|
+
}, never, never>;
|
|
15
|
+
export declare const withStorageSpan: <A, E, R>(operation: string, storageType: string, attributes?: Record<string, unknown>) => (effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, import("effect/Tracer").ParentSpan>>;
|
|
16
|
+
export declare const WebSdkLive: Layer.Layer<import("@effect/opentelemetry/Resource").Resource, never, never>;
|
|
17
|
+
export declare const NodeSdkLive: Layer.Layer<import("@effect/opentelemetry/Resource").Resource, never, never>;
|
|
18
|
+
export declare const WorkersSdkLive: Layer.Layer<import("@effect/opentelemetry/Resource").Resource, never, never>;
|
|
19
|
+
//# sourceMappingURL=tracing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../../src/core/tracing.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAOhD,eAAO,MAAM,cAAc;iBAAqC,MAAM;;iBAAN,MAAM;EAErE,CAAC;AAGF,eAAO,MAAM,kBAAkB,GAAI,UAAU;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE;iBALL,MAAM;gBAUrE,CAAC;AAGF,eAAO,MAAM,yBAAyB,GAAI,aAAa,MAAM;iBAbG,MAAM;gBAgBlE,CAAC;AAGL,eAAO,MAAM,eAAe,GACzB,CAAC,EAAE,CAAC,EAAE,CAAC,EACN,WAAW,MAAM,EACjB,aAAa,MAAM,EACnB,aAAa,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,MAErC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,wEAS5B,CAAC;AAGN,eAAO,MAAM,UAAU,8EAIpB,CAAC;AAEJ,eAAO,MAAM,WAAW,8EAIrB,CAAC;AAGJ,eAAO,MAAM,cAAc,8EAIxB,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { NodeSdk, WebSdk } from "@effect/opentelemetry";
|
|
2
|
+
import { BatchSpanProcessor, ConsoleSpanExporter, } from "@opentelemetry/sdk-trace-base";
|
|
3
|
+
import { Context, Effect, Layer } from "effect";
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// Universal Tracing (Environment-agnostic)
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Generic service tag for tracing context
|
|
8
|
+
export const TracingService = Context.GenericTag("TracingService");
|
|
9
|
+
// Create a tracing layer using Effect's native tracing (works in all environments)
|
|
10
|
+
export const createTracingLayer = (options) => {
|
|
11
|
+
const serviceName = options?.serviceName ?? "uploadista-storage";
|
|
12
|
+
// Return a layer that provides tracing service context
|
|
13
|
+
return Layer.succeed(TracingService, { serviceName });
|
|
14
|
+
};
|
|
15
|
+
// Storage-specific tracing layers
|
|
16
|
+
export const createStorageTracingLayer = (storageType) => createTracingLayer({
|
|
17
|
+
serviceName: `uploadista-${storageType}-store`,
|
|
18
|
+
});
|
|
19
|
+
// Utility to add storage context to spans
|
|
20
|
+
export const withStorageSpan = (operation, storageType, attributes) => (effect) => effect.pipe(Effect.withSpan(`${storageType}-${operation}`, {
|
|
21
|
+
attributes: {
|
|
22
|
+
"storage.type": storageType,
|
|
23
|
+
operation: operation,
|
|
24
|
+
...attributes,
|
|
25
|
+
},
|
|
26
|
+
}));
|
|
27
|
+
// Set up tracing with the OpenTelemetry SDK
|
|
28
|
+
export const WebSdkLive = WebSdk.layer(() => ({
|
|
29
|
+
resource: { serviceName: "uploadista-storage" },
|
|
30
|
+
// Export span data to the console
|
|
31
|
+
spanProcessor: new BatchSpanProcessor(new ConsoleSpanExporter()),
|
|
32
|
+
}));
|
|
33
|
+
export const NodeSdkLive = NodeSdk.layer(() => ({
|
|
34
|
+
resource: { serviceName: "uploadista-storage" },
|
|
35
|
+
// Export span data to the console
|
|
36
|
+
spanProcessor: new BatchSpanProcessor(new ConsoleSpanExporter()),
|
|
37
|
+
}));
|
|
38
|
+
// Cloudflare Workers SDK (uses WebSdk as base)
|
|
39
|
+
export const WorkersSdkLive = WebSdk.layer(() => ({
|
|
40
|
+
resource: { serviceName: "uploadista-storage-workers" },
|
|
41
|
+
// Export span data to the console in Workers environment
|
|
42
|
+
spanProcessor: new BatchSpanProcessor(new ConsoleSpanExporter()),
|
|
43
|
+
}));
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Effect, Metric } from "effect";
|
|
2
|
+
import type { StorageMetrics } from "./metrics.js";
|
|
3
|
+
export declare const withUploadMetrics: <A, E, R>(metrics: StorageMetrics, uploadId: string, effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
4
|
+
export declare const withApiMetrics: <A, E, R>(metrics: StorageMetrics, operation: string, effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
5
|
+
export declare const withTimingMetrics: <A, E, R>(metric: Metric.Metric.Histogram<number>, effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
6
|
+
export declare const trackFileSize: <A, E, R>(metrics: StorageMetrics, fileSize: number, effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
7
|
+
export declare const trackPartSize: <A, E, R>(metrics: StorageMetrics, partSize: number, effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
8
|
+
export declare const withActiveUploadTracking: <A, E, R>(metrics: StorageMetrics, effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
9
|
+
export declare const withThroughputTracking: <A, E, R>(metrics: StorageMetrics, bytes: number, effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
10
|
+
export declare const withStorageOperationMetrics: <A, E, R>(metrics: StorageMetrics, operation: string, uploadId: string, effect: Effect.Effect<A, E, R>, fileSize?: number) => Effect.Effect<A, E, R>;
|
|
11
|
+
//# sourceMappingURL=utilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../../src/core/utilities.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAOnD,eAAO,MAAM,iBAAiB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EACvC,SAAS,cAAc,EACvB,UAAU,MAAM,EAChB,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAiBrB,CAAC;AAGJ,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EACpC,SAAS,cAAc,EACvB,WAAW,MAAM,EACjB,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAOrB,CAAC;AAGJ,eAAO,MAAM,iBAAiB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EACvC,QAAQ,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,EACvC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAUpB,CAAC;AAGL,eAAO,MAAM,aAAa,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EACnC,SAAS,cAAc,EACvB,UAAU,MAAM,EAChB,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAGrB,CAAC;AAGJ,eAAO,MAAM,aAAa,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EACnC,SAAS,cAAc,EACvB,UAAU,MAAM,EAChB,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAGrB,CAAC;AAGJ,eAAO,MAAM,wBAAwB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAC9C,SAAS,cAAc,EACvB,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAIrB,CAAC;AAGJ,eAAO,MAAM,sBAAsB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAC5C,SAAS,cAAc,EACvB,OAAO,MAAM,EACb,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAWpB,CAAC;AAGL,eAAO,MAAM,2BAA2B,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EACjD,SAAS,cAAc,EACvB,WAAW,MAAM,EACjB,UAAU,MAAM,EAChB,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC9B,WAAW,MAAM,KAChB,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAgBvB,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Effect, Metric } from "effect";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// Storage Observability Utility Functions
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// Generic upload metrics wrapper
|
|
6
|
+
export const withUploadMetrics = (metrics, uploadId, effect) => effect.pipe(Effect.tap(() => metrics.uploadRequestsTotal.pipe(Metric.tagged("upload_id", uploadId))(Effect.succeed(1))), Effect.tapError(() => metrics.uploadErrorsTotal.pipe(Metric.tagged("upload_id", uploadId))(Effect.succeed(1))), Effect.tap(() => metrics.uploadSuccessTotal.pipe(Metric.tagged("upload_id", uploadId))(Effect.succeed(1))));
|
|
7
|
+
// Generic API call metrics wrapper
|
|
8
|
+
export const withApiMetrics = (metrics, operation, effect) => effect.pipe(Effect.tap(() => metrics.apiCallsTotal.pipe(Metric.tagged("operation", operation))(Effect.succeed(1))));
|
|
9
|
+
// Generic timing metrics wrapper
|
|
10
|
+
export const withTimingMetrics = (metric, effect) => Effect.gen(function* () {
|
|
11
|
+
const startTime = yield* Effect.sync(() => Date.now());
|
|
12
|
+
const result = yield* effect;
|
|
13
|
+
const endTime = yield* Effect.sync(() => Date.now());
|
|
14
|
+
const duration = (endTime - startTime) / 1000; // Convert to seconds
|
|
15
|
+
yield* metric(Effect.succeed(duration));
|
|
16
|
+
return result;
|
|
17
|
+
});
|
|
18
|
+
// File size tracking
|
|
19
|
+
export const trackFileSize = (metrics, fileSize, effect) => effect.pipe(Effect.tap(() => metrics.fileSizeHistogram(Effect.succeed(fileSize))));
|
|
20
|
+
// Part size tracking
|
|
21
|
+
export const trackPartSize = (metrics, partSize, effect) => effect.pipe(Effect.tap(() => metrics.partSizeHistogram(Effect.succeed(partSize))));
|
|
22
|
+
// Active uploads tracking
|
|
23
|
+
export const withActiveUploadTracking = (metrics, effect) => effect.pipe(Effect.tap(() => metrics.activeUploadsGauge(Effect.succeed(1))), Effect.ensuring(metrics.activeUploadsGauge(Effect.succeed(-1))));
|
|
24
|
+
// Throughput calculation and tracking
|
|
25
|
+
export const withThroughputTracking = (metrics, bytes, effect) => Effect.gen(function* () {
|
|
26
|
+
const startTime = yield* Effect.sync(() => Date.now());
|
|
27
|
+
const result = yield* effect;
|
|
28
|
+
const endTime = yield* Effect.sync(() => Date.now());
|
|
29
|
+
const durationSeconds = (endTime - startTime) / 1000;
|
|
30
|
+
const throughputBps = durationSeconds > 0 ? bytes / durationSeconds : 0;
|
|
31
|
+
yield* metrics.uploadThroughputGauge(Effect.succeed(throughputBps));
|
|
32
|
+
return result;
|
|
33
|
+
});
|
|
34
|
+
// Combined metrics wrapper for common upload operations
|
|
35
|
+
export const withStorageOperationMetrics = (metrics, operation, uploadId, effect, fileSize) => {
|
|
36
|
+
let wrappedEffect = effect.pipe((eff) => withApiMetrics(metrics, operation, eff), (eff) => withUploadMetrics(metrics, uploadId, eff), (eff) => withTimingMetrics(metrics.uploadDurationHistogram, eff), (eff) => withActiveUploadTracking(metrics, eff));
|
|
37
|
+
if (fileSize !== undefined) {
|
|
38
|
+
wrappedEffect = wrappedEffect.pipe((eff) => trackFileSize(metrics, fileSize, eff), (eff) => withThroughputTracking(metrics, fileSize, eff));
|
|
39
|
+
}
|
|
40
|
+
return wrappedEffect;
|
|
41
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
export type FlowErrorCategory = "flow_validation_error" | "node_execution_error" | "node_not_found_error" | "flow_timeout_error" | "flow_cancelled_error" | "unknown_flow_error";
|
|
3
|
+
/**
|
|
4
|
+
* Classify flow execution errors
|
|
5
|
+
*/
|
|
6
|
+
export declare const classifyFlowError: (error: unknown) => FlowErrorCategory;
|
|
7
|
+
/**
|
|
8
|
+
* Track flow errors with classification
|
|
9
|
+
*/
|
|
10
|
+
export declare const trackFlowError: <E>(error: E) => Effect.Effect<void, never, never>;
|
|
11
|
+
/**
|
|
12
|
+
* Track node errors with classification
|
|
13
|
+
*/
|
|
14
|
+
export declare const trackNodeError: <E>(nodeId: string, nodeType: string, error: E) => Effect.Effect<void, never, never>;
|
|
15
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/flow/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AAOxC,MAAM,MAAM,iBAAiB,GACzB,uBAAuB,GACvB,sBAAsB,GACtB,sBAAsB,GACtB,oBAAoB,GACpB,sBAAsB,GACtB,oBAAoB,CAAC;AAEzB;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,iBA0BlD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,EAC9B,OAAO,CAAC,KACP,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAgBlC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,EAC9B,QAAQ,MAAM,EACd,UAAU,MAAM,EAChB,OAAO,CAAC,KACP,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAkBlC,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Effect, Metric } from "effect";
|
|
2
|
+
import { createFlowMetrics } from "./metrics.js";
|
|
3
|
+
/**
|
|
4
|
+
* Classify flow execution errors
|
|
5
|
+
*/
|
|
6
|
+
export const classifyFlowError = (error) => {
|
|
7
|
+
if (!error || typeof error !== "object")
|
|
8
|
+
return "unknown_flow_error";
|
|
9
|
+
const errorCode = "code" in error ? error.code : undefined;
|
|
10
|
+
if (!errorCode)
|
|
11
|
+
return "unknown_flow_error";
|
|
12
|
+
// Flow-specific error codes
|
|
13
|
+
switch (errorCode) {
|
|
14
|
+
case "FLOW_VALIDATION_ERROR":
|
|
15
|
+
case "FLOW_INVALID_INPUT":
|
|
16
|
+
case "FLOW_INVALID_OUTPUT":
|
|
17
|
+
return "flow_validation_error";
|
|
18
|
+
case "FLOW_NODE_NOT_FOUND":
|
|
19
|
+
case "FLOW_EDGE_INVALID":
|
|
20
|
+
return "node_not_found_error";
|
|
21
|
+
case "FLOW_NODE_EXECUTION_FAILED":
|
|
22
|
+
case "FLOW_NODE_ERROR":
|
|
23
|
+
return "node_execution_error";
|
|
24
|
+
case "FLOW_TIMEOUT":
|
|
25
|
+
return "flow_timeout_error";
|
|
26
|
+
case "FLOW_CANCELLED":
|
|
27
|
+
case "ABORTED":
|
|
28
|
+
return "flow_cancelled_error";
|
|
29
|
+
default:
|
|
30
|
+
return "unknown_flow_error";
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Track flow errors with classification
|
|
35
|
+
*/
|
|
36
|
+
export const trackFlowError = (error) => {
|
|
37
|
+
const metrics = createFlowMetrics();
|
|
38
|
+
const category = classifyFlowError(error);
|
|
39
|
+
return Effect.gen(function* () {
|
|
40
|
+
// Increment total failed flows
|
|
41
|
+
yield* Metric.increment(metrics.flowFailedTotal);
|
|
42
|
+
// Log error with classification
|
|
43
|
+
yield* Effect.logError("Flow execution failed").pipe(Effect.annotateLogs({
|
|
44
|
+
"error.category": category,
|
|
45
|
+
"error.message": String(error),
|
|
46
|
+
}));
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Track node errors with classification
|
|
51
|
+
*/
|
|
52
|
+
export const trackNodeError = (nodeId, nodeType, error) => {
|
|
53
|
+
const metrics = createFlowMetrics();
|
|
54
|
+
const category = classifyFlowError(error);
|
|
55
|
+
return Effect.gen(function* () {
|
|
56
|
+
// Increment node failed counter
|
|
57
|
+
yield* Metric.increment(metrics.nodeFailedTotal);
|
|
58
|
+
// Log error with node context
|
|
59
|
+
yield* Effect.logError("Node execution failed").pipe(Effect.annotateLogs({
|
|
60
|
+
"node.id": nodeId,
|
|
61
|
+
"node.type": nodeType,
|
|
62
|
+
"error.category": category,
|
|
63
|
+
"error.message": String(error),
|
|
64
|
+
}));
|
|
65
|
+
});
|
|
66
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from "./metrics.js";
|
|
2
|
+
export * from "./tracing.js";
|
|
3
|
+
export { makeFlowObservabilityLive, FlowObservabilityLive, getFlowMetrics, withFlowDuration, withNodeDuration, trackActiveFlow, trackActiveNode, } from "./layers.js";
|
|
4
|
+
export * from "./errors.js";
|
|
5
|
+
export { makeTestFlowObservability as makeTestFlowObservabilityUtil, runWithTestFlowObservability, } from "./testing.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/flow/index.ts"],"names":[],"mappings":"AACA,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,cAAc,aAAa,CAAC;AAC5B,OAAO,EACL,yBAAyB,IAAI,6BAA6B,EAC1D,4BAA4B,GAC7B,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Flow observability exports
|
|
2
|
+
export * from "./metrics.js";
|
|
3
|
+
export * from "./tracing.js";
|
|
4
|
+
export { makeFlowObservabilityLive, FlowObservabilityLive, getFlowMetrics, withFlowDuration, withNodeDuration, trackActiveFlow, trackActiveNode, } from "./layers.js";
|
|
5
|
+
export * from "./errors.js";
|
|
6
|
+
export { makeTestFlowObservability as makeTestFlowObservabilityUtil, runWithTestFlowObservability, } from "./testing.js";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Effect, Layer } from "effect";
|
|
2
|
+
import { FlowObservability } from "../core/layers.js";
|
|
3
|
+
/**
|
|
4
|
+
* Create a live flow observability layer with full metrics
|
|
5
|
+
*/
|
|
6
|
+
export declare const makeFlowObservabilityLive: (serviceName?: string) => Layer.Layer<FlowObservability>;
|
|
7
|
+
/**
|
|
8
|
+
* Default live flow observability layer
|
|
9
|
+
*/
|
|
10
|
+
export declare const FlowObservabilityLive: Layer.Layer<FlowObservability, never, never>;
|
|
11
|
+
/**
|
|
12
|
+
* No-op flow observability layer (for testing or disabled observability)
|
|
13
|
+
*/
|
|
14
|
+
export declare const FlowObservabilityDisabled: Layer.Layer<FlowObservability, never, never>;
|
|
15
|
+
/**
|
|
16
|
+
* Helper to get flow metrics from context
|
|
17
|
+
*/
|
|
18
|
+
export declare const getFlowMetrics: Effect.Effect<{
|
|
19
|
+
flowStarted: Effect.Effect<void>;
|
|
20
|
+
flowCompleted: Effect.Effect<void>;
|
|
21
|
+
flowFailed: Effect.Effect<void>;
|
|
22
|
+
nodeExecuted: Effect.Effect<void>;
|
|
23
|
+
}, never, FlowObservability>;
|
|
24
|
+
/**
|
|
25
|
+
* Helper to track flow duration
|
|
26
|
+
*/
|
|
27
|
+
export declare const withFlowDuration: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
28
|
+
/**
|
|
29
|
+
* Helper to track node duration
|
|
30
|
+
*/
|
|
31
|
+
export declare const withNodeDuration: <A, E, R>(nodeId: string, nodeType: string, effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
32
|
+
/**
|
|
33
|
+
* Helper to track active flows
|
|
34
|
+
*/
|
|
35
|
+
export declare const trackActiveFlow: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
36
|
+
/**
|
|
37
|
+
* Helper to track active nodes
|
|
38
|
+
*/
|
|
39
|
+
export declare const trackActiveNode: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
40
|
+
//# sourceMappingURL=layers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layers.d.ts","sourceRoot":"","sources":["../../src/flow/layers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAU,MAAM,QAAQ,CAAC;AAC/C,OAAO,EACL,iBAAiB,EAElB,MAAM,mBAAmB,CAAC;AAO3B;;GAEG;AACH,eAAO,MAAM,yBAAyB,GACpC,oBAAsC,KACrC,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAa/B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,8CAA8B,CAAC;AAEjE;;GAEG;AACH,eAAO,MAAM,yBAAyB,8CAAoC,CAAC;AAE3E;;GAEG;AACH,eAAO,MAAM,cAAc;;;;;4BAGzB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EACtC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAUvB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EACtC,QAAQ,MAAM,EACd,UAAU,MAAM,EAChB,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAiBvB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EACrC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAavB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EACrC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAavB,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Effect, Layer, Metric } from "effect";
|
|
2
|
+
import { FlowObservability, makeFlowObservabilityLayer, } from "../core/layers.js";
|
|
3
|
+
import { createFlowMetrics } from "./metrics.js";
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// Flow Observability Layer Implementation
|
|
6
|
+
// ============================================================================
|
|
7
|
+
/**
|
|
8
|
+
* Create a live flow observability layer with full metrics
|
|
9
|
+
*/
|
|
10
|
+
export const makeFlowObservabilityLive = (serviceName = "uploadista-flow-engine") => {
|
|
11
|
+
const metrics = createFlowMetrics();
|
|
12
|
+
return Layer.succeed(FlowObservability, {
|
|
13
|
+
serviceName,
|
|
14
|
+
enabled: true,
|
|
15
|
+
metrics: {
|
|
16
|
+
flowStarted: Metric.increment(metrics.flowStartedTotal),
|
|
17
|
+
flowCompleted: Metric.increment(metrics.flowCompletedTotal),
|
|
18
|
+
flowFailed: Metric.increment(metrics.flowFailedTotal),
|
|
19
|
+
nodeExecuted: Metric.increment(metrics.nodeExecutedTotal),
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Default live flow observability layer
|
|
25
|
+
*/
|
|
26
|
+
export const FlowObservabilityLive = makeFlowObservabilityLive();
|
|
27
|
+
/**
|
|
28
|
+
* No-op flow observability layer (for testing or disabled observability)
|
|
29
|
+
*/
|
|
30
|
+
export const FlowObservabilityDisabled = makeFlowObservabilityLayer(false);
|
|
31
|
+
/**
|
|
32
|
+
* Helper to get flow metrics from context
|
|
33
|
+
*/
|
|
34
|
+
export const getFlowMetrics = Effect.gen(function* () {
|
|
35
|
+
const obs = yield* FlowObservability;
|
|
36
|
+
return obs.metrics;
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* Helper to track flow duration
|
|
40
|
+
*/
|
|
41
|
+
export const withFlowDuration = (effect) => {
|
|
42
|
+
const metrics = createFlowMetrics();
|
|
43
|
+
return Effect.gen(function* () {
|
|
44
|
+
const startTime = Date.now();
|
|
45
|
+
const result = yield* effect;
|
|
46
|
+
const duration = (Date.now() - startTime) / 1000; // Convert to seconds
|
|
47
|
+
yield* Metric.update(metrics.flowDurationHistogram, duration);
|
|
48
|
+
yield* Metric.update(metrics.flowLatencySummary, duration);
|
|
49
|
+
return result;
|
|
50
|
+
}).pipe(Effect.withSpan("flow-execution"));
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Helper to track node duration
|
|
54
|
+
*/
|
|
55
|
+
export const withNodeDuration = (nodeId, nodeType, effect) => {
|
|
56
|
+
const metrics = createFlowMetrics();
|
|
57
|
+
return Effect.gen(function* () {
|
|
58
|
+
const startTime = Date.now();
|
|
59
|
+
const result = yield* effect;
|
|
60
|
+
const duration = (Date.now() - startTime) / 1000; // Convert to seconds
|
|
61
|
+
yield* Metric.update(metrics.nodeDurationHistogram, duration);
|
|
62
|
+
yield* Metric.update(metrics.nodeLatencySummary, duration);
|
|
63
|
+
return result;
|
|
64
|
+
}).pipe(Effect.withSpan(`node-${nodeType}`, {
|
|
65
|
+
attributes: {
|
|
66
|
+
"node.id": nodeId,
|
|
67
|
+
"node.type": nodeType,
|
|
68
|
+
},
|
|
69
|
+
}));
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Helper to track active flows
|
|
73
|
+
*/
|
|
74
|
+
export const trackActiveFlow = (effect) => {
|
|
75
|
+
const metrics = createFlowMetrics();
|
|
76
|
+
return Effect.gen(function* () {
|
|
77
|
+
// Increment active flows
|
|
78
|
+
yield* Metric.increment(metrics.activeFlowsGauge);
|
|
79
|
+
// Use acquireUseRelease for proper cleanup
|
|
80
|
+
return yield* Effect.acquireUseRelease(Effect.void, () => effect, () => Metric.set(metrics.activeFlowsGauge, -1));
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Helper to track active nodes
|
|
85
|
+
*/
|
|
86
|
+
export const trackActiveNode = (effect) => {
|
|
87
|
+
const metrics = createFlowMetrics();
|
|
88
|
+
return Effect.gen(function* () {
|
|
89
|
+
// Increment active nodes
|
|
90
|
+
yield* Metric.increment(metrics.activeNodesGauge);
|
|
91
|
+
// Use acquireUseRelease for proper cleanup
|
|
92
|
+
return yield* Effect.acquireUseRelease(Effect.void, () => effect, () => Metric.set(metrics.activeNodesGauge, -1));
|
|
93
|
+
});
|
|
94
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Metric } from "effect";
|
|
2
|
+
/**
|
|
3
|
+
* Flow engine metrics for tracking flow execution operations
|
|
4
|
+
*/
|
|
5
|
+
export declare const createFlowMetrics: () => {
|
|
6
|
+
flowStartedTotal: Metric.Metric.Counter<number>;
|
|
7
|
+
flowCompletedTotal: Metric.Metric.Counter<number>;
|
|
8
|
+
flowFailedTotal: Metric.Metric.Counter<number>;
|
|
9
|
+
flowPausedTotal: Metric.Metric.Counter<number>;
|
|
10
|
+
flowResumedTotal: Metric.Metric.Counter<number>;
|
|
11
|
+
nodeExecutedTotal: Metric.Metric.Counter<number>;
|
|
12
|
+
nodeSuccessTotal: Metric.Metric.Counter<number>;
|
|
13
|
+
nodeFailedTotal: Metric.Metric.Counter<number>;
|
|
14
|
+
nodeSkippedTotal: Metric.Metric.Counter<number>;
|
|
15
|
+
flowDurationHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
16
|
+
nodeDurationHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
17
|
+
flowNodeCountHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
18
|
+
parallelNodesHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
19
|
+
activeFlowsGauge: Metric.Metric.Gauge<number>;
|
|
20
|
+
activeNodesGauge: Metric.Metric.Gauge<number>;
|
|
21
|
+
pausedFlowsGauge: Metric.Metric.Gauge<number>;
|
|
22
|
+
flowLatencySummary: Metric.Metric.Summary<number>;
|
|
23
|
+
nodeLatencySummary: Metric.Metric.Summary<number>;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Type for flow metrics
|
|
27
|
+
*/
|
|
28
|
+
export type FlowMetrics = ReturnType<typeof createFlowMetrics>;
|
|
29
|
+
/**
|
|
30
|
+
* Default flow metrics instance
|
|
31
|
+
*/
|
|
32
|
+
export declare const flowMetrics: {
|
|
33
|
+
flowStartedTotal: Metric.Metric.Counter<number>;
|
|
34
|
+
flowCompletedTotal: Metric.Metric.Counter<number>;
|
|
35
|
+
flowFailedTotal: Metric.Metric.Counter<number>;
|
|
36
|
+
flowPausedTotal: Metric.Metric.Counter<number>;
|
|
37
|
+
flowResumedTotal: Metric.Metric.Counter<number>;
|
|
38
|
+
nodeExecutedTotal: Metric.Metric.Counter<number>;
|
|
39
|
+
nodeSuccessTotal: Metric.Metric.Counter<number>;
|
|
40
|
+
nodeFailedTotal: Metric.Metric.Counter<number>;
|
|
41
|
+
nodeSkippedTotal: Metric.Metric.Counter<number>;
|
|
42
|
+
flowDurationHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
43
|
+
nodeDurationHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
44
|
+
flowNodeCountHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
45
|
+
parallelNodesHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
46
|
+
activeFlowsGauge: Metric.Metric.Gauge<number>;
|
|
47
|
+
activeNodesGauge: Metric.Metric.Gauge<number>;
|
|
48
|
+
pausedFlowsGauge: Metric.Metric.Gauge<number>;
|
|
49
|
+
flowLatencySummary: Metric.Metric.Summary<number>;
|
|
50
|
+
nodeLatencySummary: Metric.Metric.Summary<number>;
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/flow/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAoB,MAAM,QAAQ,CAAC;AAMlD;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;CA8G5B,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE/D;;GAEG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;CAAsB,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Metric, MetricBoundaries } from "effect";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// Flow Engine Metrics
|
|
4
|
+
// ============================================================================
|
|
5
|
+
/**
|
|
6
|
+
* Flow engine metrics for tracking flow execution operations
|
|
7
|
+
*/
|
|
8
|
+
export const createFlowMetrics = () => ({
|
|
9
|
+
// Counter metrics
|
|
10
|
+
flowStartedTotal: Metric.counter("flow_started_total", {
|
|
11
|
+
description: "Total number of flows started",
|
|
12
|
+
}),
|
|
13
|
+
flowCompletedTotal: Metric.counter("flow_completed_total", {
|
|
14
|
+
description: "Total number of flows completed successfully",
|
|
15
|
+
}),
|
|
16
|
+
flowFailedTotal: Metric.counter("flow_failed_total", {
|
|
17
|
+
description: "Total number of flows that failed",
|
|
18
|
+
}),
|
|
19
|
+
flowPausedTotal: Metric.counter("flow_paused_total", {
|
|
20
|
+
description: "Total number of flows that were paused",
|
|
21
|
+
}),
|
|
22
|
+
flowResumedTotal: Metric.counter("flow_resumed_total", {
|
|
23
|
+
description: "Total number of flows that were resumed",
|
|
24
|
+
}),
|
|
25
|
+
nodeExecutedTotal: Metric.counter("node_executed_total", {
|
|
26
|
+
description: "Total number of nodes executed",
|
|
27
|
+
}),
|
|
28
|
+
nodeSuccessTotal: Metric.counter("node_success_total", {
|
|
29
|
+
description: "Total number of nodes executed successfully",
|
|
30
|
+
}),
|
|
31
|
+
nodeFailedTotal: Metric.counter("node_failed_total", {
|
|
32
|
+
description: "Total number of nodes that failed",
|
|
33
|
+
}),
|
|
34
|
+
nodeSkippedTotal: Metric.counter("node_skipped_total", {
|
|
35
|
+
description: "Total number of nodes skipped (conditional)",
|
|
36
|
+
}),
|
|
37
|
+
// Histogram metrics
|
|
38
|
+
flowDurationHistogram: Metric.histogram("flow_duration_seconds", MetricBoundaries.exponential({
|
|
39
|
+
start: 0.1, // 100ms
|
|
40
|
+
factor: 2,
|
|
41
|
+
count: 20, // Up to ~100 seconds
|
|
42
|
+
}), "Duration of complete flow execution in seconds"),
|
|
43
|
+
nodeDurationHistogram: Metric.histogram("node_duration_seconds", MetricBoundaries.exponential({
|
|
44
|
+
start: 0.01, // 10ms
|
|
45
|
+
factor: 2,
|
|
46
|
+
count: 18, // Up to ~26 seconds
|
|
47
|
+
}), "Duration of individual node execution in seconds"),
|
|
48
|
+
flowNodeCountHistogram: Metric.histogram("flow_node_count", MetricBoundaries.linear({
|
|
49
|
+
start: 1,
|
|
50
|
+
width: 5,
|
|
51
|
+
count: 20, // Up to 100 nodes
|
|
52
|
+
}), "Number of nodes in a flow"),
|
|
53
|
+
parallelNodesHistogram: Metric.histogram("parallel_nodes_count", MetricBoundaries.linear({
|
|
54
|
+
start: 1,
|
|
55
|
+
width: 2,
|
|
56
|
+
count: 15, // Up to 30 parallel nodes
|
|
57
|
+
}), "Number of nodes executed in parallel"),
|
|
58
|
+
// Gauge metrics
|
|
59
|
+
activeFlowsGauge: Metric.gauge("active_flows", {
|
|
60
|
+
description: "Number of currently active flows",
|
|
61
|
+
}),
|
|
62
|
+
activeNodesGauge: Metric.gauge("active_nodes", {
|
|
63
|
+
description: "Number of currently executing nodes",
|
|
64
|
+
}),
|
|
65
|
+
pausedFlowsGauge: Metric.gauge("paused_flows", {
|
|
66
|
+
description: "Number of currently paused flows",
|
|
67
|
+
}),
|
|
68
|
+
// Summary metrics for latency percentiles
|
|
69
|
+
flowLatencySummary: Metric.summary({
|
|
70
|
+
name: "flow_latency_seconds",
|
|
71
|
+
maxAge: "10 minutes",
|
|
72
|
+
maxSize: 1000,
|
|
73
|
+
error: 0.01,
|
|
74
|
+
quantiles: [0.5, 0.9, 0.95, 0.99],
|
|
75
|
+
description: "Flow execution latency percentiles",
|
|
76
|
+
}),
|
|
77
|
+
nodeLatencySummary: Metric.summary({
|
|
78
|
+
name: "node_latency_seconds",
|
|
79
|
+
maxAge: "10 minutes",
|
|
80
|
+
maxSize: 1000,
|
|
81
|
+
error: 0.01,
|
|
82
|
+
quantiles: [0.5, 0.9, 0.95, 0.99],
|
|
83
|
+
description: "Node execution latency percentiles",
|
|
84
|
+
}),
|
|
85
|
+
});
|
|
86
|
+
/**
|
|
87
|
+
* Default flow metrics instance
|
|
88
|
+
*/
|
|
89
|
+
export const flowMetrics = createFlowMetrics();
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Effect, Layer } from "effect";
|
|
2
|
+
import { FlowObservability } from "../core/layers.js";
|
|
3
|
+
/**
|
|
4
|
+
* Mock flow observability for testing
|
|
5
|
+
*/
|
|
6
|
+
export declare const makeTestFlowObservability: () => Layer.Layer<FlowObservability>;
|
|
7
|
+
/**
|
|
8
|
+
* Run an effect with test flow observability
|
|
9
|
+
*/
|
|
10
|
+
export declare const runWithTestFlowObservability: <A, E>(effect: Effect.Effect<A, E, FlowObservability>) => Effect.Effect<A, E>;
|
|
11
|
+
//# sourceMappingURL=testing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../src/flow/testing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAOtD;;GAEG;AACH,eAAO,MAAM,yBAAyB,QAAO,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAYzE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,4BAA4B,GAAI,CAAC,EAAE,CAAC,EAC/C,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,iBAAiB,CAAC,KAC7C,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAEpB,CAAC"}
|