-
Notifications
You must be signed in to change notification settings - Fork 6
Jaeger #108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Jaeger #108
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| { | ||
| "steps": [ | ||
| { | ||
| "displayName": "Check connection", | ||
| "dataStream": { | ||
| "name": "services" | ||
| }, | ||
| "required": true, | ||
| "error": "Could not reach the Jaeger Query API. Check the URL is correct, that the instance is reachable (directly or via an on-prem relay agent), and enable 'Ignore certificate errors' if it uses a self-signed certificate.", | ||
| "success": "Connected successfully." | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "words": [ | ||
| "jaeger", | ||
| "opentelemetry" | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| [ | ||
| { | ||
| "name": "Service", | ||
| "sourceType": "Service", | ||
| "icon": "server", | ||
| "singular": "Service", | ||
| "plural": "Services" | ||
| }, | ||
| { | ||
| "name": "Dependency", | ||
| "sourceType": "Dependency", | ||
| "icon": "share-nodes", | ||
| "singular": "Dependency", | ||
| "plural": "Dependencies" | ||
| } | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| { | ||
| "name": "dependencies", | ||
| "displayName": "Dependencies", | ||
| "description": "Call dependencies between services, one row per parent-child pair", | ||
| "tags": [ | ||
| "Services" | ||
| ], | ||
| "baseDataSourceName": "httpRequestUnscoped", | ||
| "matches": "none", | ||
| "timeframes": [ | ||
| "last1hour", | ||
| "last12hours", | ||
| "last24hours", | ||
| "last7days" | ||
| ], | ||
| "config": { | ||
| "httpMethod": "get", | ||
| "endpointPath": "/api/dependencies", | ||
| "pathToData": "data", | ||
| "getArgs": [ | ||
| { | ||
| "key": "endTs", | ||
| "value": "{{timeframe?.unixEnd ? timeframe.unixEnd * 1000 : Date.now()}}" | ||
| }, | ||
| { | ||
| "key": "lookback", | ||
| "value": "{{(timeframe?.unixStart && timeframe?.unixEnd) ? (timeframe.unixEnd - timeframe.unixStart) * 1000 : 1800000}}" | ||
| } | ||
| ], | ||
| "headers": [] | ||
| }, | ||
| "metadata": [ | ||
| { | ||
| "name": "key", | ||
| "displayName": "Dependency", | ||
| "computed": true, | ||
| "valueExpression": "{{ $['parent'] + ' -> ' + $['child'] }}", | ||
| "shape": "string", | ||
| "role": "label" | ||
| }, | ||
| { | ||
| "name": "parent", | ||
| "displayName": "Parent Service", | ||
| "shape": "string" | ||
| }, | ||
| { | ||
| "name": "child", | ||
| "displayName": "Child Service", | ||
| "shape": "string" | ||
| }, | ||
| { | ||
| "name": "callCount", | ||
| "displayName": "Call Count", | ||
| "shape": [ | ||
| "number", | ||
| { | ||
| "thousandsSeparator": true | ||
| } | ||
| ] | ||
| }, | ||
| { | ||
| "name": "source", | ||
| "displayName": "Source", | ||
| "shape": "string", | ||
| "visible": false | ||
| } | ||
| ] | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| { | ||
| "name": "operations", | ||
| "displayName": "Operations", | ||
| "description": "Operation names reported by a service", | ||
| "tags": [ | ||
| "Operations" | ||
| ], | ||
| "baseDataSourceName": "httpRequestScopedSingle", | ||
| "matches": { | ||
| "sourceType": { | ||
| "type": "equals", | ||
| "value": "Service" | ||
| } | ||
| }, | ||
| "timeframes": false, | ||
| "config": { | ||
| "httpMethod": "get", | ||
| "endpointPath": "/api/v3/operations", | ||
| "getArgs": [ | ||
| { | ||
| "key": "service", | ||
| "value": "{{object.rawId}}" | ||
| } | ||
| ], | ||
| "headers": [], | ||
| "pathToData": "operations" | ||
| }, | ||
| "metadata": [ | ||
| { | ||
| "name": "name", | ||
| "displayName": "Operation", | ||
| "shape": "string", | ||
| "role": "label" | ||
| }, | ||
| { | ||
| "name": "spanKind", | ||
| "displayName": "Span Kind", | ||
| "shape": "string" | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| // dataStreams/scripts/traces.js | ||
| // | ||
| // The /api/v3/traces response is a single JSON object shaped | ||
| // { "result": { "resourceSpans": [...] } } (OTLP JSON) - not NDJSON - so the | ||
| // platform's automatic JSON parse populates `data` and we read it directly. | ||
| // | ||
| // OTLP JSON encodes int64 (startTimeUnixNano/endTimeUnixNano) as STRING. | ||
| // Epoch nanoseconds (~1.7e18) exceed JS Number safe-integer precision, so we | ||
| // do the nanosecond arithmetic in BigInt and only convert to Number once | ||
| // we've reduced to milliseconds (~1.7e12, well within safe range). | ||
|
|
||
| const SPAN_KIND = { | ||
| 0: "UNSPECIFIED", | ||
| 1: "INTERNAL", | ||
| 2: "SERVER", | ||
| 3: "CLIENT", | ||
| 4: "PRODUCER", | ||
| 5: "CONSUMER", | ||
| }; | ||
|
|
||
| const STATUS_CODE = { | ||
| 0: "UNSET", | ||
| 1: "OK", | ||
| 2: "ERROR", | ||
| }; | ||
|
|
||
| function attrsToObject(attributes) { | ||
| const obj = {}; | ||
| (attributes || []).forEach((a) => { | ||
| const value = a.value || {}; | ||
| obj[a.key] = | ||
| value.stringValue ?? | ||
| value.intValue ?? | ||
| value.doubleValue ?? | ||
| value.boolValue; | ||
| }); | ||
|
Comment on lines
+27
to
+36
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
file="plugins/Jaeger/v1/dataStreams/scripts/traces.js"
printf '%s\n' "== target file =="
cat -n "$file" | sed -n '1,140p'
printf '%s\n' "== related Jaeger data-stream files =="
git ls-files 'plugins/Jaeger/v1/dataStreams/*' | sed -n '1,160p'
printf '%s\n' "== attrsToObject usages and attribute fixtures =="
rg -n -S 'attrsToObject|attributes|bytesValue|arrayValue|kvlistValue|intValue|doubleValue|boolValue' \
plugins/Jaeger/v1/dataStreams plugins/Jaeger/v1 2>/dev/null | sed -n '1,240p'Repository: squaredup/plugins Length of output: 5749 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' "== traces data-stream schema around attributes =="
cat -n plugins/Jaeger/v1/dataStreams/traces.json | sed -n '80,125p'
printf '%s\n' "== plugin metadata and version references =="
fd -i 'metadata.json' plugins/Jaeger | while read -r f; do
printf '\n%s\n' "[$f]"
cat -n "$f" | sed -n '1,100p'
done
printf '%s\n' "== standalone behavior probe =="
node - <<'JS'
function attrsToObject(attributes) {
const obj = {};
(attributes || []).forEach((a) => {
const value = a.value || {};
obj[a.key] =
value.stringValue ??
value.intValue ??
value.doubleValue ??
value.boolValue;
});
return obj;
}
const input = [
{ key: "bytes", value: { bytesValue: "AQI=" } },
{
key: "array",
value: { arrayValue: { values: [
{ stringValue: "x" },
{ intValue: "7" }
] } }
},
{
key: "map",
value: { kvlistValue: { values: [
{ key: "nested", value: { boolValue: true } }
] } }
}
];
const output = attrsToObject(input);
console.log("object:", JSON.stringify(output));
console.log("bytes:", output.bytes);
console.log("array:", output.array);
console.log("map:", output.map);
console.log("serialized:", JSON.stringify({ attributes: output }));
JSRepository: squaredup/plugins Length of output: 3210 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' "== changed files =="
git diff --name-only
printf '%s\n' "== change summary =="
git diff --stat
printf '%s\n' "== metadata change, if any =="
git diff -- plugins/Jaeger/v1/metadata.jsonRepository: squaredup/plugins Length of output: 226 Preserve non-scalar OTLP attributes.
🤖 Prompt for AI Agents |
||
| return obj; | ||
| } | ||
|
|
||
| function nanoStrToBig(nanoStr) { | ||
| return BigInt(nanoStr || "0"); | ||
| } | ||
|
|
||
| const resourceSpans = (data && data.result && data.result.resourceSpans) || []; | ||
|
|
||
| result = _.flatMap(resourceSpans, (rs) => { | ||
| const resourceAttrs = attrsToObject((rs.resource || {}).attributes); | ||
| const serviceName = resourceAttrs["service.name"] || ""; | ||
|
|
||
| return _.flatMap(rs.scopeSpans || [], (ss) => { | ||
| return (ss.spans || []).map((s) => { | ||
| const startNsBig = nanoStrToBig(s.startTimeUnixNano); | ||
| const endNsBig = nanoStrToBig(s.endTimeUnixNano); | ||
| // Compute duration from the full-precision nanosecond values before | ||
| // rounding to ms — rounding each endpoint first and then subtracting | ||
| // can be off by up to 1ms (e.g. start=1.999999ms, end=2.000001ms | ||
| // truncate to 1ms/2ms, giving a 1ms duration for a ~2ns span). | ||
| const durationMsBig = (endNsBig - startNsBig) / 1000000n; | ||
| const status = s.status || {}; | ||
| // proto3 JSON omits fields at their default value, so an | ||
| // INTERNAL/unspecified-kind span (kind 0) typically has no `kind` | ||
| // field at all — default the missing case to 0 rather than | ||
| // stringifying `undefined`. | ||
| const kind = s.kind ?? 0; | ||
|
|
||
| return { | ||
| traceId: s.traceId, | ||
| spanId: s.spanId, | ||
| parentSpanId: s.parentSpanId || "", | ||
| operationName: s.name, | ||
| serviceName, | ||
| kind: SPAN_KIND[kind] ?? String(kind), | ||
| startTime: new Date( | ||
| Number(startNsBig / 1000000n), | ||
| ).toISOString(), | ||
| durationMs: Number(durationMsBig), | ||
| statusCode: STATUS_CODE[status.code] ?? "UNSET", | ||
| statusMessage: status.message || "", | ||
| attributes: attrsToObject(s.attributes), | ||
| }; | ||
| }); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| { | ||
| "name": "services", | ||
| "displayName": "Services", | ||
| "description": "Services known to the Jaeger backend", | ||
| "tags": [ | ||
| "Services" | ||
| ], | ||
| "baseDataSourceName": "httpRequestUnscoped", | ||
| "matches": "none", | ||
| "timeframes": false, | ||
| "config": { | ||
| "httpMethod": "get", | ||
| "endpointPath": "/api/v3/services", | ||
| "postRequestScript": "result = (data.services || []).map(service => ({ service }));", | ||
| "headers": [] | ||
| }, | ||
| "metadata": [ | ||
| { | ||
| "name": "service", | ||
| "displayName": "Service", | ||
| "shape": "string", | ||
| "role": "label" | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| { | ||
| "name": "traces", | ||
| "displayName": "Traces", | ||
| "description": "Spans reported by a service within the selected timeframe", | ||
| "tags": [ | ||
| "Traces" | ||
| ], | ||
| "baseDataSourceName": "httpRequestScopedSingle", | ||
| "matches": { | ||
| "sourceType": { | ||
| "type": "equals", | ||
| "value": "Service" | ||
| } | ||
| }, | ||
| "timeframes": true, | ||
| "config": { | ||
| "httpMethod": "get", | ||
| "endpointPath": "/api/v3/traces", | ||
| "getArgs": [ | ||
| { | ||
| "key": "query.service_name", | ||
| "value": "{{object.rawId}}" | ||
| }, | ||
| { | ||
| "key": "query.start_time_min", | ||
| "value": "{{timeframe.start}}" | ||
| }, | ||
| { | ||
| "key": "query.start_time_max", | ||
| "value": "{{timeframe.end}}" | ||
| }, | ||
| { | ||
| "key": "query.search_depth", | ||
| "value": "{{limit}}" | ||
| } | ||
| ], | ||
| "headers": [], | ||
| "postRequestScript": "traces.js" | ||
| }, | ||
| "ui": [ | ||
| { | ||
| "name": "limit", | ||
| "label": "Number of Traces", | ||
| "type": "number", | ||
| "defaultValue": 20, | ||
| "min": 1 | ||
| } | ||
| ], | ||
| "metadata": [ | ||
| { | ||
| "name": "traceId", | ||
| "displayName": "Trace ID", | ||
| "shape": "string" | ||
| }, | ||
| { | ||
| "name": "spanId", | ||
| "displayName": "Span ID", | ||
| "shape": "string", | ||
| "visible": false | ||
| }, | ||
| { | ||
| "name": "parentSpanId", | ||
| "displayName": "Parent Span ID", | ||
| "shape": "string", | ||
| "visible": false | ||
| }, | ||
| { | ||
| "name": "operationName", | ||
| "displayName": "Operation", | ||
| "shape": "string", | ||
| "role": "label" | ||
| }, | ||
| { | ||
| "name": "serviceName", | ||
| "displayName": "Service", | ||
| "shape": "string" | ||
| }, | ||
| { | ||
| "name": "kind", | ||
| "displayName": "Kind", | ||
| "shape": "string" | ||
| }, | ||
| { | ||
| "name": "startTime", | ||
| "displayName": "Start Time", | ||
| "shape": "date", | ||
| "role": "timestamp" | ||
| }, | ||
| { | ||
| "name": "durationMs", | ||
| "displayName": "Duration (ms)", | ||
| "shape": "milliseconds", | ||
| "role": "value" | ||
| }, | ||
| { | ||
| "name": "statusCode", | ||
| "displayName": "Status Code", | ||
| "shape": "string" | ||
| }, | ||
| { | ||
| "name": "statusMessage", | ||
| "displayName": "Status Message", | ||
| "shape": "string", | ||
| "visible": false | ||
| }, | ||
| { | ||
| "name": "attributes", | ||
| "displayName": "Attributes", | ||
| "shape": "json", | ||
| "visible": false | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| { | ||
| "items": [ | ||
| { | ||
| "name": "overview", | ||
| "type": "dashboard" | ||
| }, | ||
| { | ||
| "name": "service", | ||
| "type": "dashboard" | ||
| } | ||
| ] | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Use the documented one-hour fallback.
When no timeframe exists, Line 27 sends a
lookbackof1800000ms. This selects 30 minutes. The README states that scheduled dependency indexing uses a one-hour trailing window.Set the fallback to
3600000. Otherwise, scheduled imports omit dependency calls from half of the documented query window.Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents