@geekmidas/telescope 1.1.1 → 10.0.0-alpha.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.
Files changed (167) hide show
  1. package/README.md +14 -4
  2. package/dist/{hono-Bz9PQQzj.mjs → hono-Bw-ey6CT.mjs} +2 -2
  3. package/dist/{hono-Bz9PQQzj.mjs.map → hono-Bw-ey6CT.mjs.map} +1 -1
  4. package/dist/{hono-Z_JIICmt.cjs → hono-CPHX7FAm.cjs} +2 -2
  5. package/dist/{hono-Z_JIICmt.cjs.map → hono-CPHX7FAm.cjs.map} +1 -1
  6. package/dist/{http-Crsriv32.mjs → http-BIvr0YdI.mjs} +1 -1
  7. package/dist/{http-Crsriv32.mjs.map → http-BIvr0YdI.mjs.map} +1 -1
  8. package/dist/{http-JycFRfSe.cjs → http-BsqmhurZ.cjs} +1 -1
  9. package/dist/{http-JycFRfSe.cjs.map → http-BsqmhurZ.cjs.map} +1 -1
  10. package/dist/{http-D5kn3_Mt.d.mts → http-DULM1u2L.d.cts} +3 -3
  11. package/dist/{http-D5kn3_Mt.d.mts.map → http-DULM1u2L.d.cts.map} +1 -1
  12. package/dist/{http-Dvca6fC3.d.cts → http-GB-Nlvwc.d.mts} +3 -3
  13. package/dist/{http-Dvca6fC3.d.cts.map → http-GB-Nlvwc.d.mts.map} +1 -1
  14. package/dist/instrumentation/hono.cjs +2 -2
  15. package/dist/instrumentation/hono.mjs +2 -2
  16. package/dist/instrumentation/http.cjs +1 -1
  17. package/dist/instrumentation/http.d.cts +1 -1
  18. package/dist/instrumentation/http.d.mts +1 -1
  19. package/dist/instrumentation/http.mjs +1 -1
  20. package/dist/instrumentation/index.cjs +5 -5
  21. package/dist/instrumentation/index.d.cts +3 -3
  22. package/dist/instrumentation/index.d.mts +3 -3
  23. package/dist/instrumentation/index.mjs +5 -5
  24. package/dist/instrumentation/middleware.cjs +2 -2
  25. package/dist/instrumentation/middleware.d.cts +1 -1
  26. package/dist/instrumentation/middleware.d.mts +1 -1
  27. package/dist/instrumentation/middleware.mjs +2 -2
  28. package/dist/instrumentation/otel.cjs +2 -2
  29. package/dist/instrumentation/otel.d.cts +1 -1
  30. package/dist/instrumentation/otel.d.mts +1 -1
  31. package/dist/instrumentation/otel.mjs +2 -2
  32. package/dist/instrumentation/setup.cjs +1 -1
  33. package/dist/instrumentation/setup.mjs +1 -1
  34. package/dist/logger/console.d.cts.map +1 -1
  35. package/dist/{middleware-D3v5SnSs.cjs → middleware-Bcq6XXGG.cjs} +3 -3
  36. package/dist/middleware-Bcq6XXGG.cjs.map +1 -0
  37. package/dist/{middleware-Dy1ctr2N.mjs → middleware-D1XEHvyy.mjs} +3 -3
  38. package/dist/middleware-D1XEHvyy.mjs.map +1 -0
  39. package/dist/{middleware-CA84nsNN.d.cts → middleware-PeyS-9Ny.d.cts} +2 -2
  40. package/dist/{middleware-CA84nsNN.d.cts.map → middleware-PeyS-9Ny.d.cts.map} +1 -1
  41. package/dist/{middleware-DAceVpfw.d.mts → middleware-oHvo_ZgU.d.mts} +2 -2
  42. package/dist/{middleware-DAceVpfw.d.mts.map → middleware-oHvo_ZgU.d.mts.map} +1 -1
  43. package/dist/{otel-DPKNyJ0t.mjs → otel-B9bNoeZ2.mjs} +4 -4
  44. package/dist/otel-B9bNoeZ2.mjs.map +1 -0
  45. package/dist/{otel-1bJPb2Z7.d.mts → otel-Bk89b1lX.d.cts} +3 -3
  46. package/dist/{otel-1bJPb2Z7.d.mts.map → otel-Bk89b1lX.d.cts.map} +1 -1
  47. package/dist/{otel-DuiBeBPU.cjs → otel-BzjOtVxZ.cjs} +4 -4
  48. package/dist/otel-BzjOtVxZ.cjs.map +1 -0
  49. package/dist/{otel-SBVoVEHr.d.cts → otel-lPMsM26e.d.mts} +3 -3
  50. package/dist/{otel-SBVoVEHr.d.cts.map → otel-lPMsM26e.d.mts.map} +1 -1
  51. package/dist/otlp/hono.cjs +3 -3
  52. package/dist/otlp/hono.mjs +3 -3
  53. package/dist/otlp/index.cjs +3 -3
  54. package/dist/otlp/index.mjs +3 -3
  55. package/dist/otlp/receiver.cjs +3 -3
  56. package/dist/otlp/receiver.mjs +3 -3
  57. package/dist/otlp/transformer.cjs +2 -2
  58. package/dist/otlp/transformer.mjs +2 -2
  59. package/dist/otlp/types.cjs +1 -1
  60. package/dist/otlp/types.mjs +1 -1
  61. package/dist/{receiver-q4f7eirs.mjs → receiver-D46dfkYI.mjs} +2 -2
  62. package/dist/{receiver-q4f7eirs.mjs.map → receiver-D46dfkYI.mjs.map} +1 -1
  63. package/dist/{receiver-BW_4go_X.cjs → receiver-slCoAFat.cjs} +2 -2
  64. package/dist/{receiver-BW_4go_X.cjs.map → receiver-slCoAFat.cjs.map} +1 -1
  65. package/dist/{setup-BhCp2LUp.mjs → setup-DC9JMEYZ.mjs} +1 -1
  66. package/dist/{setup-BhCp2LUp.mjs.map → setup-DC9JMEYZ.mjs.map} +1 -1
  67. package/dist/{setup-_uT4L_x5.cjs → setup-DufxQpi9.cjs} +1 -1
  68. package/dist/{setup-_uT4L_x5.cjs.map → setup-DufxQpi9.cjs.map} +1 -1
  69. package/dist/storage/kysely.cjs +48 -38
  70. package/dist/storage/kysely.cjs.map +1 -1
  71. package/dist/storage/kysely.d.cts +29 -9
  72. package/dist/storage/kysely.d.cts.map +1 -1
  73. package/dist/storage/kysely.d.mts +29 -9
  74. package/dist/storage/kysely.d.mts.map +1 -1
  75. package/dist/storage/kysely.mjs +48 -38
  76. package/dist/storage/kysely.mjs.map +1 -1
  77. package/dist/tracing-BX8uY2nR.d.cts.map +1 -1
  78. package/dist/tracing-PnECEbEu.d.mts.map +1 -1
  79. package/dist/{transformer-CFrJQkUg.mjs → transformer-CFQ4WFk6.mjs} +2 -2
  80. package/dist/{transformer-CFrJQkUg.mjs.map → transformer-CFQ4WFk6.mjs.map} +1 -1
  81. package/dist/{transformer-DvDYyFRQ.cjs → transformer-Csl6fbgv.cjs} +2 -2
  82. package/dist/{transformer-DvDYyFRQ.cjs.map → transformer-Csl6fbgv.cjs.map} +1 -1
  83. package/dist/types-B1EIa_cQ.d.mts.map +1 -1
  84. package/dist/{types-BBeTDpHr.cjs → types-BMiHgsi4.cjs} +1 -1
  85. package/dist/{types-BBeTDpHr.cjs.map → types-BMiHgsi4.cjs.map} +1 -1
  86. package/dist/{types-CXYLhG_G.mjs → types-Cj59nqy7.mjs} +1 -1
  87. package/dist/{types-CXYLhG_G.mjs.map → types-Cj59nqy7.mjs.map} +1 -1
  88. package/dist/types-l7Rlb5Mg.d.cts.map +1 -1
  89. package/package.json +6 -3
  90. package/CHANGELOG.md +0 -51
  91. package/dist/middleware-D3v5SnSs.cjs.map +0 -1
  92. package/dist/middleware-Dy1ctr2N.mjs.map +0 -1
  93. package/dist/otel-DPKNyJ0t.mjs.map +0 -1
  94. package/dist/otel-DuiBeBPU.cjs.map +0 -1
  95. package/src/Telescope.ts +0 -561
  96. package/src/__tests__/Telescope.spec.ts +0 -438
  97. package/src/__tests__/pino-transport.spec.ts +0 -115
  98. package/src/__tests__/redact.spec.ts +0 -336
  99. package/src/adapters/__tests__/lambda.spec.ts +0 -318
  100. package/src/adapters/__tests__/middy.spec.ts +0 -741
  101. package/src/adapters/index.ts +0 -22
  102. package/src/adapters/lambda.ts +0 -432
  103. package/src/adapters/middy.ts +0 -402
  104. package/src/adapters/types.ts +0 -181
  105. package/src/core.ts +0 -51
  106. package/src/index.ts +0 -34
  107. package/src/instrumentation/__tests__/core.spec.ts +0 -248
  108. package/src/instrumentation/__tests__/hono.spec.ts +0 -348
  109. package/src/instrumentation/__tests__/middleware.spec.ts +0 -528
  110. package/src/instrumentation/__tests__/otel.spec.ts +0 -291
  111. package/src/instrumentation/__tests__/tracing.spec.ts +0 -208
  112. package/src/instrumentation/core.ts +0 -228
  113. package/src/instrumentation/hono.ts +0 -270
  114. package/src/instrumentation/http.ts +0 -306
  115. package/src/instrumentation/index.ts +0 -90
  116. package/src/instrumentation/middleware.ts +0 -297
  117. package/src/instrumentation/otel.ts +0 -271
  118. package/src/instrumentation/setup.ts +0 -246
  119. package/src/instrumentation/tracing.ts +0 -145
  120. package/src/logger/__tests__/console.spec.ts +0 -366
  121. package/src/logger/__tests__/pino.spec.ts +0 -217
  122. package/src/logger/console.ts +0 -230
  123. package/src/logger/pino.ts +0 -201
  124. package/src/metrics/MetricsAggregator.ts +0 -555
  125. package/src/metrics/__tests__/MetricsAggregator.spec.ts +0 -298
  126. package/src/metrics/index.ts +0 -2
  127. package/src/metrics/storage/InMemoryMetricsStorage.ts +0 -380
  128. package/src/metrics/storage/MetricsStorage.ts +0 -74
  129. package/src/metrics/storage/index.ts +0 -3
  130. package/src/metrics/storage/types.ts +0 -243
  131. package/src/otlp/__tests__/receiver.spec.ts +0 -367
  132. package/src/otlp/__tests__/transformer.spec.ts +0 -455
  133. package/src/otlp/hono.ts +0 -120
  134. package/src/otlp/index.ts +0 -22
  135. package/src/otlp/receiver.ts +0 -162
  136. package/src/otlp/transformer.ts +0 -367
  137. package/src/otlp/types.ts +0 -364
  138. package/src/redact.ts +0 -116
  139. package/src/server/__tests__/hono.spec.ts +0 -814
  140. package/src/server/hono.ts +0 -442
  141. package/src/storage/__tests__/kysely.spec.ts +0 -715
  142. package/src/storage/__tests__/memory.spec.ts +0 -694
  143. package/src/storage/kysely.ts +0 -599
  144. package/src/storage/memory.ts +0 -204
  145. package/src/types.ts +0 -447
  146. package/src/ui-assets.ts +0 -40
  147. package/tsconfig.json +0 -9
  148. package/ui/CHANGELOG.md +0 -7
  149. package/ui/index.html +0 -12
  150. package/ui/node_modules/.bin/tsc +0 -21
  151. package/ui/node_modules/.bin/tsserver +0 -21
  152. package/ui/node_modules/.bin/vite +0 -21
  153. package/ui/package.json +0 -24
  154. package/ui/src/App.tsx +0 -595
  155. package/ui/src/api.ts +0 -88
  156. package/ui/src/components/ExceptionDetail.tsx +0 -100
  157. package/ui/src/components/FilterBar.tsx +0 -162
  158. package/ui/src/components/LogDetail.tsx +0 -91
  159. package/ui/src/components/RequestDetail.tsx +0 -143
  160. package/ui/src/main.tsx +0 -10
  161. package/ui/src/styles.css +0 -11
  162. package/ui/src/types.ts +0 -63
  163. package/ui/src/vite-env.d.ts +0 -1
  164. package/ui/src/vite-plugin-gkm-config.ts +0 -54
  165. package/ui/tsconfig.json +0 -20
  166. package/ui/tsconfig.tsbuildinfo +0 -1
  167. package/ui/vite.config.ts +0 -13
