neatlogs 1.1.19 → 1.1.21
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 +34 -2
- package/dist/ai-sdk.cjs +186 -11
- package/dist/ai-sdk.cjs.map +1 -1
- package/dist/ai-sdk.d.ts +17 -7
- package/dist/ai-sdk.mjs +189 -12
- package/dist/ai-sdk.mjs.map +1 -1
- package/dist/anthropic.cjs +31 -14
- package/dist/anthropic.cjs.map +1 -1
- package/dist/anthropic.mjs +31 -14
- package/dist/anthropic.mjs.map +1 -1
- package/dist/azure-openai.cjs +31 -14
- package/dist/azure-openai.cjs.map +1 -1
- package/dist/azure-openai.mjs +31 -14
- package/dist/azure-openai.mjs.map +1 -1
- package/dist/bedrock.cjs +31 -14
- package/dist/bedrock.cjs.map +1 -1
- package/dist/bedrock.mjs +31 -14
- package/dist/bedrock.mjs.map +1 -1
- package/dist/browser.cjs +26 -1
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.mjs +26 -1
- package/dist/browser.mjs.map +1 -1
- package/dist/claude-agent-sdk.cjs +2 -2
- package/dist/claude-agent-sdk.cjs.map +1 -1
- package/dist/claude-agent-sdk.mjs +2 -2
- package/dist/claude-agent-sdk.mjs.map +1 -1
- package/dist/cli.cjs +452 -200
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +427 -173
- package/dist/cli.mjs.map +1 -1
- package/dist/google-genai.cjs +31 -14
- package/dist/google-genai.cjs.map +1 -1
- package/dist/google-genai.mjs +31 -14
- package/dist/google-genai.mjs.map +1 -1
- package/dist/index.cjs +652 -333
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +15 -6
- package/dist/index.mjs +505 -184
- package/dist/index.mjs.map +1 -1
- package/dist/langchain.cjs +3 -3
- package/dist/langchain.cjs.map +1 -1
- package/dist/langchain.mjs +3 -3
- package/dist/langchain.mjs.map +1 -1
- package/dist/mastra-wrap.cjs +2 -2
- package/dist/mastra-wrap.cjs.map +1 -1
- package/dist/mastra-wrap.mjs +2 -2
- package/dist/mastra-wrap.mjs.map +1 -1
- package/dist/openai-agents.cjs.map +1 -1
- package/dist/openai-agents.mjs.map +1 -1
- package/dist/openai.cjs +31 -14
- package/dist/openai.cjs.map +1 -1
- package/dist/openai.mjs +31 -14
- package/dist/openai.mjs.map +1 -1
- package/dist/opencode-plugin.cjs +1 -1
- package/dist/opencode-plugin.cjs.map +1 -1
- package/dist/opencode-plugin.mjs +1 -1
- package/dist/opencode-plugin.mjs.map +1 -1
- package/dist/openrouter-agent.cjs +31 -14
- package/dist/openrouter-agent.cjs.map +1 -1
- package/dist/openrouter-agent.mjs +31 -14
- package/dist/openrouter-agent.mjs.map +1 -1
- package/dist/pi-agent.cjs.map +1 -1
- package/dist/pi-agent.mjs.map +1 -1
- package/dist/vertex-ai.cjs +31 -14
- package/dist/vertex-ai.cjs.map +1 -1
- package/dist/vertex-ai.mjs +31 -14
- package/dist/vertex-ai.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -58,6 +58,110 @@ function enableLogging() {
|
|
|
58
58
|
_disabled = false;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
// src/span-kinds/constants.ts
|
|
62
|
+
var VECTOR_DB_SYSTEMS = /* @__PURE__ */ new Set([
|
|
63
|
+
"chroma",
|
|
64
|
+
"chromadb",
|
|
65
|
+
"pinecone",
|
|
66
|
+
"qdrant",
|
|
67
|
+
"milvus",
|
|
68
|
+
"marqo",
|
|
69
|
+
"weaviate",
|
|
70
|
+
"lancedb",
|
|
71
|
+
"pgvector",
|
|
72
|
+
"elasticsearch"
|
|
73
|
+
]);
|
|
74
|
+
var RETRIEVAL_OPS = [
|
|
75
|
+
"query",
|
|
76
|
+
"search",
|
|
77
|
+
"get",
|
|
78
|
+
"fetch",
|
|
79
|
+
"find",
|
|
80
|
+
"retrieve",
|
|
81
|
+
"scroll",
|
|
82
|
+
"peek",
|
|
83
|
+
"discover",
|
|
84
|
+
"recommend",
|
|
85
|
+
"aggregate",
|
|
86
|
+
"hybrid_search",
|
|
87
|
+
"select"
|
|
88
|
+
];
|
|
89
|
+
var VECTOR_DB_NAMES = [
|
|
90
|
+
"chroma",
|
|
91
|
+
"pinecone",
|
|
92
|
+
"weaviate",
|
|
93
|
+
"qdrant",
|
|
94
|
+
"milvus",
|
|
95
|
+
"lancedb",
|
|
96
|
+
"marqo"
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
// src/span-kinds/mapping.ts
|
|
100
|
+
var VALID_SPAN_KINDS = /* @__PURE__ */ new Set([
|
|
101
|
+
"WORKFLOW",
|
|
102
|
+
"AGENT",
|
|
103
|
+
"CHAIN",
|
|
104
|
+
"TOOL",
|
|
105
|
+
"RETRIEVER",
|
|
106
|
+
"EMBEDDING",
|
|
107
|
+
"MCP_TOOL",
|
|
108
|
+
"GUARDRAIL",
|
|
109
|
+
"EVALUATOR"
|
|
110
|
+
]);
|
|
111
|
+
var ALL_SPAN_KINDS = /* @__PURE__ */ new Set([
|
|
112
|
+
...VALID_SPAN_KINDS,
|
|
113
|
+
"LLM",
|
|
114
|
+
"RERANKER",
|
|
115
|
+
"VECTOR_STORE"
|
|
116
|
+
]);
|
|
117
|
+
function resolveExplicitSpanKind(attributes) {
|
|
118
|
+
for (const key of [
|
|
119
|
+
"neatlogs.span.kind",
|
|
120
|
+
"openinference.span.kind",
|
|
121
|
+
"traceloop.span.kind"
|
|
122
|
+
]) {
|
|
123
|
+
const value = String(attributes[key] ?? "").trim();
|
|
124
|
+
if (value) return value;
|
|
125
|
+
}
|
|
126
|
+
return "";
|
|
127
|
+
}
|
|
128
|
+
function inferSpanKindFromName(spanName) {
|
|
129
|
+
const nameLower = spanName.toLowerCase();
|
|
130
|
+
if (["openai", "anthropic", "cohere", "bedrock", "chat", "completion", "llm", "gemini", "google_genai"].some(
|
|
131
|
+
(kw) => nameLower.includes(kw)
|
|
132
|
+
)) {
|
|
133
|
+
return "LLM";
|
|
134
|
+
}
|
|
135
|
+
if (nameLower.includes("embed")) {
|
|
136
|
+
return "EMBEDDING";
|
|
137
|
+
}
|
|
138
|
+
if (VECTOR_DB_NAMES.some((db) => nameLower.includes(db))) {
|
|
139
|
+
if (RETRIEVAL_OPS.some((kw) => nameLower.includes(kw))) {
|
|
140
|
+
return "RETRIEVER";
|
|
141
|
+
}
|
|
142
|
+
return "VECTOR_STORE";
|
|
143
|
+
}
|
|
144
|
+
if (["retriev", "search", "query"].some((kw) => nameLower.includes(kw))) {
|
|
145
|
+
return "RETRIEVER";
|
|
146
|
+
}
|
|
147
|
+
if (nameLower.includes("rerank")) {
|
|
148
|
+
return "RERANKER";
|
|
149
|
+
}
|
|
150
|
+
if (nameLower.includes("agent")) {
|
|
151
|
+
return "AGENT";
|
|
152
|
+
}
|
|
153
|
+
if (["tool", "function"].some((kw) => nameLower.includes(kw))) {
|
|
154
|
+
return "TOOL";
|
|
155
|
+
}
|
|
156
|
+
if (["guardrail", "validate", "moderate", "safety"].some((kw) => nameLower.includes(kw))) {
|
|
157
|
+
return "GUARDRAIL";
|
|
158
|
+
}
|
|
159
|
+
if (["evaluat", "score", "metric"].some((kw) => nameLower.includes(kw))) {
|
|
160
|
+
return "EVALUATOR";
|
|
161
|
+
}
|
|
162
|
+
return "CHAIN";
|
|
163
|
+
}
|
|
164
|
+
|
|
61
165
|
// src/config/attribute-mapping.json
|
|
62
166
|
var attribute_mapping_default = {
|
|
63
167
|
$schema: "http://json-schema.org/draft-07/schema#",
|
|
@@ -1235,27 +1339,11 @@ var AttributeMapper = class {
|
|
|
1235
1339
|
/** Extract and normalize span kind from multiple possible sources. */
|
|
1236
1340
|
mapSpanKind(attributes) {
|
|
1237
1341
|
const spanKindConfig = this.mappings.span_kind ?? {};
|
|
1238
|
-
const sources = spanKindConfig.sources ?? [];
|
|
1239
1342
|
const valuesMap = spanKindConfig.values ?? {};
|
|
1240
|
-
const
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
if (
|
|
1244
|
-
spanKindValue = attributes["openinference.span.kind"];
|
|
1245
|
-
}
|
|
1246
|
-
} else {
|
|
1247
|
-
for (const source of sources) {
|
|
1248
|
-
if (source in attributes) {
|
|
1249
|
-
spanKindValue = attributes[source];
|
|
1250
|
-
break;
|
|
1251
|
-
}
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
if (!spanKindValue && "neatlogs.span.kind" in attributes) {
|
|
1255
|
-
spanKindValue = attributes["neatlogs.span.kind"];
|
|
1256
|
-
}
|
|
1257
|
-
if (spanKindValue && spanKindValue in valuesMap) {
|
|
1258
|
-
return valuesMap[spanKindValue];
|
|
1343
|
+
const spanKindValue = resolveExplicitSpanKind(attributes);
|
|
1344
|
+
if (spanKindValue) {
|
|
1345
|
+
const mapped = valuesMap[spanKindValue] ?? valuesMap[spanKindValue.toUpperCase()];
|
|
1346
|
+
if (mapped) return mapped;
|
|
1259
1347
|
}
|
|
1260
1348
|
if (spanKindValue && Object.values(valuesMap).includes(spanKindValue)) {
|
|
1261
1349
|
return spanKindValue;
|
|
@@ -1829,7 +1917,7 @@ function clearDoctorCapture() {
|
|
|
1829
1917
|
|
|
1830
1918
|
// src/core/context.ts
|
|
1831
1919
|
import {
|
|
1832
|
-
trace as
|
|
1920
|
+
trace as otelTrace3,
|
|
1833
1921
|
createContextKey as createContextKey2,
|
|
1834
1922
|
ROOT_CONTEXT as ROOT_CONTEXT3,
|
|
1835
1923
|
SpanStatusCode as SpanStatusCode3
|
|
@@ -2177,11 +2265,11 @@ function getNeatlogsRootSpan() {
|
|
|
2177
2265
|
function getNeatlogsParentContext(baseContext) {
|
|
2178
2266
|
return baseContext ?? getNeatlogsActiveContext();
|
|
2179
2267
|
}
|
|
2180
|
-
function withNeatlogsSpan(span2, fn, baseContext) {
|
|
2268
|
+
function withNeatlogsSpan(span2, fn, baseContext, rootSpan) {
|
|
2181
2269
|
const base = baseContext ?? getNeatlogsActiveContext();
|
|
2182
2270
|
let ctx = otelTrace.setSpan(base, span2);
|
|
2183
2271
|
if (base.getValue(NEATLOGS_ROOT_SPAN_KEY) === void 0) {
|
|
2184
|
-
ctx = ctx.setValue(NEATLOGS_ROOT_SPAN_KEY, span2);
|
|
2272
|
+
ctx = ctx.setValue(NEATLOGS_ROOT_SPAN_KEY, rootSpan ?? span2);
|
|
2185
2273
|
}
|
|
2186
2274
|
return privateContextStorage.run(ctx, fn);
|
|
2187
2275
|
}
|
|
@@ -3913,6 +4001,84 @@ function discardPendingMediaOwner(owner) {
|
|
|
3913
4001
|
pendingMediaOwners.delete(owner ?? defaultPendingMediaOwner);
|
|
3914
4002
|
}
|
|
3915
4003
|
|
|
4004
|
+
// src/core/auto-root.ts
|
|
4005
|
+
import {
|
|
4006
|
+
trace as otelTrace2
|
|
4007
|
+
} from "@opentelemetry/api";
|
|
4008
|
+
var boundAutoRoots = /* @__PURE__ */ new WeakMap();
|
|
4009
|
+
var ROOT_KINDS = /* @__PURE__ */ new Set(["workflow", "chain", "agent", "mcp_tool", "llm"]);
|
|
4010
|
+
function autoRootEnabled() {
|
|
4011
|
+
const val = (process.env.NEATLOGS_AUTO_ROOT ?? "").trim().toLowerCase();
|
|
4012
|
+
return !["false", "0", "no", "off"].includes(val);
|
|
4013
|
+
}
|
|
4014
|
+
function resolveRootWorkflowName() {
|
|
4015
|
+
const clientName = getActiveClient()?.workflowName;
|
|
4016
|
+
if (typeof clientName === "string" && clientName.trim()) return clientName;
|
|
4017
|
+
try {
|
|
4018
|
+
const name = getSessionConfig()?.workflowName;
|
|
4019
|
+
if (typeof name === "string" && name.trim()) return name;
|
|
4020
|
+
} catch {
|
|
4021
|
+
}
|
|
4022
|
+
return "workflow";
|
|
4023
|
+
}
|
|
4024
|
+
function stampRootIdentity(root) {
|
|
4025
|
+
applySessionAttributes(root, void 0, true);
|
|
4026
|
+
applyEndUserAttributes(root, void 0, void 0, true);
|
|
4027
|
+
}
|
|
4028
|
+
function bindSpanToAutoRoot(child, root) {
|
|
4029
|
+
if (!root) return child;
|
|
4030
|
+
let ended = false;
|
|
4031
|
+
const proxy = new Proxy(child, {
|
|
4032
|
+
get(target, prop, _receiver) {
|
|
4033
|
+
if (prop === "end") {
|
|
4034
|
+
return (...args) => {
|
|
4035
|
+
if (ended) return;
|
|
4036
|
+
ended = true;
|
|
4037
|
+
try {
|
|
4038
|
+
target.end(...args);
|
|
4039
|
+
} finally {
|
|
4040
|
+
try {
|
|
4041
|
+
root.end();
|
|
4042
|
+
} catch {
|
|
4043
|
+
}
|
|
4044
|
+
}
|
|
4045
|
+
};
|
|
4046
|
+
}
|
|
4047
|
+
const value = Reflect.get(target, prop, target);
|
|
4048
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
4049
|
+
}
|
|
4050
|
+
});
|
|
4051
|
+
boundAutoRoots.set(proxy, root);
|
|
4052
|
+
aliasMediaCaptureSpan(proxy, child);
|
|
4053
|
+
return proxy;
|
|
4054
|
+
}
|
|
4055
|
+
function maybeOpenAutoRoot(tracer, kind, baseCtx) {
|
|
4056
|
+
const k = String(kind ?? "").toLowerCase();
|
|
4057
|
+
const existing = getActiveNeatlogsSpan();
|
|
4058
|
+
if (!autoRootEnabled() || ROOT_KINDS.has(k) || existing !== void 0 && existing.isRecording()) {
|
|
4059
|
+
return { root: void 0, ctx: baseCtx };
|
|
4060
|
+
}
|
|
4061
|
+
const root = tracer.startSpan(
|
|
4062
|
+
resolveRootWorkflowName(),
|
|
4063
|
+
{ attributes: { "neatlogs.span.kind": "workflow", "neatlogs.auto_root": true } },
|
|
4064
|
+
baseCtx
|
|
4065
|
+
);
|
|
4066
|
+
stampRootIdentity(root);
|
|
4067
|
+
return { root, ctx: otelTrace2.setSpan(baseCtx, root) };
|
|
4068
|
+
}
|
|
4069
|
+
|
|
4070
|
+
// src/errors.ts
|
|
4071
|
+
var NeatlogsConfigurationError = class extends TypeError {
|
|
4072
|
+
code;
|
|
4073
|
+
option;
|
|
4074
|
+
constructor(code, option, message) {
|
|
4075
|
+
super(message);
|
|
4076
|
+
this.name = "NeatlogsConfigurationError";
|
|
4077
|
+
this.code = code;
|
|
4078
|
+
this.option = option;
|
|
4079
|
+
}
|
|
4080
|
+
};
|
|
4081
|
+
|
|
3916
4082
|
// src/decorators/base.ts
|
|
3917
4083
|
import {
|
|
3918
4084
|
ROOT_CONTEXT as ROOT_CONTEXT2,
|
|
@@ -3990,7 +4156,28 @@ function decorateSpan(opts, fn) {
|
|
|
3990
4156
|
const tracer = getNeatlogsTracer(TRACER_NAME);
|
|
3991
4157
|
const isRoot = isRootSpan();
|
|
3992
4158
|
const parentContext = isRoot ? ROOT_CONTEXT2 : getNeatlogsParentContext();
|
|
3993
|
-
const
|
|
4159
|
+
const autoRoot = maybeOpenAutoRoot(
|
|
4160
|
+
tracer,
|
|
4161
|
+
String(opts.kind ?? ""),
|
|
4162
|
+
parentContext
|
|
4163
|
+
);
|
|
4164
|
+
if (autoRoot.root) {
|
|
4165
|
+
applySessionAttributes(autoRoot.root, opts.sessionId, true, {
|
|
4166
|
+
parentSessionId: opts.parentSessionId,
|
|
4167
|
+
sessionFeatureName: opts.sessionFeatureName,
|
|
4168
|
+
sessionEntryPoint: opts.sessionEntryPoint
|
|
4169
|
+
});
|
|
4170
|
+
applyEndUserAttributes(
|
|
4171
|
+
autoRoot.root,
|
|
4172
|
+
opts.endUserId,
|
|
4173
|
+
opts.endUserMetadata,
|
|
4174
|
+
true
|
|
4175
|
+
);
|
|
4176
|
+
}
|
|
4177
|
+
const span2 = bindSpanToAutoRoot(
|
|
4178
|
+
tracer.startSpan(spanName, {}, autoRoot.ctx),
|
|
4179
|
+
autoRoot.root
|
|
4180
|
+
);
|
|
3994
4181
|
return withNeatlogsSpan(span2, () => {
|
|
3995
4182
|
let finished = false;
|
|
3996
4183
|
let streamInterrupted = false;
|
|
@@ -4359,7 +4546,8 @@ function decorateSpan(opts, fn) {
|
|
|
4359
4546
|
});
|
|
4360
4547
|
try {
|
|
4361
4548
|
setCommonSpanAttrs(span2, opts);
|
|
4362
|
-
|
|
4549
|
+
const spanIsRoot = isRoot && !autoRoot.root;
|
|
4550
|
+
applySessionAttributes(span2, opts.sessionId, spanIsRoot, {
|
|
4363
4551
|
parentSessionId: opts.parentSessionId,
|
|
4364
4552
|
sessionFeatureName: opts.sessionFeatureName,
|
|
4365
4553
|
sessionEntryPoint: opts.sessionEntryPoint
|
|
@@ -4368,7 +4556,7 @@ function decorateSpan(opts, fn) {
|
|
|
4368
4556
|
span2,
|
|
4369
4557
|
opts.endUserId,
|
|
4370
4558
|
opts.endUserMetadata,
|
|
4371
|
-
|
|
4559
|
+
spanIsRoot
|
|
4372
4560
|
);
|
|
4373
4561
|
if (captureInput && args.length > 0) {
|
|
4374
4562
|
try {
|
|
@@ -4408,7 +4596,7 @@ function decorateSpan(opts, fn) {
|
|
|
4408
4596
|
finishError(error);
|
|
4409
4597
|
throw error;
|
|
4410
4598
|
}
|
|
4411
|
-
});
|
|
4599
|
+
}, autoRoot.ctx, autoRoot.root);
|
|
4412
4600
|
};
|
|
4413
4601
|
Object.defineProperty(wrapped, "name", {
|
|
4414
4602
|
value: spanName,
|
|
@@ -4437,6 +4625,9 @@ var _sessionConfig = {};
|
|
|
4437
4625
|
function _setSessionConfig(config) {
|
|
4438
4626
|
_sessionConfig = config;
|
|
4439
4627
|
}
|
|
4628
|
+
function getSessionConfig() {
|
|
4629
|
+
return { ..._sessionConfig };
|
|
4630
|
+
}
|
|
4440
4631
|
var PROMPT_VARIABLES_KEY = createContextKey2("neatlogs.prompt_variables");
|
|
4441
4632
|
var PROMPT_TEMPLATE_KEY = createContextKey2("neatlogs.prompt_template");
|
|
4442
4633
|
var PROMPT_VERSION_KEY = createContextKey2("neatlogs.prompt_version");
|
|
@@ -4460,6 +4651,20 @@ var KNOWN_OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
|
4460
4651
|
"endUserId",
|
|
4461
4652
|
"endUserMetadata"
|
|
4462
4653
|
]);
|
|
4654
|
+
function assertNoHttpSpanKind(kind, attributes) {
|
|
4655
|
+
const candidates = [
|
|
4656
|
+
kind,
|
|
4657
|
+
attributes?.["neatlogs.span.kind"],
|
|
4658
|
+
attributes?.["openinference.span.kind"]
|
|
4659
|
+
];
|
|
4660
|
+
if (candidates.some((value) => String(value ?? "").trim().toUpperCase() === "HTTP")) {
|
|
4661
|
+
throw new NeatlogsConfigurationError(
|
|
4662
|
+
"UNSUPPORTED_SPAN_KIND",
|
|
4663
|
+
"kind",
|
|
4664
|
+
"HTTP spans are not supported. Trace the semantic LLM, TOOL, RETRIEVER, or other AI operation instead."
|
|
4665
|
+
);
|
|
4666
|
+
}
|
|
4667
|
+
}
|
|
4463
4668
|
function _setSpanAttributes(span2, kind, attributes) {
|
|
4464
4669
|
if (kind?.toUpperCase() === "LLM") {
|
|
4465
4670
|
span2.setAttribute("neatlogs.internal", true);
|
|
@@ -4530,6 +4735,7 @@ async function trace(options, fn) {
|
|
|
4530
4735
|
attributes: explicitAttributes,
|
|
4531
4736
|
...extraOptions
|
|
4532
4737
|
} = options;
|
|
4738
|
+
assertNoHttpSpanKind(kind, explicitAttributes);
|
|
4533
4739
|
const sessionId = sessionIdOption ?? currentSessionId();
|
|
4534
4740
|
const currentSpan = getActiveNeatlogsSpan();
|
|
4535
4741
|
const rootSpan = getNeatlogsRootSpan();
|
|
@@ -4574,7 +4780,7 @@ async function trace(options, fn) {
|
|
|
4574
4780
|
}
|
|
4575
4781
|
let ctx = getNeatlogsActiveContext();
|
|
4576
4782
|
if (shouldFallbackToRoot) {
|
|
4577
|
-
ctx =
|
|
4783
|
+
ctx = otelTrace3.setSpan(ctx, rootSpan);
|
|
4578
4784
|
}
|
|
4579
4785
|
const variablesJson = promptVariables ? JSON.stringify(promptVariables) : void 0;
|
|
4580
4786
|
const userVariablesJson = userPromptVariables ? JSON.stringify(userPromptVariables) : void 0;
|
|
@@ -4608,12 +4814,13 @@ async function trace(options, fn) {
|
|
|
4608
4814
|
const tracer = getNeatlogsTracer("neatlogs.trace");
|
|
4609
4815
|
const spanCallback = async (span3) => {
|
|
4610
4816
|
_setSpanAttributes(span3, kind, extraAttributes);
|
|
4611
|
-
|
|
4817
|
+
const spanIsRoot = isRootTrace && !autoRoot.root;
|
|
4818
|
+
applySessionAttributes(span3, sessionId, spanIsRoot, {
|
|
4612
4819
|
parentSessionId,
|
|
4613
4820
|
sessionFeatureName,
|
|
4614
4821
|
sessionEntryPoint
|
|
4615
4822
|
});
|
|
4616
|
-
applyEndUserAttributes(span3, endUserId, endUserMetadata,
|
|
4823
|
+
applyEndUserAttributes(span3, endUserId, endUserMetadata, spanIsRoot);
|
|
4617
4824
|
if (input !== void 0 && input !== null) {
|
|
4618
4825
|
const capturedInput = captureMedia(
|
|
4619
4826
|
span3,
|
|
@@ -4665,106 +4872,34 @@ async function trace(options, fn) {
|
|
|
4665
4872
|
};
|
|
4666
4873
|
const parentContext = shouldCreateRootTrace ? ROOT_CONTEXT3 : getNeatlogsParentContext(ctx);
|
|
4667
4874
|
const startAttributes = kind ? { "openinference.span.kind": kind } : void 0;
|
|
4668
|
-
const
|
|
4875
|
+
const autoRoot = isRootTrace ? maybeOpenAutoRoot(tracer, String(kind ?? "CHAIN"), parentContext) : { root: void 0, ctx: parentContext };
|
|
4876
|
+
if (autoRoot.root) {
|
|
4877
|
+
applySessionAttributes(autoRoot.root, sessionId, true, {
|
|
4878
|
+
parentSessionId,
|
|
4879
|
+
sessionFeatureName,
|
|
4880
|
+
sessionEntryPoint
|
|
4881
|
+
});
|
|
4882
|
+
applyEndUserAttributes(autoRoot.root, endUserId, endUserMetadata, true);
|
|
4883
|
+
}
|
|
4884
|
+
const span2 = bindSpanToAutoRoot(
|
|
4885
|
+
tracer.startSpan(
|
|
4886
|
+
name,
|
|
4887
|
+
startAttributes ? { attributes: startAttributes } : {},
|
|
4888
|
+
autoRoot.ctx
|
|
4889
|
+
),
|
|
4890
|
+
autoRoot.root
|
|
4891
|
+
);
|
|
4669
4892
|
if (shouldCreateRootTrace) {
|
|
4670
4893
|
logger6.debug(`[trace] Creating NEW root trace '${name}' (sessionId=${sessionId})`);
|
|
4671
4894
|
} else {
|
|
4672
4895
|
logger6.debug(`[trace] Creating child span '${name}'`);
|
|
4673
4896
|
}
|
|
4674
|
-
return withNeatlogsSpan(
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
"chromadb",
|
|
4681
|
-
"pinecone",
|
|
4682
|
-
"qdrant",
|
|
4683
|
-
"milvus",
|
|
4684
|
-
"marqo",
|
|
4685
|
-
"weaviate",
|
|
4686
|
-
"lancedb",
|
|
4687
|
-
"pgvector",
|
|
4688
|
-
"elasticsearch"
|
|
4689
|
-
]);
|
|
4690
|
-
var RETRIEVAL_OPS = [
|
|
4691
|
-
"query",
|
|
4692
|
-
"search",
|
|
4693
|
-
"get",
|
|
4694
|
-
"fetch",
|
|
4695
|
-
"find",
|
|
4696
|
-
"retrieve",
|
|
4697
|
-
"scroll",
|
|
4698
|
-
"peek",
|
|
4699
|
-
"discover",
|
|
4700
|
-
"recommend",
|
|
4701
|
-
"aggregate",
|
|
4702
|
-
"hybrid_search",
|
|
4703
|
-
"select"
|
|
4704
|
-
];
|
|
4705
|
-
var VECTOR_DB_NAMES = [
|
|
4706
|
-
"chroma",
|
|
4707
|
-
"pinecone",
|
|
4708
|
-
"weaviate",
|
|
4709
|
-
"qdrant",
|
|
4710
|
-
"milvus",
|
|
4711
|
-
"lancedb",
|
|
4712
|
-
"marqo"
|
|
4713
|
-
];
|
|
4714
|
-
|
|
4715
|
-
// src/span-kinds/mapping.ts
|
|
4716
|
-
var VALID_SPAN_KINDS = /* @__PURE__ */ new Set([
|
|
4717
|
-
"WORKFLOW",
|
|
4718
|
-
"AGENT",
|
|
4719
|
-
"CHAIN",
|
|
4720
|
-
"TOOL",
|
|
4721
|
-
"RETRIEVER",
|
|
4722
|
-
"EMBEDDING",
|
|
4723
|
-
"MCP_TOOL",
|
|
4724
|
-
"GUARDRAIL"
|
|
4725
|
-
]);
|
|
4726
|
-
var ALL_SPAN_KINDS = /* @__PURE__ */ new Set([
|
|
4727
|
-
...VALID_SPAN_KINDS,
|
|
4728
|
-
"LLM",
|
|
4729
|
-
"RERANKER",
|
|
4730
|
-
"EVALUATOR",
|
|
4731
|
-
"VECTOR_STORE"
|
|
4732
|
-
]);
|
|
4733
|
-
function inferSpanKindFromName(spanName) {
|
|
4734
|
-
const nameLower = spanName.toLowerCase();
|
|
4735
|
-
if (["openai", "anthropic", "cohere", "bedrock", "chat", "completion", "llm", "gemini", "google_genai"].some(
|
|
4736
|
-
(kw) => nameLower.includes(kw)
|
|
4737
|
-
)) {
|
|
4738
|
-
return "LLM";
|
|
4739
|
-
}
|
|
4740
|
-
if (nameLower.includes("embed")) {
|
|
4741
|
-
return "EMBEDDING";
|
|
4742
|
-
}
|
|
4743
|
-
if (VECTOR_DB_NAMES.some((db) => nameLower.includes(db))) {
|
|
4744
|
-
if (RETRIEVAL_OPS.some((kw) => nameLower.includes(kw))) {
|
|
4745
|
-
return "RETRIEVER";
|
|
4746
|
-
}
|
|
4747
|
-
return "VECTOR_STORE";
|
|
4748
|
-
}
|
|
4749
|
-
if (["retriev", "search", "query"].some((kw) => nameLower.includes(kw))) {
|
|
4750
|
-
return "RETRIEVER";
|
|
4751
|
-
}
|
|
4752
|
-
if (nameLower.includes("rerank")) {
|
|
4753
|
-
return "RERANKER";
|
|
4754
|
-
}
|
|
4755
|
-
if (nameLower.includes("agent")) {
|
|
4756
|
-
return "AGENT";
|
|
4757
|
-
}
|
|
4758
|
-
if (["tool", "function"].some((kw) => nameLower.includes(kw))) {
|
|
4759
|
-
return "TOOL";
|
|
4760
|
-
}
|
|
4761
|
-
if (["guardrail", "validate", "moderate", "safety"].some((kw) => nameLower.includes(kw))) {
|
|
4762
|
-
return "GUARDRAIL";
|
|
4763
|
-
}
|
|
4764
|
-
if (["evaluat", "score", "metric"].some((kw) => nameLower.includes(kw))) {
|
|
4765
|
-
return "EVALUATOR";
|
|
4766
|
-
}
|
|
4767
|
-
return "CHAIN";
|
|
4897
|
+
return withNeatlogsSpan(
|
|
4898
|
+
span2,
|
|
4899
|
+
() => spanCallback(span2),
|
|
4900
|
+
autoRoot.ctx,
|
|
4901
|
+
autoRoot.root
|
|
4902
|
+
);
|
|
4768
4903
|
}
|
|
4769
4904
|
|
|
4770
4905
|
// src/decorators/orchestration.ts
|
|
@@ -5284,6 +5419,7 @@ var neatlogs_telemetry_schema_default = {
|
|
|
5284
5419
|
"VECTOR_STORE",
|
|
5285
5420
|
"MEMORY",
|
|
5286
5421
|
"GUARDRAIL",
|
|
5422
|
+
"EVALUATOR",
|
|
5287
5423
|
"LOG",
|
|
5288
5424
|
"HTTP",
|
|
5289
5425
|
"UNKNOWN"
|
|
@@ -6712,6 +6848,7 @@ var neatlogs_telemetry_schema_default = {
|
|
|
6712
6848
|
"AGENT",
|
|
6713
6849
|
"CHAIN",
|
|
6714
6850
|
"TASK",
|
|
6851
|
+
"EVALUATOR",
|
|
6715
6852
|
"HTTP",
|
|
6716
6853
|
"UNKNOWN"
|
|
6717
6854
|
]
|
|
@@ -6875,6 +7012,7 @@ var neatlogs_telemetry_schema_default = {
|
|
|
6875
7012
|
"neatlogs.vector_store.*",
|
|
6876
7013
|
"neatlogs.memory.*",
|
|
6877
7014
|
"neatlogs.guardrail.*",
|
|
7015
|
+
"neatlogs.evaluator.*",
|
|
6878
7016
|
"neatlogs.mcp.*",
|
|
6879
7017
|
"neatlogs.code.*"
|
|
6880
7018
|
],
|
|
@@ -6958,7 +7096,7 @@ var TELEMETRY_CONFLICT_PRECEDENCE = Object.freeze(
|
|
|
6958
7096
|
);
|
|
6959
7097
|
|
|
6960
7098
|
// src/version.ts
|
|
6961
|
-
var __version__ = "1.1.
|
|
7099
|
+
var __version__ = "1.1.21";
|
|
6962
7100
|
|
|
6963
7101
|
// src/init.ts
|
|
6964
7102
|
import * as path2 from "path";
|
|
@@ -6980,16 +7118,16 @@ import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-proto";
|
|
|
6980
7118
|
import * as fs from "fs";
|
|
6981
7119
|
import * as path from "path";
|
|
6982
7120
|
import { performance } from "perf_hooks";
|
|
6983
|
-
import { ROOT_CONTEXT as ROOT_CONTEXT4, trace as
|
|
7121
|
+
import { ROOT_CONTEXT as ROOT_CONTEXT4, trace as otelTrace4, TraceFlags, SpanStatusCode as SpanStatusCode4 } from "@opentelemetry/api";
|
|
6984
7122
|
|
|
6985
7123
|
// src/core/instrumentation-scope-parser.ts
|
|
6986
7124
|
var SCOPE_PATTERNS = {
|
|
6987
7125
|
// Neatlogs custom instrumentations
|
|
6988
7126
|
"@neatlogs/instrumentation-google-genai": { provider: "google", framework: "google_genai" },
|
|
6989
7127
|
"@neatlogs/instrumentation-mastra": { framework: "mastra" },
|
|
6990
|
-
"@neatlogs/instrumentation-ai-sdk": { framework: "
|
|
7128
|
+
"@neatlogs/instrumentation-ai-sdk": { framework: "vercel_ai_sdk" },
|
|
6991
7129
|
// Vercel AI SDK native scope (the `ai` package emits this directly)
|
|
6992
|
-
ai: { framework: "
|
|
7130
|
+
ai: { framework: "vercel_ai_sdk" },
|
|
6993
7131
|
// OpenInference instrumentations (npm @arizeai scope names)
|
|
6994
7132
|
"@arizeai/openinference-instrumentation-openai": { provider: "openai", framework: "openai" },
|
|
6995
7133
|
"@arizeai/openinference-instrumentation-anthropic": { provider: "anthropic", framework: "anthropic" },
|
|
@@ -7078,7 +7216,7 @@ function parseInstrumentationScope(scopeName) {
|
|
|
7078
7216
|
} else if (scopeLower.includes("dspy")) {
|
|
7079
7217
|
result.framework = "dspy";
|
|
7080
7218
|
} else if (scopeLower.includes("vercel-ai") || scopeLower.includes("ai-sdk")) {
|
|
7081
|
-
result.framework = "
|
|
7219
|
+
result.framework = "vercel_ai_sdk";
|
|
7082
7220
|
}
|
|
7083
7221
|
if (scopeLower.includes("openai")) {
|
|
7084
7222
|
result.provider = "openai";
|
|
@@ -8967,6 +9105,46 @@ function addVerificationMarkerResourceAttribute(attributes) {
|
|
|
8967
9105
|
if (marker) attributes[VERIFICATION_MARKER_KEY] = marker;
|
|
8968
9106
|
}
|
|
8969
9107
|
|
|
9108
|
+
// src/core/http-span.ts
|
|
9109
|
+
import { SpanKind } from "@opentelemetry/api";
|
|
9110
|
+
var HTTP_SCOPE_PREFIXES = [
|
|
9111
|
+
"@opentelemetry/instrumentation-http",
|
|
9112
|
+
"@opentelemetry/instrumentation-fetch",
|
|
9113
|
+
"@opentelemetry/instrumentation-undici"
|
|
9114
|
+
];
|
|
9115
|
+
var HTTP_ATTRIBUTE_KEYS = [
|
|
9116
|
+
"http.method",
|
|
9117
|
+
"http.request.method",
|
|
9118
|
+
"http.url",
|
|
9119
|
+
"http.route",
|
|
9120
|
+
"url.full"
|
|
9121
|
+
];
|
|
9122
|
+
var SEMANTIC_AI_KINDS = /* @__PURE__ */ new Set([
|
|
9123
|
+
"WORKFLOW",
|
|
9124
|
+
"AGENT",
|
|
9125
|
+
"CHAIN",
|
|
9126
|
+
"TOOL",
|
|
9127
|
+
"RETRIEVER",
|
|
9128
|
+
"EMBEDDING",
|
|
9129
|
+
"GUARDRAIL",
|
|
9130
|
+
"LLM",
|
|
9131
|
+
"RERANKER",
|
|
9132
|
+
"VECTOR_STORE",
|
|
9133
|
+
"TASK",
|
|
9134
|
+
"EVALUATOR",
|
|
9135
|
+
"LOG",
|
|
9136
|
+
"MEMORY",
|
|
9137
|
+
"MCP_TOOL"
|
|
9138
|
+
]);
|
|
9139
|
+
function isHttpSpan(span2, attributes = span2.attributes ?? {}) {
|
|
9140
|
+
const resolvedKind = resolveExplicitSpanKind(attributes).toUpperCase();
|
|
9141
|
+
if (resolvedKind === "HTTP") return true;
|
|
9142
|
+
if (SEMANTIC_AI_KINDS.has(resolvedKind)) return false;
|
|
9143
|
+
const scopeName = span2.instrumentationLibrary.name ?? "";
|
|
9144
|
+
if (HTTP_SCOPE_PREFIXES.some((prefix) => scopeName.startsWith(prefix))) return true;
|
|
9145
|
+
return span2.kind === SpanKind.CLIENT && HTTP_ATTRIBUTE_KEYS.some((key) => key in attributes);
|
|
9146
|
+
}
|
|
9147
|
+
|
|
8970
9148
|
// src/core/span-processor.ts
|
|
8971
9149
|
var logger11 = getLogger();
|
|
8972
9150
|
function resolveLogFilePath(configuredPath) {
|
|
@@ -9238,6 +9416,7 @@ var NeatlogsSpanProcessor = class {
|
|
|
9238
9416
|
onEnd(span2) {
|
|
9239
9417
|
this._activeSpans.delete(span2.spanContext().spanId);
|
|
9240
9418
|
if (this._closed) return;
|
|
9419
|
+
if (isHttpSpan(span2)) return;
|
|
9241
9420
|
if (span2.name === "neatlogs.trace.complete") {
|
|
9242
9421
|
const markerData = spanToDict(span2, { includeScope: true });
|
|
9243
9422
|
markerData.resource = { attributes: markerData.resource };
|
|
@@ -9397,6 +9576,7 @@ var NeatlogsSpanProcessor = class {
|
|
|
9397
9576
|
const maskResult = scheduleMask(span2, spanData, this.mask ?? null);
|
|
9398
9577
|
const pending = maskResult.then((maskedSpanData) => {
|
|
9399
9578
|
if (maskedSpanData === null) return;
|
|
9579
|
+
if (isHttpSpan(span2, maskedSpanData.attributes ?? {})) return;
|
|
9400
9580
|
if (this._processedLogStream && !this._processedLogStream.destroyed) {
|
|
9401
9581
|
try {
|
|
9402
9582
|
this._processedLogStream.write(JSON.stringify(maskedSpanData) + "\n");
|
|
@@ -9495,8 +9675,8 @@ var NeatlogsSpanProcessor = class {
|
|
|
9495
9675
|
isRemote: false,
|
|
9496
9676
|
traceFlags: TraceFlags.SAMPLED
|
|
9497
9677
|
};
|
|
9498
|
-
const wrappedSpan =
|
|
9499
|
-
const ctx =
|
|
9678
|
+
const wrappedSpan = otelTrace4.wrapSpanContext(spanCtx);
|
|
9679
|
+
const ctx = otelTrace4.setSpan(ROOT_CONTEXT4, wrappedSpan);
|
|
9500
9680
|
const marker = markerTracer.startSpan("neatlogs.trace.complete", void 0, ctx);
|
|
9501
9681
|
marker.setAttribute("neatlogs.trace.complete", true);
|
|
9502
9682
|
marker.setAttribute("neatlogs.internal", true);
|
|
@@ -9665,7 +9845,8 @@ var CompletionMarkerSpanProcessor = class {
|
|
|
9665
9845
|
onStart(_span, _parentContext) {
|
|
9666
9846
|
}
|
|
9667
9847
|
onEnd(span2) {
|
|
9668
|
-
if (this.closed || span2.name === "neatlogs.trace.complete" || span2.parentSpanId)
|
|
9848
|
+
if (this.closed || span2.name === "neatlogs.trace.complete" || span2.parentSpanId || isHttpSpan(span2))
|
|
9849
|
+
return;
|
|
9669
9850
|
if (!this.source.ownsSpan(span2)) return;
|
|
9670
9851
|
const task = this.source.whenMaskComplete(span2).then((eligible) => {
|
|
9671
9852
|
if (!eligible || this.closed) return;
|
|
@@ -9805,6 +9986,10 @@ var FilteringExporter = class {
|
|
|
9805
9986
|
_onPrepared;
|
|
9806
9987
|
export(spans, resultCallback) {
|
|
9807
9988
|
void mapWithConcurrency(spans, MEDIA_RESOLUTION_CONCURRENCY, async (span2) => {
|
|
9989
|
+
if (isHttpSpan(span2)) {
|
|
9990
|
+
discardPendingMedia(span2);
|
|
9991
|
+
return { span: null, mediaReady: true };
|
|
9992
|
+
}
|
|
9808
9993
|
if (span2.instrumentationLibrary.name === "next.js") {
|
|
9809
9994
|
discardPendingMedia(span2);
|
|
9810
9995
|
this.diagnostics?.recordFrameworkSpanDrop();
|
|
@@ -9825,6 +10010,11 @@ var FilteringExporter = class {
|
|
|
9825
10010
|
...masked.attributes && typeof masked.attributes === "object" ? masked.attributes : {}
|
|
9826
10011
|
};
|
|
9827
10012
|
const maskedData = { ...masked, attributes };
|
|
10013
|
+
const maskedSpan = maskedReadableSpan(span2, maskedData);
|
|
10014
|
+
if (isHttpSpan(maskedSpan)) {
|
|
10015
|
+
discardPendingMedia(span2);
|
|
10016
|
+
return { span: null, mediaReady: true };
|
|
10017
|
+
}
|
|
9828
10018
|
captureMediaInSnapshot(
|
|
9829
10019
|
span2,
|
|
9830
10020
|
maskedData,
|
|
@@ -11132,18 +11322,6 @@ function closeSharedClient() {
|
|
|
11132
11322
|
function _resetMastraCache() {
|
|
11133
11323
|
}
|
|
11134
11324
|
|
|
11135
|
-
// src/errors.ts
|
|
11136
|
-
var NeatlogsConfigurationError = class extends TypeError {
|
|
11137
|
-
code;
|
|
11138
|
-
option;
|
|
11139
|
-
constructor(code, option, message) {
|
|
11140
|
-
super(message);
|
|
11141
|
-
this.name = "NeatlogsConfigurationError";
|
|
11142
|
-
this.code = code;
|
|
11143
|
-
this.option = option;
|
|
11144
|
-
}
|
|
11145
|
-
};
|
|
11146
|
-
|
|
11147
11325
|
// src/core/deadline.ts
|
|
11148
11326
|
async function runByDeadline(operation, deadlineMs) {
|
|
11149
11327
|
const remaining = Math.max(0, deadlineMs - Date.now());
|
|
@@ -11244,6 +11422,24 @@ var INIT_OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
|
11244
11422
|
"piiSpanTypes",
|
|
11245
11423
|
"uploadAuthority"
|
|
11246
11424
|
]);
|
|
11425
|
+
function resolveInitEndpoint(endpoint) {
|
|
11426
|
+
const explicit = (endpoint ?? "").trim();
|
|
11427
|
+
if (explicit) return explicit;
|
|
11428
|
+
const fromEnv = (process.env.NEATLOGS_ENDPOINT ?? "").trim();
|
|
11429
|
+
return fromEnv || DEFAULT_INGEST_ENDPOINT;
|
|
11430
|
+
}
|
|
11431
|
+
function resolveIngestBaseUrl(endpoint) {
|
|
11432
|
+
const parsed = new URL(endpoint.trim());
|
|
11433
|
+
const path3 = parsed.pathname.replace(/\/+$/, "");
|
|
11434
|
+
if (path3 !== "" && path3 !== "/v1/traces") {
|
|
11435
|
+
throw new NeatlogsConfigurationError(
|
|
11436
|
+
"INVALID_ENDPOINT",
|
|
11437
|
+
"endpoint",
|
|
11438
|
+
"endpoint must be a base URL or an OTLP traces URL ending in /v1/traces."
|
|
11439
|
+
);
|
|
11440
|
+
}
|
|
11441
|
+
return parsed.origin;
|
|
11442
|
+
}
|
|
11247
11443
|
function validateInitOptions(options) {
|
|
11248
11444
|
const raw = options;
|
|
11249
11445
|
if (Object.prototype.hasOwnProperty.call(raw, "instrumentations")) {
|
|
@@ -11327,8 +11523,13 @@ function stableValue(value, ancestors = /* @__PURE__ */ new WeakSet(), location
|
|
|
11327
11523
|
ancestors.delete(value);
|
|
11328
11524
|
}
|
|
11329
11525
|
}
|
|
11526
|
+
function resolveApiKey(apiKey) {
|
|
11527
|
+
const explicit = (apiKey ?? "").trim();
|
|
11528
|
+
if (explicit) return explicit;
|
|
11529
|
+
return (process.env.NEATLOGS_API_KEY ?? "").trim();
|
|
11530
|
+
}
|
|
11330
11531
|
function initIdentity(options) {
|
|
11331
|
-
const apiKey = (options.apiKey
|
|
11532
|
+
const apiKey = resolveApiKey(options.apiKey);
|
|
11332
11533
|
const apiKeyDigest = createHash4("sha256").update(apiKey).digest("hex");
|
|
11333
11534
|
const serialized = stableValue({
|
|
11334
11535
|
apiKeyDigest,
|
|
@@ -11348,7 +11549,7 @@ function initIdentity(options) {
|
|
|
11348
11549
|
captureLogs: options.captureLogs ?? false,
|
|
11349
11550
|
pii: options.pii ?? null,
|
|
11350
11551
|
version: options.version ?? null,
|
|
11351
|
-
endpoint: options.endpoint
|
|
11552
|
+
endpoint: resolveInitEndpoint(options.endpoint),
|
|
11352
11553
|
batchSize: options.batchSize ?? 100,
|
|
11353
11554
|
flushInterval: options.flushInterval ?? 5,
|
|
11354
11555
|
piiEnabled: options.piiEnabled ?? null,
|
|
@@ -11428,12 +11629,7 @@ async function _performInit(options) {
|
|
|
11428
11629
|
);
|
|
11429
11630
|
}
|
|
11430
11631
|
_deliveryDiagnostics = new DeliveryDiagnostics();
|
|
11431
|
-
let resolvedKey;
|
|
11432
|
-
if (options.apiKey && options.apiKey.trim()) {
|
|
11433
|
-
resolvedKey = options.apiKey.trim();
|
|
11434
|
-
} else {
|
|
11435
|
-
resolvedKey = (process.env.NEATLOGS_API_KEY ?? "").trim();
|
|
11436
|
-
}
|
|
11632
|
+
let resolvedKey = resolveApiKey(options.apiKey);
|
|
11437
11633
|
let disableExportResolved = !!options.disableExport || ["true", "1", "yes"].includes(
|
|
11438
11634
|
(process.env.NEATLOGS_DISABLE_EXPORT ?? "").toLowerCase()
|
|
11439
11635
|
);
|
|
@@ -11454,8 +11650,8 @@ async function _performInit(options) {
|
|
|
11454
11650
|
}
|
|
11455
11651
|
_debugMode2 = options.debug ?? false;
|
|
11456
11652
|
const resolvedWorkflowName = _resolveWorkflowName(options.workflowName);
|
|
11457
|
-
const endpoint = options.endpoint
|
|
11458
|
-
const baseUrl =
|
|
11653
|
+
const endpoint = resolveInitEndpoint(options.endpoint);
|
|
11654
|
+
const baseUrl = resolveIngestBaseUrl(endpoint);
|
|
11459
11655
|
const uploadAuthority = disableExportResolved ? new DisabledUploadAuthority("export_disabled") : resolveUploadAuthority(
|
|
11460
11656
|
options.uploadAuthority,
|
|
11461
11657
|
process.env.NEATLOGS_UPLOADS_ENABLED,
|
|
@@ -12175,6 +12371,36 @@ var INGESTION_STAGES = /* @__PURE__ */ new Set([
|
|
|
12175
12371
|
]);
|
|
12176
12372
|
var INGESTION_STATES = /* @__PURE__ */ new Set(["processing", "failed", "succeeded"]);
|
|
12177
12373
|
var SAFE_FAILURE_CODE = /^[A-Z0-9_]{1,64}$/;
|
|
12374
|
+
var PROBE_FAILURE = {
|
|
12375
|
+
AUTH_FAILED: {
|
|
12376
|
+
remediationCode: "CHECK_INGEST_CREDENTIAL",
|
|
12377
|
+
message: "The project key was rejected by the existing trace API"
|
|
12378
|
+
},
|
|
12379
|
+
BACKEND_PROBE_UNAVAILABLE: {
|
|
12380
|
+
remediationCode: "CHECK_TRACE_ENDPOINT",
|
|
12381
|
+
message: "The existing trace ingestion or read path is unavailable"
|
|
12382
|
+
},
|
|
12383
|
+
TRACE_READBACK_TIMEOUT: {
|
|
12384
|
+
remediationCode: "WAIT_FOR_TRACE",
|
|
12385
|
+
message: "Timed out waiting for the exact Doctor trace"
|
|
12386
|
+
},
|
|
12387
|
+
INGESTION_PIPELINE_FAILED: {
|
|
12388
|
+
remediationCode: "CONTACT_SUPPORT",
|
|
12389
|
+
message: "Trace ingestion reported a terminal pipeline failure"
|
|
12390
|
+
},
|
|
12391
|
+
BACKEND_HTTP_ERROR: {
|
|
12392
|
+
remediationCode: "CHECK_TRACE_ENDPOINT",
|
|
12393
|
+
message: "The existing trace read path returned an unexpected HTTP status"
|
|
12394
|
+
},
|
|
12395
|
+
BACKEND_CONNECTION_FAILED: {
|
|
12396
|
+
remediationCode: "CHECK_TRACE_ENDPOINT",
|
|
12397
|
+
message: "Could not connect to the existing trace read path"
|
|
12398
|
+
},
|
|
12399
|
+
TRACE_READBACK_INVALID: {
|
|
12400
|
+
remediationCode: "CONTACT_SUPPORT",
|
|
12401
|
+
message: "Trace read-back returned an invalid response"
|
|
12402
|
+
}
|
|
12403
|
+
};
|
|
12178
12404
|
var ProbeReadError = class extends Error {
|
|
12179
12405
|
constructor(reasonCode, message, details) {
|
|
12180
12406
|
super(message);
|
|
@@ -12294,7 +12520,7 @@ function validateLocalFixture(traceId) {
|
|
|
12294
12520
|
check("controlled_fixture_bounds", stats.droppedSpans === 0, "CONTROLLED_BOUNDS_VALID", "CONTROLLED_CAPTURE_TRUNCATED", "REDUCE_DIAGNOSTIC_CAPTURE", "Doctor stayed inside the bounded capture budget")
|
|
12295
12521
|
];
|
|
12296
12522
|
}
|
|
12297
|
-
async function withTimeout(operation, timeoutMs, onTimeout) {
|
|
12523
|
+
async function withTimeout(operation, timeoutMs, onTimeout, timeoutReason = "BACKEND_PROBE_UNAVAILABLE") {
|
|
12298
12524
|
let timer;
|
|
12299
12525
|
try {
|
|
12300
12526
|
return await Promise.race([
|
|
@@ -12302,7 +12528,7 @@ async function withTimeout(operation, timeoutMs, onTimeout) {
|
|
|
12302
12528
|
new Promise((_resolve, reject) => {
|
|
12303
12529
|
timer = setTimeout(() => {
|
|
12304
12530
|
onTimeout?.();
|
|
12305
|
-
reject(new ProbeReadError(
|
|
12531
|
+
reject(new ProbeReadError(timeoutReason, "Doctor operation timed out"));
|
|
12306
12532
|
}, Math.max(1, timeoutMs));
|
|
12307
12533
|
})
|
|
12308
12534
|
]);
|
|
@@ -12490,12 +12716,13 @@ function failedProbeResult(local, reasonCode, details) {
|
|
|
12490
12716
|
};
|
|
12491
12717
|
}
|
|
12492
12718
|
function failedProbeTransportCheck(reasonCode, details) {
|
|
12719
|
+
const failure2 = PROBE_FAILURE[reasonCode];
|
|
12493
12720
|
return {
|
|
12494
12721
|
name: "probe_transport",
|
|
12495
12722
|
status: "fail",
|
|
12496
12723
|
reason_code: reasonCode,
|
|
12497
|
-
remediation_code:
|
|
12498
|
-
message:
|
|
12724
|
+
remediation_code: failure2.remediationCode,
|
|
12725
|
+
message: failure2.message,
|
|
12499
12726
|
...details ? { details } : {}
|
|
12500
12727
|
};
|
|
12501
12728
|
}
|
|
@@ -12573,16 +12800,23 @@ function human(result) {
|
|
|
12573
12800
|
if (result.note) lines.push(result.note);
|
|
12574
12801
|
return lines.join("\n");
|
|
12575
12802
|
}
|
|
12803
|
+
function readbackTimeoutReason(remainingMs, requestTimeoutMs) {
|
|
12804
|
+
return remainingMs <= requestTimeoutMs ? "TRACE_READBACK_TIMEOUT" : "BACKEND_CONNECTION_FAILED";
|
|
12805
|
+
}
|
|
12806
|
+
function responseBodyFailureReason(status, remainingMs, requestTimeoutMs) {
|
|
12807
|
+
return status === 409 || status >= 200 && status < 300 && status !== 202 ? "TRACE_READBACK_INVALID" : readbackTimeoutReason(remainingMs, requestTimeoutMs);
|
|
12808
|
+
}
|
|
12576
12809
|
async function boundedFetch(io, input, initOptions, deadline) {
|
|
12577
12810
|
const remaining = deadline - Date.now();
|
|
12578
12811
|
if (remaining <= 0) {
|
|
12579
|
-
throw new ProbeReadError("
|
|
12812
|
+
throw new ProbeReadError("TRACE_READBACK_TIMEOUT", "Doctor probe deadline expired");
|
|
12580
12813
|
}
|
|
12581
12814
|
const controller = new AbortController();
|
|
12582
12815
|
return withTimeout(
|
|
12583
12816
|
() => io.fetch(input, { ...initOptions, signal: controller.signal }),
|
|
12584
12817
|
Math.min(io.requestTimeoutMs, remaining),
|
|
12585
|
-
() => controller.abort()
|
|
12818
|
+
() => controller.abort(),
|
|
12819
|
+
readbackTimeoutReason(remaining, io.requestTimeoutMs)
|
|
12586
12820
|
);
|
|
12587
12821
|
}
|
|
12588
12822
|
async function discardResponseBody(response, reader) {
|
|
@@ -12604,7 +12838,7 @@ async function boundedJson2(response, io, deadline) {
|
|
|
12604
12838
|
const remaining = deadline - Date.now();
|
|
12605
12839
|
if (remaining <= 0) {
|
|
12606
12840
|
await discardResponseBody(response);
|
|
12607
|
-
throw new ProbeReadError("
|
|
12841
|
+
throw new ProbeReadError("TRACE_READBACK_TIMEOUT", "Doctor probe deadline expired");
|
|
12608
12842
|
}
|
|
12609
12843
|
const contentLength = Number(response.headers.get("content-length"));
|
|
12610
12844
|
if (Number.isFinite(contentLength) && contentLength > MAX_READBACK_BYTES) {
|
|
@@ -12639,9 +12873,17 @@ async function boundedJson2(response, io, deadline) {
|
|
|
12639
12873
|
}
|
|
12640
12874
|
}, Math.min(io.requestTimeoutMs, remaining), () => {
|
|
12641
12875
|
void discardResponseBody(response, reader);
|
|
12642
|
-
}
|
|
12876
|
+
}, responseBodyFailureReason(
|
|
12877
|
+
response.status,
|
|
12878
|
+
remaining,
|
|
12879
|
+
io.requestTimeoutMs
|
|
12880
|
+
)).catch(async (error) => {
|
|
12643
12881
|
await discardResponseBody(response, reader);
|
|
12644
|
-
|
|
12882
|
+
if (error instanceof ProbeReadError) throw error;
|
|
12883
|
+
throw new ProbeReadError(
|
|
12884
|
+
"BACKEND_CONNECTION_FAILED",
|
|
12885
|
+
"Trace read-back response could not be read"
|
|
12886
|
+
);
|
|
12645
12887
|
});
|
|
12646
12888
|
}
|
|
12647
12889
|
function usage() {
|
|
@@ -12746,10 +12988,11 @@ async function runDoctorCli(argv, overrides = {}) {
|
|
|
12746
12988
|
"x-neatlogs-doctor": DOCTOR_MARKER_VERSION
|
|
12747
12989
|
}
|
|
12748
12990
|
}, deadline);
|
|
12749
|
-
} catch {
|
|
12991
|
+
} catch (error) {
|
|
12992
|
+
if (error instanceof ProbeReadError) throw error;
|
|
12750
12993
|
throw new ProbeReadError(
|
|
12751
|
-
"
|
|
12752
|
-
"
|
|
12994
|
+
"BACKEND_CONNECTION_FAILED",
|
|
12995
|
+
"Could not connect to the existing trace read path",
|
|
12753
12996
|
lastDiagnostics
|
|
12754
12997
|
);
|
|
12755
12998
|
}
|
|
@@ -12757,7 +13000,7 @@ async function runDoctorCli(argv, overrides = {}) {
|
|
|
12757
13000
|
const value = await boundedJson2(response, io, deadline);
|
|
12758
13001
|
if (!value || typeof value !== "object") {
|
|
12759
13002
|
throw new ProbeReadError(
|
|
12760
|
-
"
|
|
13003
|
+
"TRACE_READBACK_INVALID",
|
|
12761
13004
|
"Trace read-back returned an invalid response"
|
|
12762
13005
|
);
|
|
12763
13006
|
}
|
|
@@ -12770,12 +13013,23 @@ async function runDoctorCli(argv, overrides = {}) {
|
|
|
12770
13013
|
throw new ProbeReadError("AUTH_FAILED", "Trace read-back rejected the project key");
|
|
12771
13014
|
}
|
|
12772
13015
|
if ([202, 404, 409].includes(response.status)) {
|
|
12773
|
-
const value = await boundedJson2(response, io, deadline)
|
|
13016
|
+
const value = await boundedJson2(response, io, deadline);
|
|
12774
13017
|
const currentDiagnostics = ingestionDiagnosticDetails(value);
|
|
12775
13018
|
if (currentDiagnostics) lastDiagnostics = currentDiagnostics;
|
|
12776
13019
|
if (response.status === 409) {
|
|
13020
|
+
const terminalValue = objectValue(value);
|
|
13021
|
+
const isDlq = terminalValue.finalizationStatus === "dlq";
|
|
13022
|
+
const hasDiagnostics = Object.hasOwn(terminalValue, "ingestionDiagnostics");
|
|
13023
|
+
const validDlq = typeof terminalValue.error === "string" && (terminalValue.message === void 0 || typeof terminalValue.message === "string") && (!hasDiagnostics || currentDiagnostics !== void 0);
|
|
13024
|
+
const validReceipt = currentDiagnostics?.ingestion_state === "failed";
|
|
13025
|
+
if (isDlq ? !validDlq : !validReceipt) {
|
|
13026
|
+
throw new ProbeReadError(
|
|
13027
|
+
"TRACE_READBACK_INVALID",
|
|
13028
|
+
"Trace read-back returned an invalid terminal receipt"
|
|
13029
|
+
);
|
|
13030
|
+
}
|
|
12777
13031
|
throw new ProbeReadError(
|
|
12778
|
-
"
|
|
13032
|
+
"INGESTION_PIPELINE_FAILED",
|
|
12779
13033
|
"Trace ingestion reported a terminal failure",
|
|
12780
13034
|
currentDiagnostics
|
|
12781
13035
|
);
|
|
@@ -12783,7 +13037,7 @@ async function runDoctorCli(argv, overrides = {}) {
|
|
|
12783
13037
|
} else {
|
|
12784
13038
|
await discardResponseBody(response);
|
|
12785
13039
|
throw new ProbeReadError(
|
|
12786
|
-
"
|
|
13040
|
+
"BACKEND_HTTP_ERROR",
|
|
12787
13041
|
`Trace read-back failed with HTTP ${response.status}`,
|
|
12788
13042
|
response.status >= 500 ? lastDiagnostics : void 0
|
|
12789
13043
|
);
|
|
@@ -12793,7 +13047,7 @@ async function runDoctorCli(argv, overrides = {}) {
|
|
|
12793
13047
|
}
|
|
12794
13048
|
if (!persisted) {
|
|
12795
13049
|
throw new ProbeReadError(
|
|
12796
|
-
"
|
|
13050
|
+
"TRACE_READBACK_TIMEOUT",
|
|
12797
13051
|
"Timed out waiting for the exact Doctor trace",
|
|
12798
13052
|
lastDiagnostics
|
|
12799
13053
|
);
|
|
@@ -12802,7 +13056,7 @@ async function runDoctorCli(argv, overrides = {}) {
|
|
|
12802
13056
|
io.stdout(json ? JSON.stringify(result, null, 2) : human(result));
|
|
12803
13057
|
return result.status === "pass" ? 0 : 3;
|
|
12804
13058
|
} catch (error) {
|
|
12805
|
-
const reason = error instanceof ProbeReadError
|
|
13059
|
+
const reason = error instanceof ProbeReadError ? error.reasonCode : "BACKEND_PROBE_UNAVAILABLE";
|
|
12806
13060
|
const result = local ? failedProbeResult(
|
|
12807
13061
|
local,
|
|
12808
13062
|
reason,
|