@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
|
File without changes
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 uploadista
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import type { StorageMetrics } from "./metrics.js";
|
|
3
|
+
export type StorageErrorCategory = "network_error" | "authentication_error" | "authorization_error" | "throttling_error" | "server_error" | "client_error" | "unknown_error";
|
|
4
|
+
export declare const classifyStorageError: (error: unknown) => StorageErrorCategory;
|
|
5
|
+
export declare const createStorageErrorClassifier: (storageType: string, customErrorMapping?: (error: unknown) => StorageErrorCategory | null) => (error: unknown) => StorageErrorCategory;
|
|
6
|
+
export declare const trackStorageError: (storageType: string, metrics: StorageMetrics, operation: string, error: unknown, context?: Record<string, unknown>, errorClassifier?: (error: unknown) => StorageErrorCategory) => Effect.Effect<void, never, never>;
|
|
7
|
+
export declare const createStorageErrorTracker: (storageType: string, metrics: StorageMetrics, customErrorClassifier?: (error: unknown) => StorageErrorCategory | null) => (operation: string, error: unknown, context?: Record<string, unknown>) => Effect.Effect<void, never, never>;
|
|
8
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAMnD,MAAM,MAAM,oBAAoB,GAC5B,eAAe,GACf,sBAAsB,GACtB,qBAAqB,GACrB,kBAAkB,GAClB,cAAc,GACd,cAAc,GACd,eAAe,CAAC;AAGpB,eAAO,MAAM,oBAAoB,GAAI,OAAO,OAAO,KAAG,oBAmFrD,CAAC;AAGF,eAAO,MAAM,4BAA4B,GACvC,aAAa,MAAM,EACnB,qBAAqB,CAAC,KAAK,EAAE,OAAO,KAAK,oBAAoB,GAAG,IAAI,MAE5D,OAAO,OAAO,KAAG,oBAU1B,CAAC;AAGF,eAAO,MAAM,iBAAiB,GAC5B,aAAa,MAAM,EACnB,SAAS,cAAc,EACvB,WAAW,MAAM,EACjB,OAAO,OAAO,EACd,UAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACrC,0BA7G0C,OAAO,KAAG,oBA6Gd,sCAkCpC,CAAC;AAGL,eAAO,MAAM,yBAAyB,GACpC,aAAa,MAAM,EACnB,SAAS,cAAc,EACvB,wBAAwB,CAAC,KAAK,EAAE,OAAO,KAAK,oBAAoB,GAAG,IAAI,MAQrE,WAAW,MAAM,EACjB,OAAO,OAAO,EACd,UAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,sCAUxC,CAAC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Effect, Metric } from "effect";
|
|
2
|
+
// Generic error classifier - can be extended per storage type
|
|
3
|
+
export const classifyStorageError = (error) => {
|
|
4
|
+
if (!error || typeof error !== "object")
|
|
5
|
+
return "unknown_error";
|
|
6
|
+
const errorCode = "code" in error ? error.code : undefined;
|
|
7
|
+
const errorName = "name" in error ? error.name : undefined;
|
|
8
|
+
const errorMessage = error instanceof Error ? error.message.toLowerCase() : "";
|
|
9
|
+
// Network errors (common across all storage types)
|
|
10
|
+
if (errorCode === "NetworkError" ||
|
|
11
|
+
errorCode === "ECONNRESET" ||
|
|
12
|
+
errorCode === "ENOTFOUND" ||
|
|
13
|
+
errorCode === "ETIMEDOUT" ||
|
|
14
|
+
errorMessage.indexOf("network") >= 0 ||
|
|
15
|
+
errorMessage.indexOf("timeout") >= 0) {
|
|
16
|
+
return "network_error";
|
|
17
|
+
}
|
|
18
|
+
// Authentication errors (common patterns)
|
|
19
|
+
if (errorCode === "InvalidAccessKeyId" ||
|
|
20
|
+
errorCode === "SignatureDoesNotMatch" ||
|
|
21
|
+
errorCode === "TokenRefreshRequired" ||
|
|
22
|
+
errorCode === "AuthenticationFailed" ||
|
|
23
|
+
errorName === "AuthenticationError" ||
|
|
24
|
+
errorMessage.indexOf("authentication") >= 0 ||
|
|
25
|
+
errorMessage.indexOf("unauthorized") >= 0) {
|
|
26
|
+
return "authentication_error";
|
|
27
|
+
}
|
|
28
|
+
// Authorization errors
|
|
29
|
+
if (errorCode === "AccessDenied" ||
|
|
30
|
+
errorCode === "AccountProblem" ||
|
|
31
|
+
errorCode === "Forbidden" ||
|
|
32
|
+
errorName === "AuthorizationError" ||
|
|
33
|
+
errorMessage.indexOf("forbidden") >= 0 ||
|
|
34
|
+
errorMessage.indexOf("permission") >= 0) {
|
|
35
|
+
return "authorization_error";
|
|
36
|
+
}
|
|
37
|
+
// Throttling errors
|
|
38
|
+
if (errorCode === "SlowDown" ||
|
|
39
|
+
errorCode === "RequestTimeTooSkewed" ||
|
|
40
|
+
errorCode === "TooManyRequests" ||
|
|
41
|
+
errorName === "ThrottlingError" ||
|
|
42
|
+
errorMessage.indexOf("throttl") >= 0 ||
|
|
43
|
+
errorMessage.indexOf("rate limit") >= 0) {
|
|
44
|
+
return "throttling_error";
|
|
45
|
+
}
|
|
46
|
+
// Server errors
|
|
47
|
+
if (errorCode === "InternalError" ||
|
|
48
|
+
errorCode === "ServiceUnavailable" ||
|
|
49
|
+
errorCode === "InternalServerError" ||
|
|
50
|
+
errorName === "ServerError" ||
|
|
51
|
+
errorMessage.indexOf("server error") >= 0 ||
|
|
52
|
+
errorMessage.indexOf("service unavailable") >= 0) {
|
|
53
|
+
return "server_error";
|
|
54
|
+
}
|
|
55
|
+
// Client errors
|
|
56
|
+
if (errorCode === "InvalidRequest" ||
|
|
57
|
+
errorCode === "MalformedXML" ||
|
|
58
|
+
errorCode === "RequestEntityTooLarge" ||
|
|
59
|
+
errorCode === "BadRequest" ||
|
|
60
|
+
errorName === "ClientError" ||
|
|
61
|
+
errorMessage.indexOf("bad request") >= 0 ||
|
|
62
|
+
errorMessage.indexOf("invalid") >= 0) {
|
|
63
|
+
return "client_error";
|
|
64
|
+
}
|
|
65
|
+
return "unknown_error";
|
|
66
|
+
};
|
|
67
|
+
// Storage-specific error classifier factory
|
|
68
|
+
export const createStorageErrorClassifier = (storageType, customErrorMapping) => {
|
|
69
|
+
return (error) => {
|
|
70
|
+
// Try custom mapping first
|
|
71
|
+
if (customErrorMapping) {
|
|
72
|
+
const customResult = customErrorMapping(error);
|
|
73
|
+
if (customResult !== null)
|
|
74
|
+
return customResult;
|
|
75
|
+
}
|
|
76
|
+
// Fall back to generic classification
|
|
77
|
+
return classifyStorageError(error);
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
// Generic error tracking function
|
|
81
|
+
export const trackStorageError = (storageType, metrics, operation, error, context = {}, errorClassifier = classifyStorageError) => Effect.gen(function* () {
|
|
82
|
+
const errorCategory = errorClassifier(error);
|
|
83
|
+
// Record error metrics
|
|
84
|
+
const errorMetric = metrics.uploadErrorsTotal.pipe(Metric.tagged("operation", operation), Metric.tagged("error_category", errorCategory));
|
|
85
|
+
yield* errorMetric(Effect.succeed(1));
|
|
86
|
+
// Create detailed error context
|
|
87
|
+
const errorDetails = {
|
|
88
|
+
storage_type: storageType,
|
|
89
|
+
operation,
|
|
90
|
+
error_category: errorCategory,
|
|
91
|
+
error_type: typeof error,
|
|
92
|
+
error_message: error instanceof Error ? error.message : String(error),
|
|
93
|
+
error_code: error && typeof error === "object" && "code" in error
|
|
94
|
+
? error.code
|
|
95
|
+
: undefined,
|
|
96
|
+
error_name: error && typeof error === "object" && "name" in error
|
|
97
|
+
? error.name
|
|
98
|
+
: undefined,
|
|
99
|
+
...context,
|
|
100
|
+
};
|
|
101
|
+
// Log structured error
|
|
102
|
+
yield* Effect.logError(`${storageType.toUpperCase()} ${operation} failed`).pipe(Effect.annotateLogs(errorDetails));
|
|
103
|
+
});
|
|
104
|
+
// Factory for storage-specific error tracking
|
|
105
|
+
export const createStorageErrorTracker = (storageType, metrics, customErrorClassifier) => {
|
|
106
|
+
const errorClassifier = createStorageErrorClassifier(storageType, customErrorClassifier);
|
|
107
|
+
return (operation, error, context = {}) => trackStorageError(storageType, metrics, operation, error, context, errorClassifier);
|
|
108
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAEA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Context, Effect, Layer, Option } from "effect";
|
|
2
|
+
import type { StorageMetrics } from "./metrics.js";
|
|
3
|
+
/**
|
|
4
|
+
* Core observability service providing tracing, metrics, and logging capabilities
|
|
5
|
+
*/
|
|
6
|
+
export interface ObservabilityService {
|
|
7
|
+
readonly serviceName: string;
|
|
8
|
+
readonly enabled: boolean;
|
|
9
|
+
}
|
|
10
|
+
declare const Observability_base: Context.TagClass<Observability, "Observability", ObservabilityService>;
|
|
11
|
+
/**
|
|
12
|
+
* Observability service tag for Effect Context
|
|
13
|
+
*/
|
|
14
|
+
export declare class Observability extends Observability_base {
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Storage observability service extending base observability with storage-specific metrics
|
|
18
|
+
*/
|
|
19
|
+
export interface StorageObservabilityService extends ObservabilityService {
|
|
20
|
+
readonly storageType: string;
|
|
21
|
+
readonly metrics: StorageMetrics;
|
|
22
|
+
}
|
|
23
|
+
declare const StorageObservability_base: Context.TagClass<StorageObservability, "StorageObservability", StorageObservabilityService>;
|
|
24
|
+
/**
|
|
25
|
+
* Storage observability service tag
|
|
26
|
+
*/
|
|
27
|
+
export declare class StorageObservability extends StorageObservability_base {
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Upload observability service for upload-specific operations
|
|
31
|
+
*/
|
|
32
|
+
export interface UploadObservabilityService extends ObservabilityService {
|
|
33
|
+
readonly metrics: {
|
|
34
|
+
uploadCreated: Effect.Effect<void>;
|
|
35
|
+
uploadCompleted: Effect.Effect<void>;
|
|
36
|
+
uploadFailed: Effect.Effect<void>;
|
|
37
|
+
chunkUploaded: Effect.Effect<void>;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
declare const UploadObservability_base: Context.TagClass<UploadObservability, "UploadObservability", UploadObservabilityService>;
|
|
41
|
+
/**
|
|
42
|
+
* Upload observability service tag
|
|
43
|
+
*/
|
|
44
|
+
export declare class UploadObservability extends UploadObservability_base {
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Flow observability service for flow execution operations
|
|
48
|
+
*/
|
|
49
|
+
export interface FlowObservabilityService extends ObservabilityService {
|
|
50
|
+
readonly metrics: {
|
|
51
|
+
flowStarted: Effect.Effect<void>;
|
|
52
|
+
flowCompleted: Effect.Effect<void>;
|
|
53
|
+
flowFailed: Effect.Effect<void>;
|
|
54
|
+
nodeExecuted: Effect.Effect<void>;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
declare const FlowObservability_base: Context.TagClass<FlowObservability, "FlowObservability", FlowObservabilityService>;
|
|
58
|
+
/**
|
|
59
|
+
* Flow observability service tag
|
|
60
|
+
*/
|
|
61
|
+
export declare class FlowObservability extends FlowObservability_base {
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Create a base observability layer
|
|
65
|
+
*/
|
|
66
|
+
export declare const makeObservabilityLayer: (serviceName: string, enabled?: boolean) => Layer.Layer<Observability>;
|
|
67
|
+
/**
|
|
68
|
+
* Create a storage observability layer
|
|
69
|
+
*/
|
|
70
|
+
export declare const makeStorageObservabilityLayer: (storageType: string, metrics: StorageMetrics, enabled?: boolean) => Layer.Layer<StorageObservability>;
|
|
71
|
+
/**
|
|
72
|
+
* Create an upload observability layer
|
|
73
|
+
*/
|
|
74
|
+
export declare const makeUploadObservabilityLayer: (enabled?: boolean) => Layer.Layer<UploadObservability>;
|
|
75
|
+
/**
|
|
76
|
+
* Create a flow observability layer
|
|
77
|
+
*/
|
|
78
|
+
export declare const makeFlowObservabilityLayer: (enabled?: boolean) => Layer.Layer<FlowObservability>;
|
|
79
|
+
/**
|
|
80
|
+
* No-op observability layer (disabled)
|
|
81
|
+
*/
|
|
82
|
+
export declare const ObservabilityDisabled: Layer.Layer<Observability, never, never>;
|
|
83
|
+
/**
|
|
84
|
+
* No-op storage observability layer
|
|
85
|
+
*/
|
|
86
|
+
export declare const StorageObservabilityDisabled: (storageType: string) => Layer.Layer<StorageObservability, never, never>;
|
|
87
|
+
/**
|
|
88
|
+
* No-op upload observability layer
|
|
89
|
+
*/
|
|
90
|
+
export declare const UploadObservabilityDisabled: Layer.Layer<UploadObservability, never, never>;
|
|
91
|
+
/**
|
|
92
|
+
* No-op flow observability layer
|
|
93
|
+
*/
|
|
94
|
+
export declare const FlowObservabilityDisabled: Layer.Layer<FlowObservability, never, never>;
|
|
95
|
+
/**
|
|
96
|
+
* Check if observability is enabled in the current context
|
|
97
|
+
*/
|
|
98
|
+
export declare const isObservabilityEnabled: Effect.Effect<boolean, never, never>;
|
|
99
|
+
/**
|
|
100
|
+
* Execute an effect only if observability is enabled
|
|
101
|
+
*/
|
|
102
|
+
export declare const whenObservabilityEnabled: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<Option.Option<A>, E, R | Observability>;
|
|
103
|
+
export {};
|
|
104
|
+
//# sourceMappingURL=layers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layers.d.ts","sourceRoot":"","sources":["../../src/core/layers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAMnD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;;AAED;;GAEG;AACH,qBAAa,aAAc,SAAQ,kBAGhC;CAAG;AAEN;;GAEG;AACH,MAAM,WAAW,2BAA4B,SAAQ,oBAAoB;IACvE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;CAClC;;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,yBAGvC;CAAG;AAEN;;GAEG;AACH,MAAM,WAAW,0BAA2B,SAAQ,oBAAoB;IACtE,QAAQ,CAAC,OAAO,EAAE;QAChB,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnC,eAAe,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACrC,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAClC,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;KACpC,CAAC;CACH;;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,wBAGtC;CAAG;AAEN;;GAEG;AACH,MAAM,WAAW,wBAAyB,SAAQ,oBAAoB;IACpE,QAAQ,CAAC,OAAO,EAAE;QAChB,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACjC,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnC,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAChC,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;KACnC,CAAC;CACH;;AAED;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,sBAGpC;CAAG;AAMN;;GAEG;AACH,eAAO,MAAM,sBAAsB,GACjC,aAAa,MAAM,EACnB,iBAAc,KACb,KAAK,CAAC,KAAK,CAAC,aAAa,CAIxB,CAAC;AAEL;;GAEG;AACH,eAAO,MAAM,6BAA6B,GACxC,aAAa,MAAM,EACnB,SAAS,cAAc,EACvB,iBAAc,KACb,KAAK,CAAC,KAAK,CAAC,oBAAoB,CAM/B,CAAC;AAEL;;GAEG;AACH,eAAO,MAAM,4BAA4B,GACvC,iBAAc,KACb,KAAK,CAAC,KAAK,CAAC,mBAAmB,CAU9B,CAAC;AAEL;;GAEG;AACH,eAAO,MAAM,0BAA0B,GACrC,iBAAc,KACb,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAU5B,CAAC;AAML;;GAEG;AACH,eAAO,MAAM,qBAAqB,0CAGjC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,4BAA4B,GAAI,aAAa,MAAM,oDAK7D,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,2BAA2B,gDAAsC,CAAC;AAE/E;;GAEG;AACH,eAAO,MAAM,yBAAyB,8CAAoC,CAAC;AAM3E;;GAEG;AACH,eAAO,MAAM,sBAAsB,sCAMjC,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,wBAAwB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAC9C,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC7B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,aAAa,CAQnD,CAAC"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Context, Effect, Layer, Option } from "effect";
|
|
2
|
+
/**
|
|
3
|
+
* Observability service tag for Effect Context
|
|
4
|
+
*/
|
|
5
|
+
export class Observability extends Context.Tag("Observability")() {
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Storage observability service tag
|
|
9
|
+
*/
|
|
10
|
+
export class StorageObservability extends Context.Tag("StorageObservability")() {
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Upload observability service tag
|
|
14
|
+
*/
|
|
15
|
+
export class UploadObservability extends Context.Tag("UploadObservability")() {
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Flow observability service tag
|
|
19
|
+
*/
|
|
20
|
+
export class FlowObservability extends Context.Tag("FlowObservability")() {
|
|
21
|
+
}
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// Layer Factories
|
|
24
|
+
// ============================================================================
|
|
25
|
+
/**
|
|
26
|
+
* Create a base observability layer
|
|
27
|
+
*/
|
|
28
|
+
export const makeObservabilityLayer = (serviceName, enabled = true) => Layer.succeed(Observability, {
|
|
29
|
+
serviceName,
|
|
30
|
+
enabled,
|
|
31
|
+
});
|
|
32
|
+
/**
|
|
33
|
+
* Create a storage observability layer
|
|
34
|
+
*/
|
|
35
|
+
export const makeStorageObservabilityLayer = (storageType, metrics, enabled = true) => Layer.succeed(StorageObservability, {
|
|
36
|
+
serviceName: `uploadista-${storageType}-store`,
|
|
37
|
+
storageType,
|
|
38
|
+
metrics,
|
|
39
|
+
enabled,
|
|
40
|
+
});
|
|
41
|
+
/**
|
|
42
|
+
* Create an upload observability layer
|
|
43
|
+
*/
|
|
44
|
+
export const makeUploadObservabilityLayer = (enabled = true) => Layer.succeed(UploadObservability, {
|
|
45
|
+
serviceName: "uploadista-upload-server",
|
|
46
|
+
enabled,
|
|
47
|
+
metrics: {
|
|
48
|
+
uploadCreated: Effect.void,
|
|
49
|
+
uploadCompleted: Effect.void,
|
|
50
|
+
uploadFailed: Effect.void,
|
|
51
|
+
chunkUploaded: Effect.void,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* Create a flow observability layer
|
|
56
|
+
*/
|
|
57
|
+
export const makeFlowObservabilityLayer = (enabled = true) => Layer.succeed(FlowObservability, {
|
|
58
|
+
serviceName: "uploadista-flow-engine",
|
|
59
|
+
enabled,
|
|
60
|
+
metrics: {
|
|
61
|
+
flowStarted: Effect.void,
|
|
62
|
+
flowCompleted: Effect.void,
|
|
63
|
+
flowFailed: Effect.void,
|
|
64
|
+
nodeExecuted: Effect.void,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
// ============================================================================
|
|
68
|
+
// No-op Layers (for testing and opt-out)
|
|
69
|
+
// ============================================================================
|
|
70
|
+
/**
|
|
71
|
+
* No-op observability layer (disabled)
|
|
72
|
+
*/
|
|
73
|
+
export const ObservabilityDisabled = makeObservabilityLayer("uploadista-disabled", false);
|
|
74
|
+
/**
|
|
75
|
+
* No-op storage observability layer
|
|
76
|
+
*/
|
|
77
|
+
export const StorageObservabilityDisabled = (storageType) => makeStorageObservabilityLayer(storageType, {}, // No-op metrics
|
|
78
|
+
false);
|
|
79
|
+
/**
|
|
80
|
+
* No-op upload observability layer
|
|
81
|
+
*/
|
|
82
|
+
export const UploadObservabilityDisabled = makeUploadObservabilityLayer(false);
|
|
83
|
+
/**
|
|
84
|
+
* No-op flow observability layer
|
|
85
|
+
*/
|
|
86
|
+
export const FlowObservabilityDisabled = makeFlowObservabilityLayer(false);
|
|
87
|
+
// ============================================================================
|
|
88
|
+
// Helper Functions
|
|
89
|
+
// ============================================================================
|
|
90
|
+
/**
|
|
91
|
+
* Check if observability is enabled in the current context
|
|
92
|
+
*/
|
|
93
|
+
export const isObservabilityEnabled = Effect.gen(function* () {
|
|
94
|
+
const observability = yield* Effect.serviceOption(Observability);
|
|
95
|
+
return Option.match(observability, {
|
|
96
|
+
onNone: () => false,
|
|
97
|
+
onSome: (obs) => obs.enabled,
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
/**
|
|
101
|
+
* Execute an effect only if observability is enabled
|
|
102
|
+
*/
|
|
103
|
+
export const whenObservabilityEnabled = (effect) => Effect.gen(function* () {
|
|
104
|
+
const enabled = yield* isObservabilityEnabled;
|
|
105
|
+
if (enabled) {
|
|
106
|
+
const result = yield* effect;
|
|
107
|
+
return Option.some(result);
|
|
108
|
+
}
|
|
109
|
+
return Option.none();
|
|
110
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
export declare const logWithContext: (message: string, context: Record<string, unknown>) => Effect.Effect<void, never, never>;
|
|
3
|
+
export declare const logUploadProgress: (storageType: string, uploadId: string, progress: {
|
|
4
|
+
uploadedBytes: number;
|
|
5
|
+
totalBytes: number;
|
|
6
|
+
partNumber?: number;
|
|
7
|
+
speed?: number;
|
|
8
|
+
}) => Effect.Effect<void, never, never>;
|
|
9
|
+
export declare const logStorageOperation: (storageType: string, operation: string, uploadId: string, metadata?: Record<string, unknown>) => Effect.Effect<void, never, never>;
|
|
10
|
+
export declare const logUploadCompletion: (storageType: string, uploadId: string, metrics: {
|
|
11
|
+
fileSize: number;
|
|
12
|
+
totalDurationMs: number;
|
|
13
|
+
partsCount?: number;
|
|
14
|
+
averagePartSize?: number;
|
|
15
|
+
throughputBps?: number;
|
|
16
|
+
retryCount?: number;
|
|
17
|
+
}) => Effect.Effect<void, never, never>;
|
|
18
|
+
//# sourceMappingURL=logging.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logging.d.ts","sourceRoot":"","sources":["../../src/core/logging.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAMhC,eAAO,MAAM,cAAc,GACzB,SAAS,MAAM,EACf,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,sCACyB,CAAC;AAE5D,eAAO,MAAM,iBAAiB,GAC5B,aAAa,MAAM,EACnB,UAAU,MAAM,EAChB,UAAU;IACR,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,sCAYC,CAAC;AAEL,eAAO,MAAM,mBAAmB,GAC9B,aAAa,MAAM,EACnB,WAAW,MAAM,EACjB,UAAU,MAAM,EAChB,WAAW,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,sCAOhC,CAAC;AAEL,eAAO,MAAM,mBAAmB,GAC9B,aAAa,MAAM,EACnB,UAAU,MAAM,EAChB,SAAS;IACP,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,sCAwBF,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// Enhanced Logging Helpers (Storage-agnostic)
|
|
4
|
+
// ============================================================================
|
|
5
|
+
export const logWithContext = (message, context) => Effect.log(message).pipe(Effect.annotateLogs(context));
|
|
6
|
+
export const logUploadProgress = (storageType, uploadId, progress) => logWithContext("Upload progress", {
|
|
7
|
+
storage_type: storageType,
|
|
8
|
+
upload_id: uploadId,
|
|
9
|
+
uploaded_bytes: progress.uploadedBytes,
|
|
10
|
+
total_bytes: progress.totalBytes,
|
|
11
|
+
progress_percentage: Math.round((progress.uploadedBytes / progress.totalBytes) * 100),
|
|
12
|
+
...(progress.partNumber && { part_number: progress.partNumber }),
|
|
13
|
+
...(progress.speed && { upload_speed_bps: progress.speed }),
|
|
14
|
+
});
|
|
15
|
+
export const logStorageOperation = (storageType, operation, uploadId, metadata) => logWithContext(`${storageType.toUpperCase()} ${operation}`, {
|
|
16
|
+
storage_type: storageType,
|
|
17
|
+
operation,
|
|
18
|
+
upload_id: uploadId,
|
|
19
|
+
...metadata,
|
|
20
|
+
});
|
|
21
|
+
export const logUploadCompletion = (storageType, uploadId, metrics) => {
|
|
22
|
+
const throughputMBps = metrics.throughputBps
|
|
23
|
+
? metrics.throughputBps / (1024 * 1024)
|
|
24
|
+
: 0;
|
|
25
|
+
return logWithContext(`${storageType.toUpperCase()} upload completed`, {
|
|
26
|
+
storage_type: storageType,
|
|
27
|
+
upload_id: uploadId,
|
|
28
|
+
file_size_bytes: metrics.fileSize,
|
|
29
|
+
file_size_mb: Math.round((metrics.fileSize / (1024 * 1024)) * 100) / 100,
|
|
30
|
+
total_duration_ms: metrics.totalDurationMs,
|
|
31
|
+
total_duration_seconds: Math.round((metrics.totalDurationMs / 1000) * 100) / 100,
|
|
32
|
+
throughput_bps: metrics.throughputBps,
|
|
33
|
+
throughput_mbps: Math.round(throughputMBps * 100) / 100,
|
|
34
|
+
...(metrics.partsCount && { parts_count: metrics.partsCount }),
|
|
35
|
+
...(metrics.averagePartSize && {
|
|
36
|
+
average_part_size_bytes: metrics.averagePartSize,
|
|
37
|
+
average_part_size_mb: Math.round((metrics.averagePartSize / (1024 * 1024)) * 100) / 100,
|
|
38
|
+
}),
|
|
39
|
+
...(metrics.retryCount && { retry_count: metrics.retryCount }),
|
|
40
|
+
});
|
|
41
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Metric } from "effect";
|
|
2
|
+
export declare const createUploadMetrics: (storageType: string) => {
|
|
3
|
+
uploadRequestsTotal: Metric.Metric.Counter<number>;
|
|
4
|
+
uploadPartsTotal: Metric.Metric.Counter<number>;
|
|
5
|
+
uploadSuccessTotal: Metric.Metric.Counter<number>;
|
|
6
|
+
uploadErrorsTotal: Metric.Metric.Counter<number>;
|
|
7
|
+
apiCallsTotal: Metric.Metric.Counter<number>;
|
|
8
|
+
};
|
|
9
|
+
export declare const createUploadHistograms: (storageType: string) => {
|
|
10
|
+
uploadDurationHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
11
|
+
partUploadDurationHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
12
|
+
fileSizeHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
13
|
+
partSizeHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
14
|
+
};
|
|
15
|
+
export declare const createUploadGauges: (storageType: string) => {
|
|
16
|
+
activeUploadsGauge: Metric.Metric.Gauge<number>;
|
|
17
|
+
uploadThroughputGauge: Metric.Metric.Gauge<number>;
|
|
18
|
+
};
|
|
19
|
+
export declare const createUploadSummaries: (storageType: string) => {
|
|
20
|
+
uploadLatencySummary: Metric.Metric.Summary<number>;
|
|
21
|
+
};
|
|
22
|
+
export declare const createStorageMetrics: (storageType: string) => {
|
|
23
|
+
uploadLatencySummary: Metric.Metric.Summary<number>;
|
|
24
|
+
activeUploadsGauge: Metric.Metric.Gauge<number>;
|
|
25
|
+
uploadThroughputGauge: Metric.Metric.Gauge<number>;
|
|
26
|
+
uploadDurationHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
27
|
+
partUploadDurationHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
28
|
+
fileSizeHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
29
|
+
partSizeHistogram: Metric.Metric<import("effect/MetricKeyType").MetricKeyType.Histogram, number, import("effect/MetricState").MetricState.Histogram>;
|
|
30
|
+
uploadRequestsTotal: Metric.Metric.Counter<number>;
|
|
31
|
+
uploadPartsTotal: Metric.Metric.Counter<number>;
|
|
32
|
+
uploadSuccessTotal: Metric.Metric.Counter<number>;
|
|
33
|
+
uploadErrorsTotal: Metric.Metric.Counter<number>;
|
|
34
|
+
apiCallsTotal: Metric.Metric.Counter<number>;
|
|
35
|
+
};
|
|
36
|
+
export type StorageMetrics = ReturnType<typeof createStorageMetrics>;
|
|
37
|
+
//# sourceMappingURL=metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/core/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAoB,MAAM,QAAQ,CAAC;AAOlD,eAAO,MAAM,mBAAmB,GAAI,aAAa,MAAM;;;;;;CAoBrD,CAAC;AAGH,eAAO,MAAM,sBAAsB,GAAI,aAAa,MAAM;;;;;CAwCxD,CAAC;AAGH,eAAO,MAAM,kBAAkB,GAAI,aAAa,MAAM;;;CAWpD,CAAC;AAGH,eAAO,MAAM,qBAAqB,GAAI,aAAa,MAAM;;CASvD,CAAC;AAGH,eAAO,MAAM,oBAAoB,GAAI,aAAa,MAAM;;;;;;;;;;;;;CAKtD,CAAC;AAGH,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Metric, MetricBoundaries } from "effect";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// Core Storage Metrics (reusable across all storage types)
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// Counter metrics
|
|
6
|
+
export const createUploadMetrics = (storageType) => ({
|
|
7
|
+
uploadRequestsTotal: Metric.counter(`${storageType}_upload_requests_total`, {
|
|
8
|
+
description: `Total number of upload requests for ${storageType}`,
|
|
9
|
+
}),
|
|
10
|
+
uploadPartsTotal: Metric.counter(`${storageType}_upload_parts_total`, {
|
|
11
|
+
description: `Total number of individual parts uploaded for ${storageType}`,
|
|
12
|
+
}),
|
|
13
|
+
uploadSuccessTotal: Metric.counter(`${storageType}_upload_success_total`, {
|
|
14
|
+
description: `Total number of successful uploads for ${storageType}`,
|
|
15
|
+
}),
|
|
16
|
+
uploadErrorsTotal: Metric.counter(`${storageType}_upload_errors_total`, {
|
|
17
|
+
description: `Total number of upload errors for ${storageType}`,
|
|
18
|
+
}),
|
|
19
|
+
apiCallsTotal: Metric.counter(`${storageType}_api_calls_total`, {
|
|
20
|
+
description: `Total number of API calls for ${storageType}`,
|
|
21
|
+
}),
|
|
22
|
+
});
|
|
23
|
+
// Histogram metrics for timing and sizes (reusable)
|
|
24
|
+
export const createUploadHistograms = (storageType) => ({
|
|
25
|
+
uploadDurationHistogram: Metric.histogram(`${storageType}_upload_duration_seconds`, MetricBoundaries.exponential({
|
|
26
|
+
start: 0.01, // 10ms
|
|
27
|
+
factor: 2,
|
|
28
|
+
count: 20, // Up to ~10 seconds
|
|
29
|
+
}), `Duration of upload operations in seconds for ${storageType}`),
|
|
30
|
+
partUploadDurationHistogram: Metric.histogram(`${storageType}_part_upload_duration_seconds`, MetricBoundaries.exponential({
|
|
31
|
+
start: 0.001, // 1ms
|
|
32
|
+
factor: 2,
|
|
33
|
+
count: 15, // Up to ~32 seconds
|
|
34
|
+
}), `Duration of individual part uploads in seconds for ${storageType}`),
|
|
35
|
+
fileSizeHistogram: Metric.histogram(`${storageType}_file_size_bytes`, MetricBoundaries.exponential({
|
|
36
|
+
start: 1024, // 1KB
|
|
37
|
+
factor: 2,
|
|
38
|
+
count: 25, // Up to ~33GB
|
|
39
|
+
}), `Size of uploaded files in bytes for ${storageType}`),
|
|
40
|
+
partSizeHistogram: Metric.histogram(`${storageType}_part_size_bytes`, MetricBoundaries.linear({
|
|
41
|
+
start: 5_242_880, // 5MB (minimum part size)
|
|
42
|
+
width: 1_048_576, // 1MB increments
|
|
43
|
+
count: 20, // Up to ~25MB
|
|
44
|
+
}), `Size of upload parts in bytes for ${storageType}`),
|
|
45
|
+
});
|
|
46
|
+
// Gauge metrics for current state (reusable)
|
|
47
|
+
export const createUploadGauges = (storageType) => ({
|
|
48
|
+
activeUploadsGauge: Metric.gauge(`${storageType}_active_uploads`, {
|
|
49
|
+
description: `Number of currently active uploads for ${storageType}`,
|
|
50
|
+
}),
|
|
51
|
+
uploadThroughputGauge: Metric.gauge(`${storageType}_upload_throughput_bytes_per_second`, {
|
|
52
|
+
description: `Current upload throughput in bytes per second for ${storageType}`,
|
|
53
|
+
}),
|
|
54
|
+
});
|
|
55
|
+
// Summary metrics for percentiles (reusable)
|
|
56
|
+
export const createUploadSummaries = (storageType) => ({
|
|
57
|
+
uploadLatencySummary: Metric.summary({
|
|
58
|
+
name: `${storageType}_upload_latency_seconds`,
|
|
59
|
+
maxAge: "10 minutes",
|
|
60
|
+
maxSize: 1000,
|
|
61
|
+
error: 0.01,
|
|
62
|
+
quantiles: [0.5, 0.9, 0.95, 0.99],
|
|
63
|
+
description: `Upload latency percentiles for ${storageType}`,
|
|
64
|
+
}),
|
|
65
|
+
});
|
|
66
|
+
// Combined metrics factory
|
|
67
|
+
export const createStorageMetrics = (storageType) => ({
|
|
68
|
+
...createUploadMetrics(storageType),
|
|
69
|
+
...createUploadHistograms(storageType),
|
|
70
|
+
...createUploadGauges(storageType),
|
|
71
|
+
...createUploadSummaries(storageType),
|
|
72
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Effect, Layer } from "effect";
|
|
2
|
+
import { FlowObservability, StorageObservability, UploadObservability } from "./layers.js";
|
|
3
|
+
/**
|
|
4
|
+
* Mock storage observability for testing
|
|
5
|
+
*/
|
|
6
|
+
export declare const makeTestStorageObservability: (storageType: string) => Layer.Layer<StorageObservability>;
|
|
7
|
+
/**
|
|
8
|
+
* Mock upload observability for testing
|
|
9
|
+
*/
|
|
10
|
+
export declare const makeTestUploadObservability: () => Layer.Layer<UploadObservability>;
|
|
11
|
+
/**
|
|
12
|
+
* Mock flow observability for testing
|
|
13
|
+
*/
|
|
14
|
+
export declare const makeTestFlowObservability: () => Layer.Layer<FlowObservability>;
|
|
15
|
+
/**
|
|
16
|
+
* Capture metrics snapshot from an effect for testing
|
|
17
|
+
* Note: Metric snapshots are simplified - for full metric testing,
|
|
18
|
+
* use Effect's built-in metric testing utilities
|
|
19
|
+
*/
|
|
20
|
+
export declare const captureMetrics: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
21
|
+
/**
|
|
22
|
+
* Test helper to capture metrics around effect execution
|
|
23
|
+
* This is a simplified version - for production testing,
|
|
24
|
+
* use Effect's metric testing utilities
|
|
25
|
+
*/
|
|
26
|
+
export declare const withMetricTracking: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
27
|
+
/**
|
|
28
|
+
* Test fixture for observability testing
|
|
29
|
+
*/
|
|
30
|
+
export interface ObservabilityTestFixture {
|
|
31
|
+
readonly storageObservability: Layer.Layer<StorageObservability>;
|
|
32
|
+
readonly uploadObservability: Layer.Layer<UploadObservability>;
|
|
33
|
+
readonly flowObservability: Layer.Layer<FlowObservability>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Create a complete test fixture with all observability layers
|
|
37
|
+
*/
|
|
38
|
+
export declare const createTestFixture: (storageType?: string) => ObservabilityTestFixture;
|
|
39
|
+
/**
|
|
40
|
+
* Run an effect with test observability layers
|
|
41
|
+
*/
|
|
42
|
+
export declare const runWithTestObservability: <A, E>(effect: Effect.Effect<A, E, StorageObservability | UploadObservability | FlowObservability>, storageType?: string) => Effect.Effect<A, E>;
|
|
43
|
+
//# sourceMappingURL=testing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../src/core/testing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAU,MAAM,QAAQ,CAAC;AAM/C,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAOrB;;GAEG;AACH,eAAO,MAAM,4BAA4B,GACvC,aAAa,MAAM,KAClB,KAAK,CAAC,KAAK,CAAC,oBAAoB,CASlC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,2BAA2B,QAClC,KAAK,CAAC,KAAK,CAAC,mBAAmB,CAYlC,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,yBAAyB,QAAO,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAYzE,CAAC;AAMF;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EACpC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAMpB,CAAC;AAEL;;;;GAIG;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,CAQpB,CAAC;AAEL;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,oBAAoB,EAAE,KAAK,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;IACjE,QAAQ,CAAC,mBAAmB,EAAE,KAAK,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAC/D,QAAQ,CAAC,iBAAiB,EAAE,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;CAC5D;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAC5B,oBAA4B,KAC3B,wBAID,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,wBAAwB,GAAI,CAAC,EAAE,CAAC,EAC3C,QAAQ,MAAM,CAAC,MAAM,CACnB,CAAC,EACD,CAAC,EACD,oBAAoB,GAAG,mBAAmB,GAAG,iBAAiB,CAC/D,EACD,oBAA4B,KAC3B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAOpB,CAAC"}
|