navis.js 2.0.0 → 3.0.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/LICENSE +1 -1
- package/README.md +69 -10
- package/bin/navis.js +36 -0
- package/examples/v3-features-demo.js +226 -0
- package/package.json +2 -2
- package/src/index.js +20 -0
- package/src/messaging/base-messaging.js +82 -0
- package/src/messaging/kafka-adapter.js +152 -0
- package/src/messaging/nats-adapter.js +141 -0
- package/src/messaging/sqs-adapter.js +175 -0
- package/src/observability/logger.js +141 -0
- package/src/observability/metrics.js +193 -0
- package/src/observability/tracer.js +205 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Distributed Tracer
|
|
3
|
+
* v3: Basic distributed tracing support
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class Tracer {
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
this.serviceName = options.serviceName || 'navis-service';
|
|
9
|
+
this.enabled = options.enabled !== false;
|
|
10
|
+
this.spans = new Map();
|
|
11
|
+
this.traceId = null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Start a new trace
|
|
16
|
+
* @param {string} operationName - Operation name
|
|
17
|
+
* @param {Object} context - Trace context
|
|
18
|
+
* @returns {string} - Trace ID
|
|
19
|
+
*/
|
|
20
|
+
startTrace(operationName, context = {}) {
|
|
21
|
+
if (!this.enabled) return null;
|
|
22
|
+
|
|
23
|
+
const traceId = this._generateId();
|
|
24
|
+
const spanId = this._generateId();
|
|
25
|
+
|
|
26
|
+
this.traceId = traceId;
|
|
27
|
+
|
|
28
|
+
const span = {
|
|
29
|
+
traceId,
|
|
30
|
+
spanId,
|
|
31
|
+
operationName,
|
|
32
|
+
startTime: Date.now(),
|
|
33
|
+
tags: context.tags || {},
|
|
34
|
+
logs: [],
|
|
35
|
+
childSpans: [],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
this.spans.set(spanId, span);
|
|
39
|
+
return traceId;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Start a new span
|
|
44
|
+
* @param {string} operationName - Operation name
|
|
45
|
+
* @param {Object} options - Span options
|
|
46
|
+
* @returns {string} - Span ID
|
|
47
|
+
*/
|
|
48
|
+
startSpan(operationName, options = {}) {
|
|
49
|
+
if (!this.enabled) return null;
|
|
50
|
+
|
|
51
|
+
const spanId = this._generateId();
|
|
52
|
+
const parentSpanId = options.parentSpanId || this._getCurrentSpanId();
|
|
53
|
+
const traceId = options.traceId || this.traceId || this.startTrace(operationName);
|
|
54
|
+
|
|
55
|
+
const span = {
|
|
56
|
+
traceId,
|
|
57
|
+
spanId,
|
|
58
|
+
parentSpanId,
|
|
59
|
+
operationName,
|
|
60
|
+
startTime: Date.now(),
|
|
61
|
+
tags: options.tags || {},
|
|
62
|
+
logs: [],
|
|
63
|
+
childSpans: [],
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
this.spans.set(spanId, span);
|
|
67
|
+
|
|
68
|
+
// Add as child span if parent exists
|
|
69
|
+
if (parentSpanId) {
|
|
70
|
+
const parentSpan = this.spans.get(parentSpanId);
|
|
71
|
+
if (parentSpan) {
|
|
72
|
+
parentSpan.childSpans.push(spanId);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return spanId;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Finish a span
|
|
81
|
+
* @param {string} spanId - Span ID
|
|
82
|
+
* @param {Object} options - Finish options
|
|
83
|
+
*/
|
|
84
|
+
finishSpan(spanId, options = {}) {
|
|
85
|
+
if (!this.enabled || !spanId) return;
|
|
86
|
+
|
|
87
|
+
const span = this.spans.get(spanId);
|
|
88
|
+
if (!span) return;
|
|
89
|
+
|
|
90
|
+
span.endTime = Date.now();
|
|
91
|
+
span.duration = span.endTime - span.startTime;
|
|
92
|
+
span.status = options.status || 'ok';
|
|
93
|
+
span.error = options.error || null;
|
|
94
|
+
|
|
95
|
+
if (options.tags) {
|
|
96
|
+
span.tags = { ...span.tags, ...options.tags };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Add tag to span
|
|
102
|
+
* @param {string} spanId - Span ID
|
|
103
|
+
* @param {string} key - Tag key
|
|
104
|
+
* @param {*} value - Tag value
|
|
105
|
+
*/
|
|
106
|
+
addTag(spanId, key, value) {
|
|
107
|
+
if (!this.enabled || !spanId) return;
|
|
108
|
+
|
|
109
|
+
const span = this.spans.get(spanId);
|
|
110
|
+
if (span) {
|
|
111
|
+
span.tags[key] = value;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Add log to span
|
|
117
|
+
* @param {string} spanId - Span ID
|
|
118
|
+
* @param {string} message - Log message
|
|
119
|
+
* @param {Object} fields - Log fields
|
|
120
|
+
*/
|
|
121
|
+
addLog(spanId, message, fields = {}) {
|
|
122
|
+
if (!this.enabled || !spanId) return;
|
|
123
|
+
|
|
124
|
+
const span = this.spans.get(spanId);
|
|
125
|
+
if (span) {
|
|
126
|
+
span.logs.push({
|
|
127
|
+
timestamp: Date.now(),
|
|
128
|
+
message,
|
|
129
|
+
fields,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Get trace data
|
|
136
|
+
* @param {string} traceId - Trace ID
|
|
137
|
+
* @returns {Object} - Trace data
|
|
138
|
+
*/
|
|
139
|
+
getTrace(traceId) {
|
|
140
|
+
const traceSpans = Array.from(this.spans.values()).filter(
|
|
141
|
+
span => span.traceId === traceId
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
traceId,
|
|
146
|
+
spans: traceSpans,
|
|
147
|
+
duration: traceSpans.length > 0
|
|
148
|
+
? Math.max(...traceSpans.map(s => s.endTime || Date.now())) -
|
|
149
|
+
Math.min(...traceSpans.map(s => s.startTime))
|
|
150
|
+
: 0,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get all traces
|
|
156
|
+
* @returns {Array} - All traces
|
|
157
|
+
*/
|
|
158
|
+
getAllTraces() {
|
|
159
|
+
const traceIds = new Set();
|
|
160
|
+
for (const span of this.spans.values()) {
|
|
161
|
+
traceIds.add(span.traceId);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return Array.from(traceIds).map(traceId => this.getTrace(traceId));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Clear old spans (keep last N)
|
|
169
|
+
* @param {number} keepCount - Number of spans to keep
|
|
170
|
+
*/
|
|
171
|
+
clearOldSpans(keepCount = 1000) {
|
|
172
|
+
const spans = Array.from(this.spans.entries());
|
|
173
|
+
if (spans.length > keepCount) {
|
|
174
|
+
// Sort by start time and keep most recent
|
|
175
|
+
spans.sort((a, b) => b[1].startTime - a[1].startTime);
|
|
176
|
+
const toKeep = spans.slice(0, keepCount);
|
|
177
|
+
this.spans.clear();
|
|
178
|
+
for (const [id, span] of toKeep) {
|
|
179
|
+
this.spans.set(id, span);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Get current span ID
|
|
186
|
+
* @private
|
|
187
|
+
*/
|
|
188
|
+
_getCurrentSpanId() {
|
|
189
|
+
// Return the most recently created span
|
|
190
|
+
const spans = Array.from(this.spans.values());
|
|
191
|
+
if (spans.length === 0) return null;
|
|
192
|
+
return spans[spans.length - 1].spanId;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Generate unique ID
|
|
197
|
+
* @private
|
|
198
|
+
*/
|
|
199
|
+
_generateId() {
|
|
200
|
+
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
module.exports = Tracer;
|
|
205
|
+
|