@zenvark/prom 1.2.0 → 1.3.0

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @zenvark/prom
2
2
 
3
- Prometheus metrics integration for [Zenvark Circuit Breaker](https://github.com/zenvark/zenvark).
3
+ Prometheus metrics integration for the [Zenvark](https://www.npmjs.com/package/zenvark) circuit breaker and adaptive semaphore.
4
4
 
5
5
  ## Installation
6
6
 
@@ -40,19 +40,56 @@ const circuitBreaker = new CircuitBreaker({
40
40
  await circuitBreaker.start();
41
41
  ```
42
42
 
43
+ For the adaptive semaphore, pass `PrometheusSemaphoreMetrics` the same way:
44
+
45
+ ```typescript
46
+ import { AdaptiveSemaphore } from "zenvark";
47
+ import { PrometheusSemaphoreMetrics } from "@zenvark/prom";
48
+ import { register } from "prom-client";
49
+
50
+ const semaphore = new AdaptiveSemaphore({
51
+ id: "my-provider-api",
52
+ redis,
53
+ initialLimit: 10,
54
+ maxLimit: 1000,
55
+ metrics: new PrometheusSemaphoreMetrics({
56
+ registry: register,
57
+ customLabels: { service: "my-api", environment: "production" },
58
+ }),
59
+ });
60
+ ```
61
+
43
62
  ## Available Metrics
44
63
 
45
- | Metric | Type | Description | Labels |
46
- | -------------------------------------- | --------- | -------------------------------- | ------------------------------ |
47
- | `zenvark_call_duration_seconds` | Histogram | Duration of protected calls | `breaker_id`, `result` |
48
- | `zenvark_blocked_requests_total` | Counter | Requests blocked by open circuit | `breaker_id` |
49
- | `zenvark_healthcheck_duration_seconds` | Histogram | Health check attempt duration | `breaker_id`, `type`, `result` |
64
+ ### Circuit breaker
65
+
66
+ | Metric | Type | Description | Labels |
67
+ | -------------------------------------- | --------- | -------------------------------------------- | ------------------------------ |
68
+ | `zenvark_call_duration_seconds` | Histogram | Duration of protected calls | `breaker_id`, `result` |
69
+ | `zenvark_blocked_requests_total` | Counter | Requests blocked by open circuit | `breaker_id` |
70
+ | `zenvark_healthcheck_duration_seconds` | Histogram | Health check attempt duration | `breaker_id`, `type`, `result` |
71
+ | `zenvark_state` | Gauge | Circuit state (active state 1, all others 0) | `breaker_id`, `state` |
72
+
73
+ ### Semaphore
74
+
75
+ | Metric | Type | Description | Labels |
76
+ | ----------------------------------------- | --------- | ------------------------------------------- | ---------------------------------- |
77
+ | `zenvark_semaphore_acquire_wait_seconds` | Histogram | Time spent waiting for a lease | `semaphore_id`, `class`, `result` |
78
+ | `zenvark_semaphore_hold_duration_seconds` | Histogram | Time a lease was held | `semaphore_id`, `class`, `outcome` |
79
+ | `zenvark_semaphore_limit` | Gauge | This process's view of the fleet-wide limit | `semaphore_id` |
80
+ | `zenvark_semaphore_throttle_events_total` | Counter | Caller-reported throttle events | `semaphore_id` |
81
+
82
+ The limit gauge is set on every process whose cached view changes, so a scrape of any instance reads the current fleet-wide limit. Sum or average across instances accordingly.
50
83
 
51
84
  ### Label Values
52
85
 
53
86
  - `breaker_id` - Circuit breaker instance identifier
54
87
  - `type` - Health check type: `recovery` or `idle`
55
- - `result` - Outcome: `success` or `failure`
88
+ - `result` - Breaker call outcome (`success` or `failure`), or semaphore acquire outcome (`acquired` or `timeout`)
89
+ - `state` - Circuit state: `closed` or `open`
90
+ - `semaphore_id` - Semaphore instance identifier
91
+ - `class` - Semaphore priority class (empty when acquired without a class)
92
+ - `outcome` - Lease release outcome: `success`, `throttled` or `failure`
56
93
  - Custom labels - Additional key-value pairs from configuration
57
94
 
58
95
  ## Custom Labels
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { getOrCreateCounter, getOrCreateHistogram, } from './get-or-create-metric.ts';
2
2
  export { PrometheusBreakerMetrics, type PrometheusBreakerMetricsOptions, } from './prometheus-breaker-metrics.ts';
3
+ export { PrometheusSemaphoreMetrics, type PrometheusSemaphoreMetricsOptions, } from './prometheus-semaphore-metrics.ts';
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { getOrCreateCounter, getOrCreateHistogram, } from "./get-or-create-metric.js";
2
2
  export { PrometheusBreakerMetrics, } from "./prometheus-breaker-metrics.js";
3
+ export { PrometheusSemaphoreMetrics, } from "./prometheus-semaphore-metrics.js";
3
4
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,wBAAwB,GAEzB,MAAM,iCAAiC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,wBAAwB,GAEzB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,0BAA0B,GAE3B,MAAM,mCAAmC,CAAC"}
@@ -0,0 +1,46 @@
1
+ import type { Registry } from 'prom-client';
2
+ import type { RecordAcquireParams, RecordLimitChangeParams, RecordReleaseParams, RecordThrottleParams, SemaphoreMetricsRecorder } from 'zenvark';
3
+ export type PrometheusSemaphoreMetricsOptions = {
4
+ /**
5
+ * Prometheus registry instance
6
+ */
7
+ registry: Registry;
8
+ /**
9
+ * Prefix for all metric names (default: 'zenvark')
10
+ */
11
+ prefix?: string;
12
+ /**
13
+ * Custom labels to add to all metrics
14
+ */
15
+ customLabels?: Record<string, string>;
16
+ };
17
+ export declare class PrometheusSemaphoreMetrics implements SemaphoreMetricsRecorder {
18
+ private readonly customLabels;
19
+ private readonly acquireWaitHistogram;
20
+ private readonly holdDurationHistogram;
21
+ private readonly limitGauge;
22
+ private readonly throttleEventsCounter;
23
+ constructor(options: PrometheusSemaphoreMetricsOptions);
24
+ /**
25
+ * Initialize metrics for a semaphore.
26
+ * Sets the throttle counter to 0 so the metric appears in scrapes immediately.
27
+ */
28
+ initialize(id: string): void;
29
+ private getLabels;
30
+ /**
31
+ * Record the outcome of an acquire attempt and the time spent waiting
32
+ */
33
+ recordAcquire(params: RecordAcquireParams): void;
34
+ /**
35
+ * Record a lease release with its outcome and hold duration
36
+ */
37
+ recordRelease(params: RecordReleaseParams): void;
38
+ /**
39
+ * Record a change of the cached fleet-wide limit
40
+ */
41
+ recordLimitChange(params: RecordLimitChangeParams): void;
42
+ /**
43
+ * Record a caller-reported throttle event
44
+ */
45
+ recordThrottle(params: RecordThrottleParams): void;
46
+ }
@@ -0,0 +1,89 @@
1
+ import { getOrCreateCounter, getOrCreateGauge, getOrCreateHistogram, } from "./get-or-create-metric.js";
2
+ export class PrometheusSemaphoreMetrics {
3
+ customLabels;
4
+ acquireWaitHistogram;
5
+ holdDurationHistogram;
6
+ limitGauge;
7
+ throttleEventsCounter;
8
+ constructor(options) {
9
+ const prefix = options.prefix ?? 'zenvark';
10
+ this.customLabels = options.customLabels ?? {};
11
+ const customLabelNames = Object.keys(this.customLabels);
12
+ this.acquireWaitHistogram = getOrCreateHistogram(options.registry, {
13
+ name: `${prefix}_semaphore_acquire_wait_seconds`,
14
+ help: 'Time spent waiting for a semaphore lease in seconds, by acquire result.',
15
+ labelNames: ['semaphore_id', 'class', 'result', ...customLabelNames],
16
+ buckets: [0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10],
17
+ registers: [options.registry],
18
+ });
19
+ this.holdDurationHistogram = getOrCreateHistogram(options.registry, {
20
+ name: `${prefix}_semaphore_hold_duration_seconds`,
21
+ help: 'Time a semaphore lease was held in seconds, by release outcome.',
22
+ labelNames: ['semaphore_id', 'class', 'outcome', ...customLabelNames],
23
+ buckets: [0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60],
24
+ registers: [options.registry],
25
+ });
26
+ this.limitGauge = getOrCreateGauge(options.registry, {
27
+ name: `${prefix}_semaphore_limit`,
28
+ help: "This process's cached view of the fleet-wide semaphore limit.",
29
+ labelNames: ['semaphore_id', ...customLabelNames],
30
+ registers: [options.registry],
31
+ });
32
+ this.throttleEventsCounter = getOrCreateCounter(options.registry, {
33
+ name: `${prefix}_semaphore_throttle_events_total`,
34
+ help: 'Total number of THROTTLED releases reported by callers.',
35
+ labelNames: ['semaphore_id', ...customLabelNames],
36
+ registers: [options.registry],
37
+ });
38
+ }
39
+ /**
40
+ * Initialize metrics for a semaphore.
41
+ * Sets the throttle counter to 0 so the metric appears in scrapes immediately.
42
+ */
43
+ initialize(id) {
44
+ const labels = this.getLabels(id);
45
+ this.throttleEventsCounter.inc(labels, 0);
46
+ }
47
+ getLabels(semaphoreId, additionalLabels = {}) {
48
+ return {
49
+ ...this.customLabels,
50
+ semaphore_id: semaphoreId,
51
+ ...additionalLabels,
52
+ };
53
+ }
54
+ /**
55
+ * Record the outcome of an acquire attempt and the time spent waiting
56
+ */
57
+ recordAcquire(params) {
58
+ const labels = this.getLabels(params.id, {
59
+ class: params.class ?? '',
60
+ result: params.result,
61
+ });
62
+ this.acquireWaitHistogram.observe(labels, params.waitMs / 1000);
63
+ }
64
+ /**
65
+ * Record a lease release with its outcome and hold duration
66
+ */
67
+ recordRelease(params) {
68
+ const labels = this.getLabels(params.id, {
69
+ class: params.class ?? '',
70
+ outcome: params.outcome,
71
+ });
72
+ this.holdDurationHistogram.observe(labels, params.heldMs / 1000);
73
+ }
74
+ /**
75
+ * Record a change of the cached fleet-wide limit
76
+ */
77
+ recordLimitChange(params) {
78
+ const labels = this.getLabels(params.id);
79
+ this.limitGauge.set(labels, params.limit);
80
+ }
81
+ /**
82
+ * Record a caller-reported throttle event
83
+ */
84
+ recordThrottle(params) {
85
+ const labels = this.getLabels(params.id);
86
+ this.throttleEventsCounter.inc(labels, 1);
87
+ }
88
+ }
89
+ //# sourceMappingURL=prometheus-semaphore-metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prometheus-semaphore-metrics.js","sourceRoot":"","sources":["../src/prometheus-semaphore-metrics.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenvark/prom",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Prometheus metrics for Zenvark Circuit Breaker",
5
5
  "keywords": [
6
6
  "zenvark",
@@ -54,10 +54,10 @@
54
54
  "rimraf": "^6.0.1",
55
55
  "typescript": "5.9.3",
56
56
  "vitest": "^4.0.15",
57
- "zenvark": "^1.2.0"
57
+ "zenvark": "^1.3.0"
58
58
  },
59
59
  "peerDependencies": {
60
60
  "prom-client": "^15.0.0",
61
- "zenvark": "^1.0.0"
61
+ "zenvark": "^1.2.0"
62
62
  }
63
63
  }