@@ -1,74 +0,0 @@
1
- import type {
2
- MetricBucket,
3
- MetricPoint,
4
- MetricQuery,
5
- MetricResult,
6
- MetricsStorageConfig,
7
- PartitionInfo,
8
- RollupOptions,
9
- } from './types';
10
-
11
- /**
12
- * Interface for metrics storage backends.
13
- *
14
- * Implementations can use different storage technologies:
15
- * - In-memory (development/testing)
16
- * - PostgreSQL with TimescaleDB
17
- * - ClickHouse
18
- * - InfluxDB
19
- */
20
- export interface MetricsStorage {
21
- /**
22
- * Write raw metric points to storage.
23
- * Points are typically buffered and flushed in batches.
24
- */
25
- write(points: MetricPoint[]): Promise<void>;
26
-
27
- /**
28
- * Query metrics with aggregation.
29
- * Returns time series data with statistics.
30
- */
31
- query(query: MetricQuery): Promise<MetricResult[]>;
32
-
33
- /**
34
- * Get a single aggregated bucket for a specific time range.
35
- * Useful for dashboards showing current values.
36
- */
37
- getBucket(
38
- projectId: string,
39
- name: string,
40
- bucket: Date,
41
- ): Promise<MetricBucket | null>;
42
-
43
- /**
44
- * Roll up data from one tier to another.
45
- * Aggregates fine-grained data into coarser buckets.
46
- */
47
- rollup(options: RollupOptions): Promise<number>;
48
-
49
- /**
50
- * Prune old data based on retention policies.
51
- * Returns the number of records deleted.
52
- */
53
- prune(): Promise<number>;
54
-
55
- /**
56
- * List partitions for management and monitoring.
57
- */
58
- listPartitions(tier?: string): Promise<PartitionInfo[]>;
59
-
60
- /**
61
- * Get storage configuration.
62
- */
63
- getConfig(): MetricsStorageConfig;
64
-
65
- /**
66
- * Flush any buffered data to storage.
67
- */
68
- flush(): Promise<void>;
69
-
70
- /**
71
- * Close the storage connection and clean up resources.
72
- */
73
- close(): Promise<void>;
74
- }
@@ -1,3 +0,0 @@
1
- export * from './InMemoryMetricsStorage';
2
- export * from './MetricsStorage';
3
- export * from './types';
@@ -1,243 +0,0 @@
1
- /**
2
- * Metric types supported by the storage system
3
- */
4
- export type MetricType = 'counter' | 'gauge' | 'histogram';
5
-
6
- /**
7
- * Labels/dimensions for metric filtering and grouping
8
- */
9
- export type MetricLabels = Record<string, string>;
10
-
11
- /**
12
- * A single metric data point
13
- */
14
- export interface MetricPoint {
15
- /** Timestamp of the metric */
16
- timestamp: Date;
17
- /** Project/app identifier for multi-tenant isolation */
18
- projectId: string;
19
- /** Service name (e.g., 'api', 'worker', 'cron') */
20
- service: string;
21
- /** Environment (e.g., 'development', 'staging', 'production') */
22
- environment: string;
23
- /** Metric name (e.g., 'http.request.duration', 'orders.created') */
24
- name: string;
25
- /** Metric type */
26
- type: MetricType;
27
- /** Numeric value */
28
- value: number;
29
- /** Dimensions for filtering/grouping */
30
- labels: MetricLabels;
31
- }
32
-
33
- /**
34
- * Aggregated metric bucket (pre-computed for efficient queries)
35
- */
36
- export interface MetricBucket {
37
- /** Bucket start timestamp */
38
- bucket: Date;
39
- /** Project identifier */
40
- projectId: string;
41
- /** Service name */
42
- service: string;
43
- /** Environment */
44
- environment: string;
45
- /** Metric name */
46
- name: string;
47
- /** Hash of labels for grouping */
48
- labelsHash: string;
49
- /** Original labels */
50
- labels: MetricLabels;
51
- /** Number of data points in bucket */
52
- count: number;
53
- /** Sum of values */
54
- sum: number;
55
- /** Minimum value */
56
- min: number;
57
- /** Maximum value */
58
- max: number;
59
- /** T-Digest sketch for percentile calculations (serialized) */
60
- sketch?: TDigestData;
61
- }
62
-
63
- /**
64
- * Serialized T-Digest data structure for percentile calculations
65
- * Stores centroids (mean, weight) that can be merged across buckets
66
- */
67
- export interface TDigestData {
68
- /** Centroids: [mean, weight][] */
69
- centroids: [number, number][];
70
- /** Compression factor (higher = more accuracy, more memory) */
71
- compression: number;
72
- /** Total count of values */
73
- count: number;
74
- /** Minimum value seen */
75
- min: number;
76
- /** Maximum value seen */
77
- max: number;
78
- }
79
-
80
- /**
81
- * Time range for queries
82
- */
83
- export interface TimeRange {
84
- start: Date;
85
- end: Date;
86
- }
87
-
88
- /**
89
- * Aggregation granularity
90
- */
91
- export type AggregationInterval = '1m' | '5m' | '15m' | '1h' | '6h' | '1d';
92
-
93
- /**
94
- * Query parameters for retrieving metrics
95
- */
96
- export interface MetricQuery {
97
- /** Project filter (required for multi-tenant) */
98
- projectId: string;
99
- /** Optional service filter */
100
- service?: string;
101
- /** Optional environment filter */
102
- environment?: string;
103
- /** Metric name pattern (supports wildcards like 'http.*') */
104
- name?: string;
105
- /** Label filters */
106
- labels?: MetricLabels;
107
- /** Time range */
108
- range: TimeRange;
109
- /** Aggregation interval */
110
- interval?: AggregationInterval;
111
- /** Group by these label keys */
112
- groupBy?: string[];
113
- /** Maximum number of results */
114
- limit?: number;
115
- }
116
-
117
- /**
118
- * Query result with aggregated values
119
- */
120
- export interface MetricResult {
121
- /** Metric name */
122
- name: string;
123
- /** Labels (grouped by keys if specified) */
124
- labels: MetricLabels;
125
- /** Time series data points */
126
- timeSeries: MetricTimeSeriesPoint[];
127
- /** Overall statistics for the query range */
128
- stats: {
129
- count: number;
130
- sum: number;
131
- avg: number;
132
- min: number;
133
- max: number;
134
- p50: number;
135
- p95: number;
136
- p99: number;
137
- };
138
- }
139
-
140
- /**
141
- * Single point in a time series result
142
- */
143
- export interface MetricTimeSeriesPoint {
144
- timestamp: Date;
145
- count: number;
146
- sum: number;
147
- avg: number;
148
- min: number;
149
- max: number;
150
- p50?: number;
151
- p95?: number;
152
- p99?: number;
153
- }
154
-
155
- /**
156
- * Storage tier configuration
157
- */
158
- export interface StorageTier {
159
- /** Table/collection name for this tier */
160
- name: string;
161
- /** Bucket interval for aggregation */
162
- interval: AggregationInterval;
163
- /** Retention duration in milliseconds (0 = forever) */
164
- retention: number;
165
- /** Partition interval for efficient pruning */
166
- partitionInterval: 'hourly' | 'daily' | 'weekly' | 'monthly';
167
- }
168
-
169
- /**
170
- * Metrics storage configuration
171
- */
172
- export interface MetricsStorageConfig {
173
- /** Storage tiers (raw, minute, hourly, etc.) */
174
- tiers: StorageTier[];
175
- /** Flush interval in milliseconds */
176
- flushInterval: number;
177
- /** Maximum batch size for writes */
178
- maxBatchSize: number;
179
- /** Enable automatic rollup between tiers */
180
- autoRollup: boolean;
181
- /** T-Digest compression factor (default: 100) */
182
- tdigestCompression: number;
183
- }
184
-
185
- /**
186
- * Default configuration with hybrid storage strategy
187
- */
188
- export const DEFAULT_METRICS_CONFIG: MetricsStorageConfig = {
189
- tiers: [
190
- {
191
- name: 'raw',
192
- interval: '1m',
193
- retention: 60 * 60 * 1000, // 1 hour
194
- partitionInterval: 'hourly',
195
- },
196
- {
197
- name: '1m',
198
- interval: '1m',
199
- retention: 7 * 24 * 60 * 60 * 1000, // 7 days
200
- partitionInterval: 'daily',
201
- },
202
- {
203
- name: '1h',
204
- interval: '1h',
205
- retention: 0, // Forever
206
- partitionInterval: 'monthly',
207
- },
208
- ],
209
- flushInterval: 10_000, // 10 seconds
210
- maxBatchSize: 1000,
211
- autoRollup: true,
212
- tdigestCompression: 100,
213
- };
214
-
215
- /**
216
- * Rollup options for aggregating data between tiers
217
- */
218
- export interface RollupOptions {
219
- /** Source tier */
220
- from: string;
221
- /** Target tier */
222
- to: string;
223
- /** Only rollup data older than this */
224
- olderThan: Date;
225
- }
226
-
227
- /**
228
- * Partition info for management
229
- */
230
- export interface PartitionInfo {
231
- /** Partition name */
232
- name: string;
233
- /** Tier this partition belongs to */
234
- tier: string;
235
- /** Partition start time */
236
- start: Date;
237
- /** Partition end time */
238
- end: Date;
239
- /** Approximate row count */
240
- rowCount?: number;
241
- /** Size in bytes */
242
- sizeBytes?: number;
243
- }
@@ -1,367 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
- import { InMemoryStorage } from '../../storage/memory';
3
- import { Telescope } from '../../Telescope';
4
- import { OTLPReceiver } from '../receiver';
5
- import {
6
- type ExportLogsServiceRequest,
7
- type ExportMetricsServiceRequest,
8
- type ExportTraceServiceRequest,
9
- SeverityNumber,
10
- SpanKind,
11
- SpanStatusCode,
12
- } from '../types';
13
-
14
- describe('OTLPReceiver', () => {
15
- let telescope: Telescope;
16
- let storage: InMemoryStorage;
17
- let receiver: OTLPReceiver;
18
-
19
- beforeEach(() => {
20
- storage = new InMemoryStorage();
21
- telescope = new Telescope({ storage });
22
- receiver = new OTLPReceiver({ telescope });
23
- });
24
-
25
- afterEach(() => {
26
- telescope.destroy();
27
- });
28
-
29
- describe('receiveTraces', () => {
30
- it('should record HTTP server spans as requests', async () => {
31
- const request: ExportTraceServiceRequest = {
32
- resourceSpans: [
33
- {
34
- resource: {
35
- attributes: [
36
- { key: 'service.name', value: { stringValue: 'test-api' } },
37
- ],
38
- },
39
- scopeSpans: [
40
- {
41
- spans: [
42
- {
43
- traceId: 'abc123',
44
- spanId: 'def456',
45
- name: 'GET /users',
46
- kind: SpanKind.SPAN_KIND_SERVER,
47
- startTimeUnixNano: '1704067200000000000',
48
- endTimeUnixNano: '1704067200050000000',
49
- attributes: [
50
- { key: 'http.method', value: { stringValue: 'GET' } },
51
- { key: 'http.target', value: { stringValue: '/users' } },
52
- { key: 'http.status_code', value: { intValue: '200' } },
53
- ],
54
- status: { code: SpanStatusCode.STATUS_CODE_OK },
55
- },
56
- ],
57
- },
58
- ],
59
- },
60
- ],
61
- };
62
-
63
- const response = await receiver.receiveTraces(request);
64
-
65
- expect(response.partialSuccess).toBeUndefined();
66
-
67
- const requests = await telescope.getRequests();
68
- expect(requests).toHaveLength(1);
69
- expect(requests[0].method).toBe('GET');
70
- expect(requests[0].path).toBe('/users');
71
- expect(requests[0].status).toBe(200);
72
- });
73
-
74
- it('should handle multiple spans', async () => {
75
- const request: ExportTraceServiceRequest = {
76
- resourceSpans: [
77
- {
78
- scopeSpans: [
79
- {
80
- spans: [
81
- {
82
- traceId: 'abc123',
83
- spanId: 'span1',
84
- name: 'GET /users',
85
- kind: SpanKind.SPAN_KIND_SERVER,
86
- startTimeUnixNano: '1704067200000000000',
87
- endTimeUnixNano: '1704067200050000000',
88
- attributes: [
89
- { key: 'http.method', value: { stringValue: 'GET' } },
90
- { key: 'http.target', value: { stringValue: '/users' } },
91
- { key: 'http.status_code', value: { intValue: '200' } },
92
- ],
93
- },
94
- {
95
- traceId: 'abc123',
96
- spanId: 'span2',
97
- name: 'POST /users',
98
- kind: SpanKind.SPAN_KIND_SERVER,
99
- startTimeUnixNano: '1704067200100000000',
100
- endTimeUnixNano: '1704067200200000000',
101
- attributes: [
102
- { key: 'http.method', value: { stringValue: 'POST' } },
103
- { key: 'http.target', value: { stringValue: '/users' } },
104
- { key: 'http.status_code', value: { intValue: '201' } },
105
- ],
106
- },
107
- ],
108
- },
109
- ],
110
- },
111
- ],
112
- };
113
-
114
- await receiver.receiveTraces(request);
115
-
116
- const requests = await telescope.getRequests();
117
- expect(requests).toHaveLength(2);
118
- });
119
-
120
- it('should return empty response for empty request', async () => {
121
- const response = await receiver.receiveTraces({});
122
- expect(response).toEqual({});
123
- });
124
- });
125
-
126
- describe('receiveLogs', () => {
127
- it('should record log entries', async () => {
128
- const request: ExportLogsServiceRequest = {
129
- resourceLogs: [
130
- {
131
- scopeLogs: [
132
- {
133
- logRecords: [
134
- {
135
- observedTimeUnixNano: '1704067200000000000',
136
- severityNumber: SeverityNumber.SEVERITY_NUMBER_INFO,
137
- body: { stringValue: 'User logged in' },
138
- attributes: [
139
- { key: 'userId', value: { stringValue: '123' } },
140
- ],
141
- },
142
- ],
143
- },
144
- ],
145
- },
146
- ],
147
- };
148
-
149
- const response = await receiver.receiveLogs(request);
150
-
151
- expect(response.partialSuccess).toBeUndefined();
152
-
153
- const logs = await telescope.getLogs();
154
- expect(logs).toHaveLength(1);
155
- expect(logs[0].level).toBe('info');
156
- expect(logs[0].message).toBe('User logged in');
157
- });
158
-
159
- it('should batch multiple log records', async () => {
160
- const request: ExportLogsServiceRequest = {
161
- resourceLogs: [
162
- {
163
- scopeLogs: [
164
- {
165
- logRecords: [
166
- {
167
- observedTimeUnixNano: '1704067200000000000',
168
- severityNumber: SeverityNumber.SEVERITY_NUMBER_INFO,
169
- body: { stringValue: 'Log 1' },
170
- },
171
- {
172
- observedTimeUnixNano: '1704067200001000000',
173
- severityNumber: SeverityNumber.SEVERITY_NUMBER_DEBUG,
174
- body: { stringValue: 'Log 2' },
175
- },
176
- {
177
- observedTimeUnixNano: '1704067200002000000',
178
- severityNumber: SeverityNumber.SEVERITY_NUMBER_WARN,
179
- body: { stringValue: 'Log 3' },
180
- },
181
- ],
182
- },
183
- ],
184
- },
185
- ],
186
- };
187
-
188
- await receiver.receiveLogs(request);
189
-
190
- const logs = await telescope.getLogs();
191
- expect(logs).toHaveLength(3);
192
- });
193
-
194
- it('should return empty response for empty request', async () => {
195
- const response = await receiver.receiveLogs({});
196
- expect(response).toEqual({});
197
- });
198
- });
199
-
200
- describe('receiveMetrics', () => {
201
- it('should call onMetrics handler', async () => {
202
- const onMetrics = vi.fn();
203
- const receiverWithHandler = new OTLPReceiver({
204
- telescope,
205
- onMetrics,
206
- });
207
-
208
- const request: ExportMetricsServiceRequest = {
209
- resourceMetrics: [
210
- {
211
- scopeMetrics: [
212
- {
213
- metrics: [
214
- {
215
- name: 'http_requests_total',
216
- sum: {
217
- dataPoints: [
218
- {
219
- timeUnixNano: '1704067200000000000',
220
- asInt: '100',
221
- },
222
- ],
223
- aggregationTemporality: 2,
224
- },
225
- },
226
- ],
227
- },
228
- ],
229
- },
230
- ],
231
- };
232
-
233
- const response = await receiverWithHandler.receiveMetrics(request);
234
-
235
- expect(response.partialSuccess).toBeUndefined();
236
- expect(onMetrics).toHaveBeenCalledTimes(1);
237
- expect(onMetrics).toHaveBeenCalledWith(
238
- expect.arrayContaining([
239
- expect.objectContaining({
240
- name: 'http_requests_total',
241
- value: 100,
242
- type: 'sum',
243
- }),
244
- ]),
245
- );
246
- });
247
-
248
- it('should log metrics when logMetrics is enabled', async () => {
249
- const receiverWithLogging = new OTLPReceiver({
250
- telescope,
251
- logMetrics: true,
252
- });
253
-
254
- const request: ExportMetricsServiceRequest = {
255
- resourceMetrics: [
256
- {
257
- scopeMetrics: [
258
- {
259
- metrics: [
260
- {
261
- name: 'cpu_usage',
262
- gauge: {
263
- dataPoints: [
264
- {
265
- timeUnixNano: '1704067200000000000',
266
- asDouble: 45.5,
267
- },
268
- ],
269
- },
270
- },
271
- ],
272
- },
273
- ],
274
- },
275
- ],
276
- };
277
-
278
- await receiverWithLogging.receiveMetrics(request);
279
-
280
- const logs = await telescope.getLogs();
281
- expect(logs).toHaveLength(1);
282
- expect(logs[0].message).toBe('OTLP Metric: cpu_usage');
283
- expect(logs[0].level).toBe('debug');
284
- });
285
-
286
- it('should return empty response when no handler', async () => {
287
- const response = await receiver.receiveMetrics({});
288
- expect(response).toEqual({});
289
- });
290
-
291
- it('should report partial success on handler error', async () => {
292
- const onMetrics = vi.fn().mockRejectedValue(new Error('Handler failed'));
293
- const receiverWithHandler = new OTLPReceiver({
294
- telescope,
295
- onMetrics,
296
- });
297
-
298
- const request: ExportMetricsServiceRequest = {
299
- resourceMetrics: [
300
- {
301
- scopeMetrics: [
302
- {
303
- metrics: [
304
- {
305
- name: 'test_metric',
306
- gauge: {
307
- dataPoints: [
308
- {
309
- timeUnixNano: '1704067200000000000',
310
- asDouble: 1,
311
- },
312
- ],
313
- },
314
- },
315
- ],
316
- },
317
- ],
318
- },
319
- ],
320
- };
321
-
322
- const response = await receiverWithHandler.receiveMetrics(request);
323
-
324
- expect(response.partialSuccess).toBeDefined();
325
- expect(response.partialSuccess?.rejectedDataPoints).toBe('1');
326
- });
327
- });
328
-
329
- describe('integration with Telescope', () => {
330
- it('should update metrics aggregator via recordRequest', async () => {
331
- const request: ExportTraceServiceRequest = {
332
- resourceSpans: [
333
- {
334
- scopeSpans: [
335
- {
336
- spans: [
337
- {
338
- traceId: 'abc123',
339
- spanId: 'def456',
340
- name: 'GET /api/test',
341
- kind: SpanKind.SPAN_KIND_SERVER,
342
- startTimeUnixNano: '1704067200000000000',
343
- endTimeUnixNano: '1704067200100000000',
344
- attributes: [
345
- { key: 'http.method', value: { stringValue: 'GET' } },
346
- {
347
- key: 'http.target',
348
- value: { stringValue: '/api/test' },
349
- },
350
- { key: 'http.status_code', value: { intValue: '200' } },
351
- ],
352
- },
353
- ],
354
- },
355
- ],
356
- },
357
- ],
358
- };
359
-
360
- await receiver.receiveTraces(request);
361
-
362
- // The Telescope's metrics aggregator should have the request
363
- const metrics = telescope.getMetrics();
364
- expect(metrics.totalRequests).toBe(1);
365
- });
366
- });
367
- });