@aztec/telemetry-client 0.0.1-commit.f2ce05ee → 0.0.1-commit.f5a9928

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.
Files changed (62) hide show
  1. package/dest/attributes.d.ts +22 -2
  2. package/dest/attributes.d.ts.map +1 -1
  3. package/dest/attributes.js +11 -1
  4. package/dest/config.d.ts +3 -1
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +17 -9
  7. package/dest/index.d.ts +2 -1
  8. package/dest/index.d.ts.map +1 -1
  9. package/dest/index.js +1 -0
  10. package/dest/json_rpc_server_metrics.d.ts +27 -0
  11. package/dest/json_rpc_server_metrics.d.ts.map +1 -0
  12. package/dest/json_rpc_server_metrics.js +154 -0
  13. package/dest/lmdb_metrics.d.ts +2 -2
  14. package/dest/lmdb_metrics.d.ts.map +1 -1
  15. package/dest/metrics.d.ts +84 -5
  16. package/dest/metrics.d.ts.map +1 -1
  17. package/dest/metrics.js +450 -19
  18. package/dest/monitored_batch_span_processor.d.ts +29 -0
  19. package/dest/monitored_batch_span_processor.d.ts.map +1 -0
  20. package/dest/monitored_batch_span_processor.js +75 -0
  21. package/dest/otel.d.ts +6 -1
  22. package/dest/otel.d.ts.map +1 -1
  23. package/dest/otel.js +99 -4
  24. package/dest/otel_propagation.d.ts +4 -1
  25. package/dest/otel_propagation.d.ts.map +1 -1
  26. package/dest/otel_propagation.js +64 -6
  27. package/dest/start.d.ts +1 -1
  28. package/dest/start.d.ts.map +1 -1
  29. package/dest/start.js +1 -1
  30. package/dest/telemetry.d.ts +5 -3
  31. package/dest/telemetry.d.ts.map +1 -1
  32. package/dest/vendor/attributes.d.ts +2 -1
  33. package/dest/vendor/attributes.d.ts.map +1 -1
  34. package/dest/vendor/attributes.js +1 -0
  35. package/dest/wrappers/fetch.d.ts +3 -3
  36. package/dest/wrappers/fetch.d.ts.map +1 -1
  37. package/dest/wrappers/fetch.js +3 -2
  38. package/dest/wrappers/index.d.ts +1 -2
  39. package/dest/wrappers/index.d.ts.map +1 -1
  40. package/dest/wrappers/index.js +0 -1
  41. package/dest/wrappers/json_rpc_server.d.ts +1 -1
  42. package/dest/wrappers/json_rpc_server.d.ts.map +1 -1
  43. package/dest/wrappers/json_rpc_server.js +6 -1
  44. package/package.json +3 -3
  45. package/src/attributes.ts +29 -1
  46. package/src/config.ts +24 -9
  47. package/src/index.ts +1 -0
  48. package/src/json_rpc_server_metrics.ts +151 -0
  49. package/src/metrics.ts +473 -19
  50. package/src/monitored_batch_span_processor.ts +100 -0
  51. package/src/otel.ts +69 -3
  52. package/src/otel_propagation.ts +63 -5
  53. package/src/start.ts +6 -1
  54. package/src/telemetry.ts +6 -2
  55. package/src/vendor/attributes.ts +1 -0
  56. package/src/wrappers/fetch.ts +9 -3
  57. package/src/wrappers/index.ts +0 -1
  58. package/src/wrappers/json_rpc_server.ts +13 -2
  59. package/dest/wrappers/l2_block_stream.d.ts +0 -16
  60. package/dest/wrappers/l2_block_stream.d.ts.map +0 -1
  61. package/dest/wrappers/l2_block_stream.js +0 -400
  62. package/src/wrappers/l2_block_stream.ts +0 -41
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/telemetry-client",
3
- "version": "0.0.1-commit.f2ce05ee",
3
+ "version": "0.0.1-commit.f5a9928",
4
4
  "inherits": [
5
5
  "../package.common.json"
6
6
  ],
@@ -27,8 +27,8 @@
27
27
  "!*.test.*"
28
28
  ],
