@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,47 @@
|
|
|
1
|
+
import { type Effect, Layer } from "effect";
|
|
2
|
+
import { type StorageMetrics } from "../core/metrics.js";
|
|
3
|
+
export declare const s3Metrics: {
|
|
4
|
+
uploadLatencySummary: import("effect/Metric").Metric.Summary<number>;
|
|
5
|
+
activeUploadsGauge: import("effect/Metric").Metric.Gauge<number>;
|
|
6
|
+
uploadThroughputGauge: import("effect/Metric").Metric.Gauge<number>;
|
|
7
|
+
uploadDurationHistogram: import("effect/Metric").Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
8
|
+
partUploadDurationHistogram: import("effect/Metric").Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
9
|
+
fileSizeHistogram: import("effect/Metric").Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
10
|
+
partSizeHistogram: import("effect/Metric").Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
11
|
+
uploadRequestsTotal: import("effect/Metric").Metric.Counter<number>;
|
|
12
|
+
uploadPartsTotal: import("effect/Metric").Metric.Counter<number>;
|
|
13
|
+
uploadSuccessTotal: import("effect/Metric").Metric.Counter<number>;
|
|
14
|
+
uploadErrorsTotal: import("effect/Metric").Metric.Counter<number>;
|
|
15
|
+
apiCallsTotal: import("effect/Metric").Metric.Counter<number>;
|
|
16
|
+
};
|
|
17
|
+
export declare const S3TracingLayer: Layer.Layer<{
|
|
18
|
+
serviceName: string;
|
|
19
|
+
}, never, never>;
|
|
20
|
+
export declare const trackS3Error: (operation: string, error: unknown, context?: Record<string, unknown>) => Effect.Effect<void, never, never>;
|
|
21
|
+
export declare const S3ObservabilityLayer: Layer.Layer<{
|
|
22
|
+
serviceName: string;
|
|
23
|
+
}, never, never>;
|
|
24
|
+
export declare const withS3UploadMetrics: <A, E, R>(uploadId: string, effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
25
|
+
export declare const withS3ApiMetrics: <A, E, R>(operation: string, effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
26
|
+
export declare const withS3TimingMetrics: <A, E, R>(metric: import("effect/Metric").Metric.Histogram<number>, effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
27
|
+
export declare const withS3OperationMetrics: <A, E, R>(operation: string, uploadId: string, effect: Effect.Effect<A, E, R>, fileSize?: number) => Effect.Effect<A, E, R>;
|
|
28
|
+
export declare const withS3Span: <A, E, R>(operation: string, attributes?: Record<string, unknown>) => (effect: Effect.Effect<A, E, R>) => Effect.Effect<unknown, unknown, unknown>;
|
|
29
|
+
export declare const logS3Operation: (operation: string, uploadId: string, metadata?: Record<string, unknown> | undefined) => Effect.Effect<void, never, never>;
|
|
30
|
+
export declare const logS3UploadProgress: (uploadId: string, progress: {
|
|
31
|
+
uploadedBytes: number;
|
|
32
|
+
totalBytes: number;
|
|
33
|
+
partNumber?: number;
|
|
34
|
+
speed?: number;
|
|
35
|
+
}) => Effect.Effect<void, never, never>;
|
|
36
|
+
export declare const logS3UploadCompletion: (uploadId: string, metrics: {
|
|
37
|
+
fileSize: number;
|
|
38
|
+
totalDurationMs: number;
|
|
39
|
+
partsCount?: number;
|
|
40
|
+
averagePartSize?: number;
|
|
41
|
+
throughputBps?: number;
|
|
42
|
+
retryCount?: number;
|
|
43
|
+
}) => Effect.Effect<void, never, never>;
|
|
44
|
+
export declare const logS3Context: (message: string, context: Record<string, unknown>) => Effect.Effect<void, never, never>;
|
|
45
|
+
export declare const s3UploadRequestsTotal: import("effect/Metric").Metric.Counter<number>, s3UploadPartsTotal: import("effect/Metric").Metric.Counter<number>, s3UploadSuccessTotal: import("effect/Metric").Metric.Counter<number>, s3UploadErrorsTotal: import("effect/Metric").Metric.Counter<number>, s3ApiCallsTotal: import("effect/Metric").Metric.Counter<number>, s3UploadDurationHistogram: import("effect/Metric").Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>, s3PartUploadDurationHistogram: import("effect/Metric").Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>, s3FileSizeHistogram: import("effect/Metric").Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>, s3PartSizeHistogram: import("effect/Metric").Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>, s3ActiveUploadsGauge: import("effect/Metric").Metric.Gauge<number>, s3UploadThroughputGauge: import("effect/Metric").Metric.Gauge<number>, s3UploadLatencySummary: import("effect/Metric").Metric.Summary<number>;
|
|
46
|
+
export type S3Metrics = StorageMetrics;
|
|
47
|
+
//# sourceMappingURL=s3.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s3.d.ts","sourceRoot":"","sources":["../../src/storage/s3.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAW5C,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAgB/E,eAAO,MAAM,SAAS;;;;;;;;;;;;;CAAqC,CAAC;AAG5D,eAAO,MAAM,cAAc;;gBAA0C,CAAC;AAqCtE,eAAO,MAAM,YAAY,6GAIxB,CAAC;AAGF,eAAO,MAAM,oBAAoB;;gBAGhC,CAAC;AAMF,eAAO,MAAM,mBAAmB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EACzC,UAAU,MAAM,EAChB,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,2BACmB,CAAC;AAEpD,eAAO,MAAM,gBAAgB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EACtC,WAAW,MAAM,EACjB,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,2BACiB,CAAC;AAElD,eAAO,MAAM,mBAAmB,+HAAoB,CAAC;AAErD,eAAO,MAAM,sBAAsB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAC5C,WAAW,MAAM,EACjB,UAAU,MAAM,EAChB,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC9B,WAAW,MAAM,2BAE4D,CAAC;AAGhF,eAAO,MAAM,UAAU,GACpB,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,WAAW,MAAM,EAAE,aAAa,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,MAChE,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,6CAC+B,CAAC;AAGjE,eAAO,MAAM,cAAc,4HAA+C,CAAC;AAC3E,eAAO,MAAM,mBAAmB;;;;;uCAA6C,CAAC;AAC9E,eAAO,MAAM,qBAAqB;;;;;;;uCAGjC,CAAC;AACF,eAAO,MAAM,YAAY,0FAAiB,CAAC;AAG3C,eAAO,MACgB,qBAAqB,kDACxB,kBAAkB,kDAChB,oBAAoB,kDACrB,mBAAmB,kDACvB,eAAe,kDACL,yBAAyB,sJACrB,6BAA6B,sJACvC,mBAAmB,sJACnB,mBAAmB,sJAClB,oBAAoB,gDACjB,uBAAuB,gDACxB,sBAAsB,gDACjC,CAAC;AAGd,MAAM,MAAM,SAAS,GAAG,cAAc,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Layer } from "effect";
|
|
2
|
+
import { createStorageErrorTracker, } from "../core/errors.js";
|
|
3
|
+
import { logStorageOperation, logUploadCompletion, logUploadProgress, logWithContext, } from "../core/logging.js";
|
|
4
|
+
import { createStorageMetrics } from "../core/metrics.js";
|
|
5
|
+
import { createStorageTracingLayer, withStorageSpan } from "../core/tracing.js";
|
|
6
|
+
import { withApiMetrics, withStorageOperationMetrics, withTimingMetrics, withUploadMetrics, } from "../core/utilities.js";
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// S3-Specific Observability
|
|
9
|
+
// ============================================================================
|
|
10
|
+
const STORAGE_TYPE = "s3";
|
|
11
|
+
// S3-specific metrics
|
|
12
|
+
export const s3Metrics = createStorageMetrics(STORAGE_TYPE);
|
|
13
|
+
// S3-specific tracing layer
|
|
14
|
+
export const S3TracingLayer = createStorageTracingLayer(STORAGE_TYPE);
|
|
15
|
+
// S3-specific error classification
|
|
16
|
+
const classifyS3Error = (error) => {
|
|
17
|
+
if (!error || typeof error !== "object")
|
|
18
|
+
return null;
|
|
19
|
+
const errorCode = "code" in error ? error.code : undefined;
|
|
20
|
+
if (!errorCode)
|
|
21
|
+
return null;
|
|
22
|
+
// S3-specific error codes
|
|
23
|
+
switch (errorCode) {
|
|
24
|
+
case "NoSuchKey":
|
|
25
|
+
case "NoSuchBucket":
|
|
26
|
+
case "NoSuchUpload":
|
|
27
|
+
return "client_error";
|
|
28
|
+
case "BucketAlreadyExists":
|
|
29
|
+
case "BucketNotEmpty":
|
|
30
|
+
return "client_error";
|
|
31
|
+
case "InvalidBucketName":
|
|
32
|
+
case "InvalidPart":
|
|
33
|
+
case "InvalidPartOrder":
|
|
34
|
+
return "client_error";
|
|
35
|
+
case "EntityTooSmall":
|
|
36
|
+
case "EntityTooLarge":
|
|
37
|
+
return "client_error";
|
|
38
|
+
case "ExpiredToken":
|
|
39
|
+
case "TokenRefreshRequired":
|
|
40
|
+
return "authentication_error";
|
|
41
|
+
case "RequestTimeTooSkewed":
|
|
42
|
+
case "SlowDown":
|
|
43
|
+
return "throttling_error";
|
|
44
|
+
default:
|
|
45
|
+
return null; // Fall back to generic classification
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
// S3-specific error tracker
|
|
49
|
+
export const trackS3Error = createStorageErrorTracker(STORAGE_TYPE, s3Metrics, classifyS3Error);
|
|
50
|
+
// S3-specific observability layer
|
|
51
|
+
export const S3ObservabilityLayer = Layer.mergeAll(S3TracingLayer);
|
|
52
|
+
// ============================================================================
|
|
53
|
+
// S3 Utility Functions
|
|
54
|
+
// ============================================================================
|
|
55
|
+
export const withS3UploadMetrics = (uploadId, effect) => withUploadMetrics(s3Metrics, uploadId, effect);
|
|
56
|
+
export const withS3ApiMetrics = (operation, effect) => withApiMetrics(s3Metrics, operation, effect);
|
|
57
|
+
export const withS3TimingMetrics = withTimingMetrics;
|
|
58
|
+
export const withS3OperationMetrics = (operation, uploadId, effect, fileSize) => withStorageOperationMetrics(s3Metrics, operation, uploadId, effect, fileSize);
|
|
59
|
+
// S3-specific span wrapper
|
|
60
|
+
export const withS3Span = (operation, attributes) => (effect) => withStorageSpan(operation, STORAGE_TYPE, attributes)(effect);
|
|
61
|
+
// S3-specific logging functions
|
|
62
|
+
export const logS3Operation = logStorageOperation.bind(null, STORAGE_TYPE);
|
|
63
|
+
export const logS3UploadProgress = logUploadProgress.bind(null, STORAGE_TYPE);
|
|
64
|
+
export const logS3UploadCompletion = logUploadCompletion.bind(null, STORAGE_TYPE);
|
|
65
|
+
export const logS3Context = logWithContext;
|
|
66
|
+
// Export metrics for external access
|
|
67
|
+
export const { uploadRequestsTotal: s3UploadRequestsTotal, uploadPartsTotal: s3UploadPartsTotal, uploadSuccessTotal: s3UploadSuccessTotal, uploadErrorsTotal: s3UploadErrorsTotal, apiCallsTotal: s3ApiCallsTotal, uploadDurationHistogram: s3UploadDurationHistogram, partUploadDurationHistogram: s3PartUploadDurationHistogram, fileSizeHistogram: s3FileSizeHistogram, partSizeHistogram: s3PartSizeHistogram, activeUploadsGauge: s3ActiveUploadsGauge, uploadThroughputGauge: s3UploadThroughputGauge, uploadLatencySummary: s3UploadLatencySummary, } = s3Metrics;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation test for core observability infrastructure
|
|
3
|
+
*
|
|
4
|
+
* This test validates:
|
|
5
|
+
* 1. Layer creation and composition
|
|
6
|
+
* 2. Storage observability with metrics
|
|
7
|
+
* 3. Error classification
|
|
8
|
+
* 4. Tracing with spans
|
|
9
|
+
* 5. Structured logging
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=test-observability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-observability.d.ts","sourceRoot":"","sources":["../src/test-observability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation test for core observability infrastructure
|
|
3
|
+
*
|
|
4
|
+
* This test validates:
|
|
5
|
+
* 1. Layer creation and composition
|
|
6
|
+
* 2. Storage observability with metrics
|
|
7
|
+
* 3. Error classification
|
|
8
|
+
* 4. Tracing with spans
|
|
9
|
+
* 5. Structured logging
|
|
10
|
+
*/
|
|
11
|
+
import { Effect, Metric } from "effect";
|
|
12
|
+
import { classifyStorageError, createStorageMetrics, makeStorageObservabilityLayer, StorageObservability, trackStorageError, withStorageSpan, logStorageOperation, logUploadCompletion, } from "./index.js";
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Test: Create Storage Observability Layer
|
|
15
|
+
// ============================================================================
|
|
16
|
+
const testStorageType = "test-s3";
|
|
17
|
+
const testMetrics = createStorageMetrics(testStorageType);
|
|
18
|
+
const TestStorageObservabilityLayer = makeStorageObservabilityLayer(testStorageType, testMetrics, true);
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// Test: Simulate Upload Operation with Full Observability
|
|
21
|
+
// ============================================================================
|
|
22
|
+
const simulateUpload = (fileSize, shouldFail) => Effect.gen(function* () {
|
|
23
|
+
const obs = yield* StorageObservability;
|
|
24
|
+
console.log(`\n📊 Testing with storage type: ${obs.storageType}`);
|
|
25
|
+
console.log(`Service: ${obs.serviceName}`);
|
|
26
|
+
console.log(`Enabled: ${obs.enabled}`);
|
|
27
|
+
// Log operation start
|
|
28
|
+
yield* logStorageOperation(testStorageType, "uploadFile", "test-upload-123", { file_size: fileSize });
|
|
29
|
+
// Simulate upload with tracing
|
|
30
|
+
const uploadEffect = Effect.gen(function* () {
|
|
31
|
+
// Track upload request
|
|
32
|
+
yield* obs.metrics.uploadRequestsTotal.pipe(Metric.tagged("upload_id", "test-upload-123"))(Effect.succeed(1));
|
|
33
|
+
// Track file size
|
|
34
|
+
yield* obs.metrics.fileSizeHistogram(Effect.succeed(fileSize));
|
|
35
|
+
// Simulate upload work
|
|
36
|
+
yield* Effect.sleep("100 millis");
|
|
37
|
+
if (shouldFail) {
|
|
38
|
+
return yield* Effect.fail(new Error("NetworkError: Connection timeout"));
|
|
39
|
+
}
|
|
40
|
+
// Track success
|
|
41
|
+
yield* obs.metrics.uploadSuccessTotal.pipe(Metric.tagged("upload_id", "test-upload-123"))(Effect.succeed(1));
|
|
42
|
+
return { uploadId: "test-upload-123", key: "test-file.jpg" };
|
|
43
|
+
});
|
|
44
|
+
// Wrap with span
|
|
45
|
+
const result = yield* uploadEffect.pipe(withStorageSpan("uploadFile", testStorageType, {
|
|
46
|
+
"file.size": fileSize,
|
|
47
|
+
"upload.id": "test-upload-123",
|
|
48
|
+
}));
|
|
49
|
+
// Log completion
|
|
50
|
+
yield* logUploadCompletion(testStorageType, "test-upload-123", {
|
|
51
|
+
fileSize,
|
|
52
|
+
totalDurationMs: 100,
|
|
53
|
+
throughputBps: (fileSize / 100) * 1000,
|
|
54
|
+
});
|
|
55
|
+
return result;
|
|
56
|
+
});
|
|
57
|
+
// ============================================================================
|
|
58
|
+
// Test: Error Classification
|
|
59
|
+
// ============================================================================
|
|
60
|
+
const testErrorClassification = Effect.gen(function* () {
|
|
61
|
+
console.log("\n🔍 Testing Error Classification:");
|
|
62
|
+
const testErrors = [
|
|
63
|
+
{ error: { code: "NetworkError" }, expected: "network_error" },
|
|
64
|
+
{ error: { code: "InvalidAccessKeyId" }, expected: "authentication_error" },
|
|
65
|
+
{ error: { code: "AccessDenied" }, expected: "authorization_error" },
|
|
66
|
+
{ error: { code: "SlowDown" }, expected: "throttling_error" },
|
|
67
|
+
{ error: { code: "InternalError" }, expected: "server_error" },
|
|
68
|
+
{ error: { code: "InvalidRequest" }, expected: "client_error" },
|
|
69
|
+
{ error: { code: "UnknownError" }, expected: "unknown_error" },
|
|
70
|
+
];
|
|
71
|
+
for (const { error, expected } of testErrors) {
|
|
72
|
+
const category = classifyStorageError(error);
|
|
73
|
+
const status = category === expected ? "✅" : "❌";
|
|
74
|
+
console.log(` ${status} ${error.code} -> ${category} (expected: ${expected})`);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
// ============================================================================
|
|
78
|
+
// Test: Error Tracking with Metrics
|
|
79
|
+
// ============================================================================
|
|
80
|
+
const testErrorTracking = Effect.gen(function* () {
|
|
81
|
+
const obs = yield* StorageObservability;
|
|
82
|
+
console.log("\n⚠️ Testing Error Tracking:");
|
|
83
|
+
const error = new Error("ECONNRESET: Connection reset by peer");
|
|
84
|
+
error.code = "ECONNRESET";
|
|
85
|
+
yield* trackStorageError(testStorageType, obs.metrics, "uploadPart", error, { upload_id: "test-upload-456", part_number: 1 });
|
|
86
|
+
console.log(" ✅ Error tracked with metrics and logs");
|
|
87
|
+
});
|
|
88
|
+
// ============================================================================
|
|
89
|
+
// Test: Successful Upload Flow
|
|
90
|
+
// ============================================================================
|
|
91
|
+
const testSuccessfulUpload = Effect.gen(function* () {
|
|
92
|
+
console.log("\n✅ Testing Successful Upload:");
|
|
93
|
+
const result = yield* simulateUpload(1024 * 1024 * 10, false); // 10MB file
|
|
94
|
+
console.log(` ✅ Upload completed: ${result.uploadId} -> ${result.key}`);
|
|
95
|
+
});
|
|
96
|
+
// ============================================================================
|
|
97
|
+
// Test: Failed Upload Flow
|
|
98
|
+
// ============================================================================
|
|
99
|
+
const testFailedUpload = Effect.gen(function* () {
|
|
100
|
+
console.log("\n❌ Testing Failed Upload:");
|
|
101
|
+
const obs = yield* StorageObservability;
|
|
102
|
+
// Use either to handle success and failure cases
|
|
103
|
+
const result = yield* Effect.either(simulateUpload(1024 * 1024 * 5, true));
|
|
104
|
+
if (result._tag === "Left") {
|
|
105
|
+
const error = result.left;
|
|
106
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
107
|
+
console.log(` ❌ Upload failed as expected: ${errorMsg}`);
|
|
108
|
+
// Track the error
|
|
109
|
+
yield* trackStorageError(testStorageType, obs.metrics, "uploadFile", error, { upload_id: "test-upload-789" });
|
|
110
|
+
console.log(" ✅ Error handled and tracked successfully");
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
console.log(" ❌ Upload should have failed but succeeded");
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
// ============================================================================
|
|
117
|
+
// Test: Metrics Snapshot
|
|
118
|
+
// ============================================================================
|
|
119
|
+
const testMetricsSnapshot = Effect.gen(function* () {
|
|
120
|
+
console.log("\n📈 Testing Metrics Snapshot:");
|
|
121
|
+
const snapshot = yield* Metric.snapshot;
|
|
122
|
+
console.log(` ✅ Captured ${snapshot.length} metric(s)`);
|
|
123
|
+
// Display some metrics
|
|
124
|
+
for (const metric of snapshot.slice(0, 5)) {
|
|
125
|
+
console.log(` - ${metric.metricKey.name}: ${JSON.stringify(metric.metricState)}`);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
// ============================================================================
|
|
129
|
+
// Run All Tests
|
|
130
|
+
// ============================================================================
|
|
131
|
+
const runTests = Effect.gen(function* () {
|
|
132
|
+
console.log("🧪 Observability Infrastructure Validation Test");
|
|
133
|
+
console.log("=".repeat(50));
|
|
134
|
+
// Run all tests
|
|
135
|
+
yield* testErrorClassification;
|
|
136
|
+
yield* testSuccessfulUpload;
|
|
137
|
+
yield* testFailedUpload;
|
|
138
|
+
yield* testErrorTracking;
|
|
139
|
+
yield* testMetricsSnapshot;
|
|
140
|
+
console.log("\n" + "=".repeat(50));
|
|
141
|
+
console.log("✅ All tests completed successfully!");
|
|
142
|
+
console.log("\nCore observability infrastructure is working correctly:");
|
|
143
|
+
console.log(" ✅ Layer creation and composition");
|
|
144
|
+
console.log(" ✅ Metrics tracking (counters, histograms, gauges)");
|
|
145
|
+
console.log(" ✅ Error classification and tracking");
|
|
146
|
+
console.log(" ✅ Tracing with spans");
|
|
147
|
+
console.log(" ✅ Structured logging");
|
|
148
|
+
}).pipe(Effect.provide(TestStorageObservabilityLayer));
|
|
149
|
+
// Run the tests
|
|
150
|
+
Effect.runPromise(runTests).catch((error) => {
|
|
151
|
+
console.error("\n❌ Test failed:", error);
|
|
152
|
+
process.exit(1);
|
|
153
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import type { UploadServerMetrics } from "./metrics.js";
|
|
3
|
+
export type UploadErrorCategory = "network_error" | "authentication_error" | "authorization_error" | "validation_error" | "size_limit_error" | "storage_error" | "abort_error" | "unknown_error";
|
|
4
|
+
/**
|
|
5
|
+
* Classify upload errors into standard categories
|
|
6
|
+
*/
|
|
7
|
+
export declare const classifyUploadError: (error: unknown) => UploadErrorCategory;
|
|
8
|
+
/**
|
|
9
|
+
* Track upload errors with metrics and structured logging
|
|
10
|
+
*/
|
|
11
|
+
export declare const trackUploadError: (metrics: UploadServerMetrics, operation: string, error: unknown, context?: Record<string, unknown>) => Effect.Effect<void, never, never>;
|
|
12
|
+
/**
|
|
13
|
+
* Create a custom error classifier for upload operations
|
|
14
|
+
*/
|
|
15
|
+
export declare const createUploadErrorClassifier: (customErrorMapping?: (error: unknown) => UploadErrorCategory | null) => (error: unknown) => UploadErrorCategory;
|
|
16
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/upload/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAMxD,MAAM,MAAM,mBAAmB,GAC3B,eAAe,GACf,sBAAsB,GACtB,qBAAqB,GACrB,kBAAkB,GAClB,kBAAkB,GAClB,eAAe,GACf,aAAa,GACb,eAAe,CAAC;AAEpB;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAAI,OAAO,OAAO,KAAG,mBAqFpD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gBAAgB,GAC3B,SAAS,mBAAmB,EAC5B,WAAW,MAAM,EACjB,OAAO,OAAO,EACd,UAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,sCAiCnC,CAAC;AAEL;;GAEG;AACH,eAAO,MAAM,2BAA2B,GACtC,qBAAqB,CAAC,KAAK,EAAE,OAAO,KAAK,mBAAmB,GAAG,IAAI,MAE3D,OAAO,OAAO,KAAG,mBAU1B,CAAC"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Effect, Metric } from "effect";
|
|
2
|
+
/**
|
|
3
|
+
* Classify upload errors into standard categories
|
|
4
|
+
*/
|
|
5
|
+
export const classifyUploadError = (error) => {
|
|
6
|
+
if (!error || typeof error !== "object")
|
|
7
|
+
return "unknown_error";
|
|
8
|
+
const errorCode = "code" in error ? error.code : undefined;
|
|
9
|
+
const errorName = "name" in error ? error.name : undefined;
|
|
10
|
+
const errorMessage = error instanceof Error ? error.message.toLowerCase() : "";
|
|
11
|
+
// Abort errors
|
|
12
|
+
if (errorCode === "ABORTED" ||
|
|
13
|
+
errorName === "AbortError" ||
|
|
14
|
+
errorMessage.includes("abort")) {
|
|
15
|
+
return "abort_error";
|
|
16
|
+
}
|
|
17
|
+
// Size limit errors
|
|
18
|
+
if (errorCode === "FILE_TOO_LARGE" ||
|
|
19
|
+
errorCode === "LIMIT_FILE_SIZE" ||
|
|
20
|
+
errorCode === "RequestEntityTooLarge" ||
|
|
21
|
+
errorMessage.includes("too large") ||
|
|
22
|
+
errorMessage.includes("size limit") ||
|
|
23
|
+
errorMessage.includes("max size")) {
|
|
24
|
+
return "size_limit_error";
|
|
25
|
+
}
|
|
26
|
+
// Validation errors
|
|
27
|
+
if (errorCode === "INVALID_FILE" ||
|
|
28
|
+
errorCode === "INVALID_METADATA" ||
|
|
29
|
+
errorCode === "VALIDATION_ERROR" ||
|
|
30
|
+
errorMessage.includes("validation") ||
|
|
31
|
+
errorMessage.includes("invalid")) {
|
|
32
|
+
return "validation_error";
|
|
33
|
+
}
|
|
34
|
+
// Network errors
|
|
35
|
+
if (errorCode === "NetworkError" ||
|
|
36
|
+
errorCode === "ECONNRESET" ||
|
|
37
|
+
errorCode === "ENOTFOUND" ||
|
|
38
|
+
errorCode === "ETIMEDOUT" ||
|
|
39
|
+
errorMessage.includes("network") ||
|
|
40
|
+
errorMessage.includes("timeout")) {
|
|
41
|
+
return "network_error";
|
|
42
|
+
}
|
|
43
|
+
// Authentication errors
|
|
44
|
+
if (errorCode === "UNAUTHORIZED" ||
|
|
45
|
+
errorCode === "AuthenticationFailed" ||
|
|
46
|
+
errorName === "AuthenticationError" ||
|
|
47
|
+
errorMessage.includes("authentication") ||
|
|
48
|
+
errorMessage.includes("unauthorized")) {
|
|
49
|
+
return "authentication_error";
|
|
50
|
+
}
|
|
51
|
+
// Authorization errors
|
|
52
|
+
if (errorCode === "FORBIDDEN" ||
|
|
53
|
+
errorCode === "AccessDenied" ||
|
|
54
|
+
errorName === "AuthorizationError" ||
|
|
55
|
+
errorMessage.includes("forbidden") ||
|
|
56
|
+
errorMessage.includes("permission")) {
|
|
57
|
+
return "authorization_error";
|
|
58
|
+
}
|
|
59
|
+
// Storage errors
|
|
60
|
+
if (errorCode === "FILE_WRITE_ERROR" ||
|
|
61
|
+
errorCode === "STORAGE_ERROR" ||
|
|
62
|
+
errorMessage.includes("storage") ||
|
|
63
|
+
errorMessage.includes("write error")) {
|
|
64
|
+
return "storage_error";
|
|
65
|
+
}
|
|
66
|
+
return "unknown_error";
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Track upload errors with metrics and structured logging
|
|
70
|
+
*/
|
|
71
|
+
export const trackUploadError = (metrics, operation, error, context = {}) => Effect.gen(function* () {
|
|
72
|
+
const errorCategory = classifyUploadError(error);
|
|
73
|
+
// Record error metrics
|
|
74
|
+
const errorMetric = metrics.uploadFailedTotal.pipe(Metric.tagged("operation", operation), Metric.tagged("error_category", errorCategory));
|
|
75
|
+
yield* errorMetric(Effect.succeed(1));
|
|
76
|
+
// Create detailed error context
|
|
77
|
+
const errorDetails = {
|
|
78
|
+
operation,
|
|
79
|
+
error_category: errorCategory,
|
|
80
|
+
error_type: typeof error,
|
|
81
|
+
error_message: error instanceof Error ? error.message : String(error),
|
|
82
|
+
error_code: error && typeof error === "object" && "code" in error
|
|
83
|
+
? String(error.code)
|
|
84
|
+
: undefined,
|
|
85
|
+
error_name: error && typeof error === "object" && "name" in error
|
|
86
|
+
? String(error.name)
|
|
87
|
+
: undefined,
|
|
88
|
+
...context,
|
|
89
|
+
};
|
|
90
|
+
// Log structured error
|
|
91
|
+
yield* Effect.logError(`Upload ${operation} failed`).pipe(Effect.annotateLogs(errorDetails));
|
|
92
|
+
});
|
|
93
|
+
/**
|
|
94
|
+
* Create a custom error classifier for upload operations
|
|
95
|
+
*/
|
|
96
|
+
export const createUploadErrorClassifier = (customErrorMapping) => {
|
|
97
|
+
return (error) => {
|
|
98
|
+
// Try custom mapping first
|
|
99
|
+
if (customErrorMapping) {
|
|
100
|
+
const customResult = customErrorMapping(error);
|
|
101
|
+
if (customResult !== null)
|
|
102
|
+
return customResult;
|
|
103
|
+
}
|
|
104
|
+
// Fall back to generic classification
|
|
105
|
+
return classifyUploadError(error);
|
|
106
|
+
};
|
|
107
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from "./metrics.js";
|
|
2
|
+
export * from "./tracing.js";
|
|
3
|
+
export { makeUploadObservabilityLive, UploadObservabilityLive, getUploadMetrics, withUploadDuration, withChunkDuration, } from "./layers.js";
|
|
4
|
+
export * from "./errors.js";
|
|
5
|
+
export * from "./testing.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/upload/index.ts"],"names":[],"mappings":"AACA,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,OAAO,EACL,2BAA2B,EAC3B,uBAAuB,EACvB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Upload observability exports
|
|
2
|
+
export * from "./metrics.js";
|
|
3
|
+
export * from "./tracing.js";
|
|
4
|
+
export { makeUploadObservabilityLive, UploadObservabilityLive, getUploadMetrics, withUploadDuration, withChunkDuration, } from "./layers.js";
|
|
5
|
+
export * from "./errors.js";
|
|
6
|
+
export * from "./testing.js";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Effect, Layer } from "effect";
|
|
2
|
+
import { UploadObservability } from "../core/layers.js";
|
|
3
|
+
/**
|
|
4
|
+
* Create a live upload observability layer with full metrics
|
|
5
|
+
*/
|
|
6
|
+
export declare const makeUploadObservabilityLive: (serviceName?: string) => Layer.Layer<UploadObservability>;
|
|
7
|
+
/**
|
|
8
|
+
* Default live upload observability layer
|
|
9
|
+
*/
|
|
10
|
+
export declare const UploadObservabilityLive: Layer.Layer<UploadObservability, never, never>;
|
|
11
|
+
/**
|
|
12
|
+
* No-op upload observability layer (for testing or disabled observability)
|
|
13
|
+
*/
|
|
14
|
+
export declare const UploadObservabilityDisabled: Layer.Layer<UploadObservability, never, never>;
|
|
15
|
+
/**
|
|
16
|
+
* Helper to get upload metrics from context
|
|
17
|
+
*/
|
|
18
|
+
export declare const getUploadMetrics: Effect.Effect<{
|
|
19
|
+
uploadCreated: Effect.Effect<void>;
|
|
20
|
+
uploadCompleted: Effect.Effect<void>;
|
|
21
|
+
uploadFailed: Effect.Effect<void>;
|
|
22
|
+
chunkUploaded: Effect.Effect<void>;
|
|
23
|
+
}, never, UploadObservability>;
|
|
24
|
+
/**
|
|
25
|
+
* Helper to track upload duration
|
|
26
|
+
*/
|
|
27
|
+
export declare const withUploadDuration: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R | UploadObservability>;
|
|
28
|
+
/**
|
|
29
|
+
* Helper to track chunk upload duration
|
|
30
|
+
*/
|
|
31
|
+
export declare const withChunkDuration: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
32
|
+
//# sourceMappingURL=layers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layers.d.ts","sourceRoot":"","sources":["../../src/upload/layers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAU,MAAM,QAAQ,CAAC;AAC/C,OAAO,EAEL,mBAAmB,EACpB,MAAM,mBAAmB,CAAC;AAO3B;;GAEG;AACH,eAAO,MAAM,2BAA2B,GACtC,oBAAwC,KACvC,KAAK,CAAC,KAAK,CAAC,mBAAmB,CAqBjC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,uBAAuB,gDAAgC,CAAC;AAErE;;GAEG;AACH,eAAO,MAAM,2BAA2B,gDAAsC,CAAC;AAE/E;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;;;8BAG3B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EACxC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,mBAAmB,CAS7C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EACvC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CASvB,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Effect, Layer, Metric } from "effect";
|
|
2
|
+
import { makeUploadObservabilityLayer, UploadObservability, } from "../core/layers.js";
|
|
3
|
+
import { createUploadServerMetrics } from "./metrics.js";
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// Upload Observability Layer Implementation
|
|
6
|
+
// ============================================================================
|
|
7
|
+
/**
|
|
8
|
+
* Create a live upload observability layer with full metrics
|
|
9
|
+
*/
|
|
10
|
+
export const makeUploadObservabilityLive = (serviceName = "uploadista-upload-server") => {
|
|
11
|
+
const metrics = createUploadServerMetrics();
|
|
12
|
+
return Layer.succeed(UploadObservability, {
|
|
13
|
+
serviceName,
|
|
14
|
+
enabled: true,
|
|
15
|
+
metrics: {
|
|
16
|
+
uploadCreated: Effect.succeed(metrics.uploadCreatedTotal).pipe(Effect.flatMap((metric) => Metric.increment(metric))),
|
|
17
|
+
uploadCompleted: Effect.succeed(metrics.uploadCompletedTotal).pipe(Effect.flatMap((metric) => Metric.increment(metric))),
|
|
18
|
+
uploadFailed: Effect.succeed(metrics.uploadFailedTotal).pipe(Effect.flatMap((metric) => Metric.increment(metric))),
|
|
19
|
+
chunkUploaded: Effect.succeed(metrics.chunkUploadedTotal).pipe(Effect.flatMap((metric) => Metric.increment(metric))),
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Default live upload observability layer
|
|
25
|
+
*/
|
|
26
|
+
export const UploadObservabilityLive = makeUploadObservabilityLive();
|
|
27
|
+
/**
|
|
28
|
+
* No-op upload observability layer (for testing or disabled observability)
|
|
29
|
+
*/
|
|
30
|
+
export const UploadObservabilityDisabled = makeUploadObservabilityLayer(false);
|
|
31
|
+
/**
|
|
32
|
+
* Helper to get upload metrics from context
|
|
33
|
+
*/
|
|
34
|
+
export const getUploadMetrics = Effect.gen(function* () {
|
|
35
|
+
const obs = yield* UploadObservability;
|
|
36
|
+
return obs.metrics;
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* Helper to track upload duration
|
|
40
|
+
*/
|
|
41
|
+
export const withUploadDuration = (effect) => {
|
|
42
|
+
const metrics = createUploadServerMetrics();
|
|
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.uploadDurationHistogram, duration);
|
|
48
|
+
return result;
|
|
49
|
+
}).pipe(Effect.withSpan("upload-operation"));
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Helper to track chunk upload duration
|
|
53
|
+
*/
|
|
54
|
+
export const withChunkDuration = (effect) => {
|
|
55
|
+
const metrics = createUploadServerMetrics();
|
|
56
|
+
return Effect.gen(function* () {
|
|
57
|
+
const startTime = Date.now();
|
|
58
|
+
const result = yield* effect;
|
|
59
|
+
const duration = (Date.now() - startTime) / 1000; // Convert to seconds
|
|
60
|
+
yield* Metric.update(metrics.chunkUploadDurationHistogram, duration);
|
|
61
|
+
return result;
|
|
62
|
+
}).pipe(Effect.withSpan("chunk-upload"));
|
|
63
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Metric } from "effect";
|
|
2
|
+
/**
|
|
3
|
+
* Upload server metrics for tracking upload operations
|
|
4
|
+
*/
|
|
5
|
+
export declare const createUploadServerMetrics: () => {
|
|
6
|
+
uploadCreatedTotal: Metric.Metric.Counter<number>;
|
|
7
|
+
uploadCompletedTotal: Metric.Metric.Counter<number>;
|
|
8
|
+
uploadFailedTotal: Metric.Metric.Counter<number>;
|
|
9
|
+
chunkUploadedTotal: Metric.Metric.Counter<number>;
|
|
10
|
+
uploadFromUrlTotal: Metric.Metric.Counter<number>;
|
|
11
|
+
uploadFromUrlSuccessTotal: Metric.Metric.Counter<number>;
|
|
12
|
+
uploadFromUrlFailedTotal: Metric.Metric.Counter<number>;
|
|
13
|
+
uploadDurationHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
14
|
+
chunkUploadDurationHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
15
|
+
uploadFileSizeHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
16
|
+
chunkSizeHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
17
|
+
activeUploadsGauge: Metric.Metric.Gauge<number>;
|
|
18
|
+
uploadThroughputGauge: Metric.Metric.Gauge<number>;
|
|
19
|
+
uploadLatencySummary: Metric.Metric.Summary<number>;
|
|
20
|
+
chunkLatencySummary: Metric.Metric.Summary<number>;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Type for upload server metrics
|
|
24
|
+
*/
|
|
25
|
+
export type UploadServerMetrics = ReturnType<typeof createUploadServerMetrics>;
|
|
26
|
+
/**
|
|
27
|
+
* Default upload server metrics instance
|
|
28
|
+
*/
|
|
29
|
+
export declare const uploadServerMetrics: {
|
|
30
|
+
uploadCreatedTotal: Metric.Metric.Counter<number>;
|
|
31
|
+
uploadCompletedTotal: Metric.Metric.Counter<number>;
|
|
32
|
+
uploadFailedTotal: Metric.Metric.Counter<number>;
|
|
33
|
+
chunkUploadedTotal: Metric.Metric.Counter<number>;
|
|
34
|
+
uploadFromUrlTotal: Metric.Metric.Counter<number>;
|
|
35
|
+
uploadFromUrlSuccessTotal: Metric.Metric.Counter<number>;
|
|
36
|
+
uploadFromUrlFailedTotal: Metric.Metric.Counter<number>;
|
|
37
|
+
uploadDurationHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
38
|
+
chunkUploadDurationHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
39
|
+
uploadFileSizeHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
40
|
+
chunkSizeHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
41
|
+
activeUploadsGauge: Metric.Metric.Gauge<number>;
|
|
42
|
+
uploadThroughputGauge: Metric.Metric.Gauge<number>;
|
|
43
|
+
uploadLatencySummary: Metric.Metric.Summary<number>;
|
|
44
|
+
chunkLatencySummary: Metric.Metric.Summary<number>;
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/upload/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAoB,MAAM,QAAQ,CAAC;AAMlD;;GAEG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;CAkGpC,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE/E;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;CAA8B,CAAC"}
|