@comprehend/telemetry-node 0.1.4 → 0.2.1
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 +112 -27
- package/dist/ComprehendDevSpanProcessor.d.ts +10 -6
- package/dist/ComprehendDevSpanProcessor.js +154 -87
- package/dist/ComprehendDevSpanProcessor.test.js +270 -449
- package/dist/ComprehendMetricsExporter.d.ts +18 -0
- package/dist/ComprehendMetricsExporter.js +178 -0
- package/dist/ComprehendMetricsExporter.test.d.ts +1 -0
- package/dist/ComprehendMetricsExporter.test.js +266 -0
- package/dist/ComprehendSDK.d.ts +18 -0
- package/dist/ComprehendSDK.js +56 -0
- package/dist/ComprehendSDK.test.d.ts +1 -0
- package/dist/ComprehendSDK.test.js +126 -0
- package/dist/WebSocketConnection.d.ts +23 -3
- package/dist/WebSocketConnection.js +106 -12
- package/dist/WebSocketConnection.test.js +236 -169
- package/dist/index.d.ts +3 -1
- package/dist/index.js +5 -1
- package/dist/util.d.ts +2 -0
- package/dist/util.js +7 -0
- package/dist/wire-protocol.d.ts +168 -28
- package/package.json +3 -1
- package/src/ComprehendDevSpanProcessor.test.ts +311 -507
- package/src/ComprehendDevSpanProcessor.ts +178 -105
- package/src/ComprehendMetricsExporter.test.ts +334 -0
- package/src/ComprehendMetricsExporter.ts +225 -0
- package/src/ComprehendSDK.test.ts +160 -0
- package/src/ComprehendSDK.ts +63 -0
- package/src/WebSocketConnection.test.ts +286 -205
- package/src/WebSocketConnection.ts +135 -13
- package/src/index.ts +3 -2
- package/src/util.ts +6 -0
- package/src/wire-protocol.ts +204 -29
- package/src/sql-analyzer.test.ts +0 -599
- package/src/sql-analyzer.ts +0 -439
|
@@ -1,33 +1,95 @@
|
|
|
1
1
|
import WebSocket from 'ws';
|
|
2
|
+
import { randomUUID } from 'crypto';
|
|
2
3
|
import {
|
|
4
|
+
AttributeType,
|
|
5
|
+
CustomMetricSpecification,
|
|
3
6
|
InitMessage,
|
|
4
7
|
NewObservedEntityMessage,
|
|
5
8
|
NewObservedInteractionMessage,
|
|
6
9
|
ObservationInputMessage,
|
|
7
|
-
ObservationMessage,
|
|
8
10
|
ObservationOutputMessage,
|
|
11
|
+
StartProcessContextMessage,
|
|
12
|
+
InitAck,
|
|
9
13
|
} from './wire-protocol';
|
|
14
|
+
import { hrTimeNow } from './util';
|
|
10
15
|
|
|
11
|
-
const INGESTION_ENDPOINT = 'wss://ingestion.comprehend.dev';
|
|
16
|
+
const INGESTION_ENDPOINT = process.env.COMPREHEND_INGESTION_ENDPOINT || 'wss://ingestion.comprehend.dev';
|
|
17
|
+
|
|
18
|
+
/** A queue of unacknowledged messages keyed by sequence number, with an associated ack type. */
|
|
19
|
+
class SeqQueue<T extends { seq: number }> {
|
|
20
|
+
readonly ackType: string;
|
|
21
|
+
private readonly pending = new Map<number, T>();
|
|
22
|
+
|
|
23
|
+
constructor(ackType: string) {
|
|
24
|
+
this.ackType = ackType;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
add(message: T): void {
|
|
28
|
+
this.pending.set(message.seq, message);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
ack(seq: number): void {
|
|
32
|
+
this.pending.delete(seq);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
values(): IterableIterator<T> {
|
|
36
|
+
return this.pending.values();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
12
39
|
|
|
13
40
|
export class WebSocketConnection {
|
|
14
41
|
private readonly organization: string;
|
|
15
42
|
private readonly token: string;
|
|
16
43
|
private readonly logger?: (message: string) => void;
|
|
44
|
+
private readonly onAuthorized?: (ack: InitAck) => void;
|
|
45
|
+
private readonly onCustomMetricChange?: (specs: CustomMetricSpecification[]) => void;
|
|
17
46
|
private readonly unacknowledgedObserved = new Map<string, NewObservedEntityMessage | NewObservedInteractionMessage>();
|
|
18
|
-
|
|
47
|
+
|
|
48
|
+
private readonly seqQueues: Record<string, SeqQueue<any>>;
|
|
49
|
+
private readonly observationsQueue = new SeqQueue<any>('ack-observations');
|
|
50
|
+
private readonly timeseriesQueue = new SeqQueue<any>('ack-timeseries');
|
|
51
|
+
private readonly cumulativeQueue = new SeqQueue<any>('ack-cumulative');
|
|
52
|
+
private readonly traceSpansQueue = new SeqQueue<any>('ack-tracespans');
|
|
53
|
+
private readonly dbQueryQueue = new SeqQueue<any>('ack-db-query');
|
|
54
|
+
private readonly contextQueue = new SeqQueue<StartProcessContextMessage>('ack-context');
|
|
55
|
+
|
|
19
56
|
private socket: WebSocket | null = null;
|
|
20
57
|
private reconnectDelay = 1000;
|
|
21
58
|
private shouldReconnect = true;
|
|
22
59
|
private authorized = false;
|
|
23
60
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
61
|
+
private processContext: { serviceEntityHash: string, resources: Record<string, AttributeType> } | null = null;
|
|
62
|
+
private ingestionId: string = randomUUID();
|
|
63
|
+
|
|
64
|
+
private _seq = 1;
|
|
65
|
+
|
|
66
|
+
constructor(options: {
|
|
67
|
+
organization: string,
|
|
68
|
+
token: string,
|
|
69
|
+
logger?: (message: string) => void,
|
|
70
|
+
onAuthorized?: (ack: InitAck) => void,
|
|
71
|
+
onCustomMetricChange?: (specs: CustomMetricSpecification[]) => void,
|
|
72
|
+
}) {
|
|
73
|
+
this.organization = options.organization;
|
|
74
|
+
this.token = options.token;
|
|
75
|
+
this.logger = options.logger;
|
|
76
|
+
this.onAuthorized = options.onAuthorized;
|
|
77
|
+
this.onCustomMetricChange = options.onCustomMetricChange;
|
|
78
|
+
|
|
79
|
+
// Build a lookup from ack type to queue for dispatch in onMessage
|
|
80
|
+
this.seqQueues = {};
|
|
81
|
+
for (const q of [this.observationsQueue, this.timeseriesQueue, this.cumulativeQueue,
|
|
82
|
+
this.traceSpansQueue, this.dbQueryQueue, this.contextQueue]) {
|
|
83
|
+
this.seqQueues[q.ackType] = q;
|
|
84
|
+
}
|
|
85
|
+
|
|
28
86
|
this.connect();
|
|
29
87
|
}
|
|
30
88
|
|
|
89
|
+
public nextSeq(): number {
|
|
90
|
+
return this._seq++;
|
|
91
|
+
}
|
|
92
|
+
|
|
31
93
|
private log(message: string) {
|
|
32
94
|
if (this.logger) {
|
|
33
95
|
this.logger(message);
|
|
@@ -47,9 +109,10 @@ export class WebSocketConnection {
|
|
|
47
109
|
|
|
48
110
|
private onOpen(): void {
|
|
49
111
|
this.log('WebSocket connected. Sending init/auth message.');
|
|
112
|
+
this.ingestionId = randomUUID();
|
|
50
113
|
const init: InitMessage = {
|
|
51
114
|
event: 'init',
|
|
52
|
-
protocolVersion:
|
|
115
|
+
protocolVersion: 2,
|
|
53
116
|
token: this.token,
|
|
54
117
|
};
|
|
55
118
|
this.sendRaw(init);
|
|
@@ -63,18 +126,42 @@ export class WebSocketConnection {
|
|
|
63
126
|
this.authorized = true;
|
|
64
127
|
this.log('Authorization acknowledged by server.');
|
|
65
128
|
|
|
129
|
+
if (this.onAuthorized) {
|
|
130
|
+
this.onAuthorized(msg);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Send context first if we have one
|
|
134
|
+
if (this.processContext) {
|
|
135
|
+
this.sendContextStart();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Replay entities and interactions
|
|
66
139
|
for (const message of this.unacknowledgedObserved.values()) {
|
|
67
140
|
this.sendRaw(message);
|
|
68
141
|
}
|
|
69
|
-
|
|
70
|
-
|
|
142
|
+
|
|
143
|
+
// Replay all seq-based queues
|
|
144
|
+
for (const q of Object.values(this.seqQueues)) {
|
|
145
|
+
if (q === this.contextQueue) continue; // already sent above
|
|
146
|
+
for (const message of q.values()) {
|
|
147
|
+
this.sendRaw(message);
|
|
148
|
+
}
|
|
71
149
|
}
|
|
72
150
|
}
|
|
73
151
|
else if (msg.type === 'ack-observed') {
|
|
74
152
|
this.unacknowledgedObserved.delete(msg.hash);
|
|
75
153
|
}
|
|
76
|
-
else if (msg.type === '
|
|
77
|
-
this.
|
|
154
|
+
else if (msg.type === 'custom-metric-change') {
|
|
155
|
+
if (this.onCustomMetricChange) {
|
|
156
|
+
this.onCustomMetricChange(msg.customMetrics);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else if ('seq' in msg) {
|
|
160
|
+
// Dispatch seq-based acks to the appropriate queue
|
|
161
|
+
const queue = this.seqQueues[msg.type];
|
|
162
|
+
if (queue) {
|
|
163
|
+
queue.ack(msg.seq);
|
|
164
|
+
}
|
|
78
165
|
}
|
|
79
166
|
} catch (e) {
|
|
80
167
|
this.log('Error parsing message from server: ' + (e instanceof Error ? e.message : String(e)));
|
|
@@ -99,12 +186,47 @@ export class WebSocketConnection {
|
|
|
99
186
|
}
|
|
100
187
|
}
|
|
101
188
|
|
|
189
|
+
private sendContextStart(): void {
|
|
190
|
+
if (!this.processContext) return;
|
|
191
|
+
const seq = this.nextSeq();
|
|
192
|
+
const contextMsg: StartProcessContextMessage = {
|
|
193
|
+
event: 'context-start',
|
|
194
|
+
seq,
|
|
195
|
+
timestamp: hrTimeNow(),
|
|
196
|
+
ingestionId: this.ingestionId,
|
|
197
|
+
type: 'process',
|
|
198
|
+
serviceEntityHash: this.processContext.serviceEntityHash,
|
|
199
|
+
resources: this.processContext.resources,
|
|
200
|
+
};
|
|
201
|
+
this.contextQueue.add(contextMsg);
|
|
202
|
+
this.sendRaw(contextMsg);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
public setProcessContext(serviceEntityHash: string, resources: Record<string, AttributeType>): void {
|
|
206
|
+
this.processContext = { serviceEntityHash, resources };
|
|
207
|
+
if (this.authorized) {
|
|
208
|
+
this.sendContextStart();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
102
212
|
public sendMessage(message: ObservationInputMessage): void {
|
|
103
213
|
if (message.event === 'new-entity' || message.event === 'new-interaction') {
|
|
104
214
|
this.unacknowledgedObserved.set(message.hash, message);
|
|
105
215
|
}
|
|
106
216
|
else if (message.event === 'observations') {
|
|
107
|
-
this.
|
|
217
|
+
this.observationsQueue.add(message);
|
|
218
|
+
}
|
|
219
|
+
else if (message.event === 'timeseries') {
|
|
220
|
+
this.timeseriesQueue.add(message);
|
|
221
|
+
}
|
|
222
|
+
else if (message.event === 'cumulative') {
|
|
223
|
+
this.cumulativeQueue.add(message);
|
|
224
|
+
}
|
|
225
|
+
else if (message.event === 'tracespans') {
|
|
226
|
+
this.traceSpansQueue.add(message);
|
|
227
|
+
}
|
|
228
|
+
else if (message.event === 'db-query') {
|
|
229
|
+
this.dbQueryQueue.add(message);
|
|
108
230
|
}
|
|
109
231
|
|
|
110
232
|
if (this.authorized) {
|
package/src/index.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export {
|
|
2
|
-
|
|
1
|
+
export { ComprehendSDK } from './ComprehendSDK';
|
|
2
|
+
export { ComprehendDevSpanProcessor } from './ComprehendDevSpanProcessor';
|
|
3
|
+
export { ComprehendMetricsExporter } from './ComprehendMetricsExporter';
|
package/src/util.ts
ADDED
package/src/wire-protocol.ts
CHANGED
|
@@ -1,20 +1,61 @@
|
|
|
1
|
-
|
|
1
|
+
export type HrTime = [number, number]; // [seconds, nanoseconds]
|
|
2
2
|
|
|
3
|
-
export type ObservationInputMessage = InitMessage |
|
|
4
|
-
|
|
3
|
+
export type ObservationInputMessage = InitMessage | StartProcessContextMessage | EndContextMessage
|
|
4
|
+
| NewObservedEntityMessage | NewObservedInteractionMessage
|
|
5
|
+
| ObservationMessage | TimeSeriesMetricsMessage | CumulativeMetricsMessage
|
|
6
|
+
| TraceSpansMessage | DatabaseQueryMessage;
|
|
7
|
+
export type ObservationOutputMessage = InitAck | ContextAck | CustomMetricChange | ObservedAck | ObservationsAck
|
|
8
|
+
| TimeSeriesMetricsAck | CumulativeMetricsAck | TraceSpansAck
|
|
9
|
+
| DatabaseQueryAck;
|
|
5
10
|
|
|
6
11
|
|
|
12
|
+
// Initialization
|
|
13
|
+
|
|
7
14
|
export interface InitMessage {
|
|
8
15
|
event: "init";
|
|
9
|
-
protocolVersion:
|
|
16
|
+
protocolVersion: 2;
|
|
10
17
|
token: string;
|
|
11
18
|
}
|
|
12
19
|
|
|
20
|
+
export interface InitAck {
|
|
21
|
+
type: "ack-authorized";
|
|
22
|
+
customMetrics: CustomMetricSpecification[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
// Contexts
|
|
27
|
+
|
|
28
|
+
export interface StartProcessContextMessage {
|
|
29
|
+
event: "context-start";
|
|
30
|
+
seq: number;
|
|
31
|
+
timestamp: HrTime;
|
|
32
|
+
ingestionId: string;
|
|
33
|
+
type: "process";
|
|
34
|
+
serviceEntityHash: string;
|
|
35
|
+
resources: {
|
|
36
|
+
[key: string]: AttributeType
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface EndContextMessage {
|
|
41
|
+
event: "context-end";
|
|
42
|
+
seq: number;
|
|
43
|
+
timestamp: HrTime;
|
|
44
|
+
ingestionId: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface ContextAck {
|
|
48
|
+
type: "ack-context";
|
|
49
|
+
seq: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
// Observed entities
|
|
13
54
|
|
|
14
55
|
export interface NewObservedEntityMessage {
|
|
15
56
|
event: "new-entity";
|
|
16
|
-
type: string;
|
|
17
57
|
hash: string;
|
|
58
|
+
type: string;
|
|
18
59
|
}
|
|
19
60
|
|
|
20
61
|
export interface NewObservedServiceMessage extends NewObservedEntityMessage {
|
|
@@ -46,13 +87,20 @@ export interface NewObservedHttpServiceMessage extends NewObservedEntityMessage
|
|
|
46
87
|
port: number;
|
|
47
88
|
}
|
|
48
89
|
|
|
90
|
+
export interface ObservedAck {
|
|
91
|
+
type: "ack-observed";
|
|
92
|
+
hash: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
// Observed interactions
|
|
49
97
|
|
|
50
98
|
export interface NewObservedInteractionMessage {
|
|
51
99
|
event: "new-interaction";
|
|
52
|
-
type: string;
|
|
53
100
|
hash: string;
|
|
54
101
|
from: string;
|
|
55
102
|
to: string;
|
|
103
|
+
type: string;
|
|
56
104
|
}
|
|
57
105
|
|
|
58
106
|
export interface NewObservedHttpRequestMessage extends NewObservedInteractionMessage {
|
|
@@ -65,15 +113,8 @@ export interface NewObservedDatabaseConnectionMessage extends NewObservedInterac
|
|
|
65
113
|
user?: string;
|
|
66
114
|
}
|
|
67
115
|
|
|
68
|
-
export interface NewObservedDatabaseQueryMessage extends NewObservedInteractionMessage {
|
|
69
|
-
type: "db-query";
|
|
70
|
-
query: string;
|
|
71
|
-
selects?: string[];
|
|
72
|
-
inserts?: string[];
|
|
73
|
-
updates?: string[];
|
|
74
|
-
deletes?: string[];
|
|
75
|
-
}
|
|
76
116
|
|
|
117
|
+
// Observations
|
|
77
118
|
|
|
78
119
|
export interface ObservationMessage {
|
|
79
120
|
event: "observations";
|
|
@@ -81,16 +122,20 @@ export interface ObservationMessage {
|
|
|
81
122
|
observations: Array<Observation>;
|
|
82
123
|
}
|
|
83
124
|
|
|
84
|
-
export
|
|
85
|
-
|
|
86
|
-
|
|
125
|
+
export type Observation = HttpClientObservation | HttpServerObservation | CustomObservation;
|
|
126
|
+
|
|
127
|
+
interface BaseObservation {
|
|
128
|
+
subject: string;
|
|
129
|
+
spanId: string;
|
|
130
|
+
traceId: string;
|
|
87
131
|
timestamp: HrTime;
|
|
88
132
|
errorMessage?: string;
|
|
89
133
|
errorType?: string;
|
|
90
134
|
stack?: string;
|
|
135
|
+
type: string;
|
|
91
136
|
}
|
|
92
137
|
|
|
93
|
-
export interface HttpClientObservation extends
|
|
138
|
+
export interface HttpClientObservation extends BaseObservation {
|
|
94
139
|
type: "http-client";
|
|
95
140
|
path: string;
|
|
96
141
|
method: string;
|
|
@@ -101,7 +146,7 @@ export interface HttpClientObservation extends Observation {
|
|
|
101
146
|
responseBytes?: number;
|
|
102
147
|
}
|
|
103
148
|
|
|
104
|
-
export interface HttpServerObservation extends
|
|
149
|
+
export interface HttpServerObservation extends BaseObservation {
|
|
105
150
|
type: "http-server";
|
|
106
151
|
path: string;
|
|
107
152
|
status: number;
|
|
@@ -112,23 +157,153 @@ export interface HttpServerObservation extends Observation {
|
|
|
112
157
|
userAgent?: string;
|
|
113
158
|
}
|
|
114
159
|
|
|
115
|
-
export
|
|
116
|
-
|
|
160
|
+
export type AttributeType = string | number | boolean;
|
|
161
|
+
|
|
162
|
+
export interface CustomObservation extends BaseObservation {
|
|
163
|
+
type: "custom";
|
|
164
|
+
id: string;
|
|
165
|
+
attributes: {
|
|
166
|
+
[key: string]: AttributeType
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface ObservationsAck {
|
|
171
|
+
type: "ack-observations";
|
|
172
|
+
seq: number;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
// Time series metrics
|
|
177
|
+
|
|
178
|
+
export interface TimeSeriesMetricsMessage {
|
|
179
|
+
event: "timeseries";
|
|
180
|
+
seq: number;
|
|
181
|
+
data: TimeSeriesDataPoint[];
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface TimeSeriesDataPoint {
|
|
185
|
+
subject: string;
|
|
186
|
+
type: string;
|
|
187
|
+
timestamp: HrTime;
|
|
188
|
+
value: number;
|
|
189
|
+
unit: string;
|
|
190
|
+
attributes: {
|
|
191
|
+
[key: string]: AttributeType
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export interface TimeSeriesMetricsAck {
|
|
196
|
+
type: "ack-timeseries";
|
|
197
|
+
seq: number;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
// Cumulative metrics
|
|
202
|
+
|
|
203
|
+
export interface CumulativeMetricsMessage {
|
|
204
|
+
event: "cumulative";
|
|
205
|
+
seq: number;
|
|
206
|
+
data: CumulativeDataPoint[];
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export interface CumulativeDataPoint {
|
|
210
|
+
subject: string;
|
|
211
|
+
type: string;
|
|
212
|
+
timestamp: HrTime;
|
|
213
|
+
value: number;
|
|
214
|
+
unit: string;
|
|
215
|
+
attributes: {
|
|
216
|
+
[key: string]: AttributeType
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export interface CumulativeMetricsAck {
|
|
221
|
+
type: "ack-cumulative";
|
|
222
|
+
seq: number;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
// Trace spans
|
|
227
|
+
|
|
228
|
+
export interface TraceSpansMessage {
|
|
229
|
+
event: "tracespans";
|
|
230
|
+
seq: number;
|
|
231
|
+
data: TraceSpan[];
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export interface TraceSpan {
|
|
235
|
+
trace: string;
|
|
236
|
+
span: string;
|
|
237
|
+
parent: string;
|
|
238
|
+
name: string;
|
|
239
|
+
timestamp: HrTime;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export interface TraceSpansAck {
|
|
243
|
+
type: "ack-tracespans";
|
|
244
|
+
seq: number;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
// Database query
|
|
249
|
+
|
|
250
|
+
export interface DatabaseQueryMessage {
|
|
251
|
+
event: "db-query";
|
|
252
|
+
seq: number;
|
|
253
|
+
query: string;
|
|
254
|
+
from: string;
|
|
255
|
+
to: string;
|
|
256
|
+
timestamp: HrTime;
|
|
117
257
|
duration: HrTime;
|
|
258
|
+
traceId?: string;
|
|
259
|
+
spanId?: string;
|
|
260
|
+
errorMessage?: string;
|
|
261
|
+
errorType?: string;
|
|
262
|
+
stack?: string;
|
|
118
263
|
returnedRows?: number;
|
|
119
264
|
}
|
|
120
265
|
|
|
266
|
+
export interface DatabaseQueryAck {
|
|
267
|
+
type: "ack-db-query";
|
|
268
|
+
seq: number;
|
|
269
|
+
}
|
|
121
270
|
|
|
122
|
-
|
|
123
|
-
|
|
271
|
+
|
|
272
|
+
// Custom metrics
|
|
273
|
+
|
|
274
|
+
export interface CustomMetricChange {
|
|
275
|
+
type: "custom-metric-change";
|
|
276
|
+
customMetrics: CustomMetricSpecification[];
|
|
124
277
|
}
|
|
125
278
|
|
|
126
|
-
export
|
|
127
|
-
|
|
128
|
-
|
|
279
|
+
export type CustomMetricSpecification = CustomCumulativeMetricSpecification | CustomTimeSeriesMetricSpecification |
|
|
280
|
+
CustomSpanObservationSpecification;
|
|
281
|
+
|
|
282
|
+
export interface CustomCumulativeMetricSpecification {
|
|
283
|
+
type: "cumulative";
|
|
284
|
+
id: string;
|
|
285
|
+
attributes: string[];
|
|
286
|
+
subject: string;
|
|
129
287
|
}
|
|
130
288
|
|
|
131
|
-
export interface
|
|
132
|
-
type: "
|
|
133
|
-
|
|
289
|
+
export interface CustomTimeSeriesMetricSpecification {
|
|
290
|
+
type: "timeseries";
|
|
291
|
+
id: string;
|
|
292
|
+
attributes: string[];
|
|
293
|
+
subject: string;
|
|
134
294
|
}
|
|
295
|
+
|
|
296
|
+
export interface CustomSpanObservationSpecification {
|
|
297
|
+
type: "span";
|
|
298
|
+
rule: SpanMatcherRule;
|
|
299
|
+
subject: string;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export type SpanMatcherRule =
|
|
303
|
+
| { kind: "type", value: "client" | "server" | "internal" }
|
|
304
|
+
| { kind: "attribute-present", key: string }
|
|
305
|
+
| { kind: "attribute-absent", key: string }
|
|
306
|
+
| { kind: "attribute-equals", key: string, value: string }
|
|
307
|
+
| { kind: "attribute-not-equals", key: string, value: string }
|
|
308
|
+
| { kind: "all", rules: SpanMatcherRule[] }
|
|
309
|
+
| { kind: "any", rules: SpanMatcherRule[] };
|