29
29
  "dependencies": {
30
- "@aztec/foundation": "0.0.1-commit.f2ce05ee",
31
- "@aztec/stdlib": "0.0.1-commit.f2ce05ee",
30
+ "@aztec/foundation": "0.0.1-commit.f5a9928",
31
+ "@aztec/stdlib": "0.0.1-commit.f5a9928",
32
32
  "@opentelemetry/api": "^1.9.0",
33
33
  "@opentelemetry/api-logs": "^0.55.0",
34
34
  "@opentelemetry/core": "^1.28.0",
package/src/attributes.ts CHANGED
@@ -18,6 +18,8 @@
18
18
  export const HTTP_REQUEST_HOST = 'http.header.request.host';
19
19
  export const HTTP_RESPONSE_STATUS_CODE = 'http.response.status_code';
20
20
 
21
+ export const JSON_RPC_REJECTION_REASON = 'aztec.json_rpc.rejection_reason';
22
+
21
23
  /** The Aztec network identifier */
22
24
  export const NETWORK_NAME = 'aztec.network_name';
23
25
 
@@ -45,6 +47,8 @@ export const BLOCK_NUMBER = 'aztec.block.number';
45
47
  export const BLOCK_HASH = 'aztec.block.hash';
46
48
  /** The slot number */
47
49
  export const SLOT_NUMBER = 'aztec.slot.number';
50
+ /** Whether an event happened before or after a slot boundary. */
51
+ export const SLOT_BOUNDARY_SIDE = 'aztec.slot_boundary_side';
48
52
  /** The checkpoint number */
49
53
  export const CHECKPOINT_NUMBER = 'aztec.checkpoint.number';
50
54
  /** The parent's block number */
@@ -61,6 +65,8 @@ export const EPOCH_SIZE = 'aztec.epoch.size';
61
65
  export const BLOCK_PROPOSER = 'aztec.block.proposer';
62
66
  /** The epoch number */
63
67
  export const EPOCH_NUMBER = 'aztec.epoch.number';
68
+ /** Kind of an EpochSession: 'full' or 'partial'. */
69
+ export const EPOCH_SESSION_KIND = 'aztec.epoch_session.kind';
64
70
  /** The tx hash */
65
71
  export const TX_HASH = 'aztec.tx.hash';
66
72
  /** Generic attribute representing whether the action was successful or not */
@@ -69,6 +75,8 @@ export const OK = 'aztec.ok';
69
75
  export const STATUS = 'aztec.status';
70
76
  /** Generic error type attribute */
71
77
  export const ERROR_TYPE = 'aztec.error_type';
78
+ /** The cause of an archiver prune (unproven / uncheckpointed / l1_conflict / orphan / l1_mismatch) */
79
+ export const PRUNE_TYPE = 'aztec.archiver.prune_type';
72
80
  /** The type of the transaction */
73
81
  export const L1_TX_TYPE = 'aztec.l1.tx_type';
74
82
  /** The L1 address of the entity that sent a transaction to L1 */
@@ -94,7 +102,6 @@ export const VALIDATOR_STATUS = 'aztec.validator_status';
94
102
 
95
103
  export const P2P_ID = 'aztec.p2p.id';
96
104
  export const P2P_REQ_RESP_PROTOCOL = 'aztec.p2p.req_resp.protocol';
97
- export const P2P_REQ_RESP_BATCH_REQUESTS_COUNT = 'aztec.p2p.req_resp.batch_requests_count';
98
105
  /** The state of a peer (Healthy, Disconnect, Banned) */
99
106
  export const P2P_PEER_SCORE_STATE = 'aztec.p2p.peer_score_state';
100
107
  export const POOL_NAME = 'aztec.pool.name';
@@ -128,6 +135,15 @@ export const NODEJS_EVENT_LOOP_STATE = 'nodejs.eventloop.state';
128
135
 
129
136
  export const TOPIC_NAME = 'aztec.gossip.topic_name';
130
137
 
138
+ /** Stage of gossiped tx validation (deserialize, fast_validation_setup, fast_validation, pool_precheck, proof_verify, pool_add) */
139
+ export const TX_VALIDATION_STAGE = 'aztec.p2p.tx_validation_stage';
140
+
141
+ /** Operation type enqueued on the mempool serial queue */
142
+ export const MEMPOOL_OPERATION = 'aztec.mempool.operation';
143
+
144
+ /** The reason a transaction was evicted from the tx pool */
145
+ export const TX_POOL_EVICTION_REASON = 'aztec.mempool.eviction_reason';
146
+
131
147
  export const TX_COLLECTION_METHOD = 'aztec.tx_collection.method';
132
148
 
133
149
  /** Scope of L1 transaction (sequencer, prover, or other) */
@@ -139,8 +155,20 @@ export const IS_COMMITTEE_MEMBER = 'aztec.is_committee_member';
139
155
  /** Fisherman fee analysis strategy identifier */
140
156
  export const FISHERMAN_FEE_STRATEGY_ID = 'aztec.fisherman.strategy_id';
141
157
 
158
+ /** Whether the analyzed block reached 100% blob capacity */
159
+ export const BLOCK_FULL = 'aztec.block_full';
160
+
142
161
  /** The L1 transaction target for block proposal */
143
162
  export const L1_BLOCK_PROPOSAL_TX_TARGET = 'aztec.l1.block_proposal_tx_target';
144
163
 
145
164
  /** Whether tracing methods were used to extract block proposal data */
146
165
  export const L1_BLOCK_PROPOSAL_USED_TRACE = 'aztec.l1.block_proposal_used_trace';
166
+
167
+ /** HA signer duty type (e.g., BLOCK_PROPOSAL, CHECKPOINT_ATTESTATION) */
168
+ export const HA_DUTY_TYPE = 'aztec.ha_signer.duty_type';
169
+
170
+ /** HA signer node identifier */
171
+ export const HA_NODE_ID = 'aztec.ha_signer.node_id';
172
+
173
+ /** The address of an attester (validator) participating in consensus */
174
+ export const ATTESTER_ADDRESS = 'aztec.attester.address';
package/src/config.ts CHANGED
@@ -1,4 +1,9 @@
1
- import { type ConfigMappingsType, booleanConfigHelper, getConfigFromMappings } from '@aztec/foundation/config';
1
+ import {
2
+ type ConfigMappingsType,
3
+ booleanConfigHelper,
4
+ getConfigFromMappings,
5
+ numberConfigHelper,
6
+ } from '@aztec/foundation/config';
2
7
 
3
8
  export interface TelemetryClientConfig {
4
9
  metricsCollectorUrl?: URL;
@@ -12,35 +17,35 @@ export interface TelemetryClientConfig {
12
17
  otelExportTimeoutMs: number;
13
18
  otelExcludeMetrics: string[];
14
19
  otelIncludeMetrics: string[];
20
+ otelMinTraceDurationMs: number;
21
+ otelBspMaxQueueSize: number;
15
22
  }
16
23
 
17
24
  export const telemetryClientConfigMappings: ConfigMappingsType<TelemetryClientConfig> = {
18
25
  metricsCollectorUrl: {
19
26
  env: 'OTEL_EXPORTER_OTLP_METRICS_ENDPOINT',
20
27
  description: 'The URL of the telemetry collector for metrics',
21
- parseEnv: (val: string) => val && new URL(val),
28
+ parseEnv: (val: string) => new URL(val),
22
29
  },
23
30
  tracesCollectorUrl: {
24
31
  env: 'OTEL_EXPORTER_OTLP_TRACES_ENDPOINT',
25
32
  description: 'The URL of the telemetry collector for traces',
26
- parseEnv: (val: string) => val && new URL(val),
33
+ parseEnv: (val: string) => new URL(val),
27
34
  },
28
35
  logsCollectorUrl: {
29
36
  env: 'OTEL_EXPORTER_OTLP_LOGS_ENDPOINT',
30
37
  description: 'The URL of the telemetry collector for logs',
31
- parseEnv: (val: string) => val && new URL(val),
38
+ parseEnv: (val: string) => new URL(val),
32
39
  },
33
40
  otelCollectIntervalMs: {
34
41
  env: 'OTEL_COLLECT_INTERVAL_MS',
35
42
  description: 'The interval at which to collect metrics',
36
- defaultValue: 60000, // Default extracted from otel client
37
- parseEnv: (val: string) => parseInt(val),
43
+ ...numberConfigHelper(60000),
38
44
  },
39
45
  otelExportTimeoutMs: {
40
46
  env: 'OTEL_EXPORT_TIMEOUT_MS',
41
47
  description: 'The timeout for exporting metrics',
42
- defaultValue: 30000, // Default extracted from otel client
43
- parseEnv: (val: string) => parseInt(val),
48
+ ...numberConfigHelper(30000),
44
49
  },
45
50
  otelExcludeMetrics: {
46
51
  env: 'OTEL_EXCLUDE_METRICS',
@@ -54,6 +59,16 @@ export const telemetryClientConfigMappings: ConfigMappingsType<TelemetryClientCo
54
59
  : [],
55
60
  defaultValue: [],
56
61
  },
62
+ otelMinTraceDurationMs: {
63
+ env: 'OTEL_MIN_TRACE_DURATION_MS',
64
+ description: 'The minimum successful trace duration to export in milliseconds. Set to 0 to export all traces.',
65
+ ...numberConfigHelper(10),
66
+ },
67
+ otelBspMaxQueueSize: {
68
+ env: 'OTEL_BSP_MAX_QUEUE_SIZE',
69
+ description: 'The maximum number of completed spans to queue before export.',
70
+ ...numberConfigHelper(16384),
71
+ },
57
72
  otelIncludeMetrics: {
58
73
  env: 'OTEL_INCLUDE_METRICS',
59
74
  description: 'A list of metric prefixes to include in export (ignored if OTEL_EXCLUDE_METRICS is set)',
@@ -70,7 +85,7 @@ export const telemetryClientConfigMappings: ConfigMappingsType<TelemetryClientCo
70
85
  publicMetricsCollectorUrl: {
71
86
  env: 'PUBLIC_OTEL_EXPORTER_OTLP_METRICS_ENDPOINT',
72
87
  description: 'A URL to publish a subset of metrics for public consumption',
73
- parseEnv: (val: string) => val && new URL(val),
88
+ parseEnv: (val: string) => new URL(val),
74
89
  },
75
90
  publicMetricsCollectFrom: {
76
91
  env: 'PUBLIC_OTEL_COLLECT_FROM',
package/src/index.ts CHANGED
@@ -8,3 +8,4 @@ export * from './l1_metrics.js';
8
8
  export * from './wrappers/index.js';
9
9
  export * from './start.js';
10
10
  export * from './otel_propagation.js';
11
+ export * from './json_rpc_server_metrics.js';
@@ -0,0 +1,151 @@
1
+ import { Timer } from '@aztec/foundation/timer';
2
+
3
+ import type Koa from 'koa';
4
+
5
+ import * as Attributes from './attributes.js';
6
+ import * as Metrics from './metrics.js';
7
+ import { getTelemetryClient } from './start.js';
8
+ import type { Histogram, MetricAttributesType, TelemetryClient, UpDownCounter } from './telemetry.js';
9
+ import { ATTR_JSONRPC_METHOD, ATTR_JSONRPC_SERVICE } from './vendor/attributes.js';
10
+
11
+ const BATCH_SIZE_BUCKETS = [1, 2, 5, 10, 20, 50, 100, 200, 500, 1000];
12
+
13
+ /** Fixed reasons for rejecting an RPC request before dispatching it to a registered handler. */
14
+ export type JsonRpcRejectionReason =
15
+ | 'unauthorized'
16
+ | 'parse_error'
17
+ | 'invalid_request'
18
+ | 'method_not_found'
19
+ | 'bad_request'
20
+ | 'internal_error';
21
+
22
+ /** Records bounded-cardinality metrics for registered JSON-RPC calls, rejected requests, and batches. */
23
+ export class JsonRpcServerMetrics {
24
+ private readonly requestCount: UpDownCounter;
25
+ private readonly requestDuration: Histogram;
26
+ private readonly requestValidationDuration: Histogram;
27
+ private readonly rejectedRequestCount: UpDownCounter;
28
+ private readonly batchCount: UpDownCounter;
29
+ private readonly batchDuration: Histogram;
30
+ private readonly batchSize: Histogram;
31
+
32
+ constructor(telemetry: TelemetryClient) {
33
+ const meter = telemetry.getMeter('JsonRpcServer');
34
+ this.requestCount = meter.createUpDownCounter(Metrics.JSON_RPC_SERVER_REQUEST_COUNT);
35
+ this.requestDuration = meter.createHistogram(Metrics.JSON_RPC_SERVER_REQUEST_DURATION);
36
+ this.requestValidationDuration = meter.createHistogram(Metrics.JSON_RPC_SERVER_REQUEST_VALIDATION_DURATION);
37
+ this.rejectedRequestCount = meter.createUpDownCounter(Metrics.JSON_RPC_SERVER_REJECTED_REQUEST_COUNT);
38
+ this.batchCount = meter.createUpDownCounter(Metrics.JSON_RPC_SERVER_BATCH_COUNT);
39
+ this.batchDuration = meter.createHistogram(Metrics.JSON_RPC_SERVER_BATCH_DURATION);
40
+ this.batchSize = meter.createHistogram(Metrics.JSON_RPC_SERVER_BATCH_SIZE, {
41
+ advice: { explicitBucketBoundaries: BATCH_SIZE_BUCKETS },
42
+ });
43
+ }
44
+
45
+ /** Records the outcome and handler duration of a registered RPC method. */
46
+ public recordRequest(fullMethod: string, durationMs: number, ok: boolean): void {
47
+ const [service, method] = splitJsonRpcMethod(fullMethod);
48
+ const attributes: MetricAttributesType = {
49
+ ...(service === undefined ? {} : { [ATTR_JSONRPC_SERVICE]: service }),
50
+ [ATTR_JSONRPC_METHOD]: method,
51
+ [Attributes.OK]: ok,
52
+ };
53
+ this.requestCount.add(1, attributes);
54
+ this.requestDuration.record(durationMs, attributes);
55
+ }
56
+
57
+ /** Records a pre-dispatch rejection using a fixed reason. */
58
+ public recordRejectedRequest(reason: JsonRpcRejectionReason): void {
59
+ this.rejectedRequestCount.add(1, { [Attributes.JSON_RPC_REJECTION_REASON]: reason });
60
+ }
61
+
62
+ /** Records the duration and outcome of validating a registered RPC method's parameters. */
63
+ public recordRequestValidation(fullMethod: string, durationMs: number, ok: boolean): void {
64
+ const [service, method] = splitJsonRpcMethod(fullMethod);
65
+ this.requestValidationDuration.record(durationMs, {
66
+ ...(service === undefined ? {} : { [ATTR_JSONRPC_SERVICE]: service }),
67
+ [ATTR_JSONRPC_METHOD]: method,
68
+ [Attributes.OK]: ok,
69
+ });
70
+ }
71
+
72
+ /** Records the outcome, processing duration, and number of calls in a batch envelope. */
73
+ public recordBatch(size: number, durationMs: number, ok: boolean): void {
74
+ const attributes = { [Attributes.OK]: ok };
75
+ this.batchCount.add(1, attributes);
76
+ this.batchDuration.record(durationMs, attributes);
77
+ this.batchSize.record(size, attributes);
78
+ }
79
+ }
80
+
81
+ let metricsOwner: TelemetryClient | undefined;
82
+ let metrics: JsonRpcServerMetrics | undefined;
83
+
84
+ export function getJsonRpcServerMetrics(): JsonRpcServerMetrics {
85
+ const telemetry = getTelemetryClient();
86
+ if (metricsOwner !== telemetry) {
87
+ metricsOwner = telemetry;
88
+ metrics = new JsonRpcServerMetrics(telemetry);
89
+ }
90
+ return metrics!;
91
+ }
92
+
93
+ export function getOtelJsonRpcServerMetricsMiddleware(
94
+ metricsProvider: () => Pick<JsonRpcServerMetrics, 'recordBatch' | 'recordRejectedRequest'> = getJsonRpcServerMetrics,
95
+ ): (ctx: Koa.Context, next: () => Promise<void>) => Promise<void> {
96
+ return async function otelJsonRpcServerMetrics(ctx, next) {
97
+ const timer = new Timer();
98
+ await next();
99
+
100
+ const requestBody = (ctx.request as { body?: unknown }).body;
101
+ if (Array.isArray(requestBody)) {
102
+ metricsProvider().recordBatch(requestBody.length, timer.ms(), Array.isArray(ctx.body));
103
+ }
104
+
105
+ for (const reason of getRejectionReasons(ctx.status, ctx.body)) {
106
+ metricsProvider().recordRejectedRequest(reason);
107
+ }
108
+ };
109
+ }
110
+
111
+ export function splitJsonRpcMethod(fullMethod: string): [service: string | undefined, method: string] {
112
+ const separator = fullMethod.indexOf('_');
113
+ return separator === -1 ? [undefined, fullMethod] : [fullMethod.slice(0, separator), fullMethod.slice(separator + 1)];
114
+ }
115
+
116
+ function getRejectionReasons(status: number, response: unknown): JsonRpcRejectionReason[] {
117
+ if (status === 401) {
118
+ return ['unauthorized'];
119
+ }
120
+
121
+ const responses = Array.isArray(response) ? response : [response];
122
+ return responses.flatMap(item => {
123
+ const code = getErrorCode(item);
124
+ if (code === -32700) {
125
+ return ['parse_error'];
126
+ }
127
+ if (code === -32601) {
128
+ return ['method_not_found'];
129
+ }
130
+ if (code === -32600) {
131
+ return ['invalid_request'];
132
+ }
133
+ if (code === -32603) {
134
+ return ['internal_error'];
135
+ }
136
+ if (code === -32000) {
137
+ return ['bad_request'];
138
+ }
139
+ return [];
140
+ });
141
+ }
142
+
143
+ function getErrorCode(response: unknown): number | undefined {
144
+ if (!response || typeof response !== 'object' || !('error' in response)) {
145
+ return undefined;
146
+ }
147
+ const error = response.error;
148
+ return error && typeof error === 'object' && 'code' in error && typeof error.code === 'number'
149
+ ? error.code
150
+ : undefined;
151
+ }