cubeapm-mcp 1.2.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -8
- package/dist/index.js +254 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -207,7 +207,7 @@ Readable resources exposing CubeAPM data and configuration:
|
|
|
207
207
|
|
|
208
208
|
## CubeAPM Query Patterns
|
|
209
209
|
|
|
210
|
-
### Metrics
|
|
210
|
+
### Metrics (PromQL / MetricsQL)
|
|
211
211
|
|
|
212
212
|
CubeAPM uses specific naming conventions that differ from standard OpenTelemetry:
|
|
213
213
|
|
|
@@ -217,7 +217,7 @@ CubeAPM uses specific naming conventions that differ from standard OpenTelemetry
|
|
|
217
217
|
| Service label | `service` (NOT `server` or `service_name`) |
|
|
218
218
|
| Common labels | `env`, `service`, `span_kind`, `status_code`, `http_code` |
|
|
219
219
|
|
|
220
|
-
|
|
220
|
+
#### Histogram Queries (P50, P90, P95, P99)
|
|
221
221
|
|
|
222
222
|
CubeAPM uses **VictoriaMetrics-style histograms** with `vmrange` labels instead of Prometheus `le` buckets:
|
|
223
223
|
|
|
@@ -233,7 +233,9 @@ histogram_quantile(0.95, sum by (le) (rate(http_request_duration_bucket[5m])))
|
|
|
233
233
|
|
|
234
234
|
> **Note:** Latency values are returned in **seconds** (0.05 = 50ms)
|
|
235
235
|
|
|
236
|
-
### Logs
|
|
236
|
+
### Logs (LogsQL)
|
|
237
|
+
|
|
238
|
+
#### Stream Selectors
|
|
237
239
|
|
|
238
240
|
Log labels vary by source. Use `*` query first to discover available labels:
|
|
239
241
|
|
|
@@ -249,17 +251,92 @@ Log labels vary by source. Use `*` query first to discover available labels:
|
|
|
249
251
|
# Lambda function logs
|
|
250
252
|
{faas.name="my-lambda-prod"}
|
|
251
253
|
|
|
252
|
-
#
|
|
253
|
-
{faas.name=~".*-prod"}
|
|
254
|
+
# Regex match
|
|
255
|
+
{faas.name=~".*-prod"}
|
|
256
|
+
|
|
257
|
+
# Text filter with boolean operators
|
|
258
|
+
{faas.name=~".*"} AND "error" AND NOT "retry"
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
#### Pipe Operators
|
|
262
|
+
|
|
263
|
+
Chain after any query with `|`:
|
|
264
|
+
|
|
265
|
+
| Pipe | Syntax | Description |
|
|
266
|
+
|------|--------|-------------|
|
|
267
|
+
| `copy` | `\| copy src AS dst` | Copy field value |
|
|
268
|
+
| `drop` | `\| drop field1, field2` | Remove fields from output |
|
|
269
|
+
| `extract_regexp` | `\| extract_regexp "(?P<name>re)"` | Extract via named capture groups |
|
|
270
|
+
| `join` | `\| join by (field) (...subquery...)` | Join with subquery results |
|
|
271
|
+
| `keep` | `\| keep field1, field2` | Keep only specified fields |
|
|
272
|
+
| `limit` | `\| limit N` | Return at most N results |
|
|
273
|
+
| `math` | `\| math result = f1 + f2` | Arithmetic (+, -, *, /, %) |
|
|
274
|
+
| `rename` | `\| rename src AS dst` | Rename a field |
|
|
275
|
+
| `replace` | `\| replace (field, "old", "new")` | Substring replacement |
|
|
276
|
+
| `replace_regexp` | `\| replace_regexp (field, "re", "repl")` | Regex replacement |
|
|
277
|
+
| `sort` | `\| sort by (field) [asc\|desc]` | Sort results |
|
|
278
|
+
| `stats` | `\| stats <func> as alias [by (fields)]` | Aggregate results |
|
|
279
|
+
| `unpack_json` | `\| unpack_json` | Extract fields from JSON body |
|
|
280
|
+
|
|
281
|
+
#### Stats Functions
|
|
282
|
+
|
|
283
|
+
Used with the `| stats` pipe:
|
|
284
|
+
|
|
285
|
+
| Function | Description |
|
|
286
|
+
|----------|-------------|
|
|
287
|
+
| `avg(field)` | Arithmetic mean |
|
|
288
|
+
| `count()` | Total matching entries |
|
|
289
|
+
| `count_empty(field)` | Entries where field is empty |
|
|
290
|
+
| `count_uniq(field)` | Distinct values |
|
|
291
|
+
| `max(field)` | Maximum value |
|
|
292
|
+
| `median(field)` | Median (50th percentile) |
|
|
293
|
+
| `min(field)` | Minimum value |
|
|
294
|
+
| `quantile(p, field)` | p-th quantile (e.g., `quantile(0.95, duration)`) |
|
|
295
|
+
| `sum(field)` | Sum of values |
|
|
296
|
+
|
|
297
|
+
#### Example Log Queries
|
|
298
|
+
|
|
299
|
+
```logsql
|
|
300
|
+
# Count errors per Lambda function
|
|
301
|
+
{faas.name=~".*"} AND "error" | stats count() as errors by (faas.name)
|
|
302
|
+
|
|
303
|
+
# Top 10 slowest requests
|
|
304
|
+
{service_name="my-service"} | sort by (duration) desc | limit 10
|
|
305
|
+
|
|
306
|
+
# Extract and aggregate from JSON logs
|
|
307
|
+
{service_name="api"} | unpack_json | stats avg(response_time) as avg_rt by (endpoint)
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Traces
|
|
311
|
+
|
|
312
|
+
Trace queries use the same pipe syntax as logs: `{stream_selector} | pipe1 | pipe2`
|
|
313
|
+
|
|
314
|
+
**Important notes:**
|
|
315
|
+
- `query`, `env`, `service` are REQUIRED parameters
|
|
316
|
+
- Duration is in **milliseconds** (not seconds like metrics)
|
|
317
|
+
- `p95` is NOT a valid stats function — use `quantile(0.95, duration)`
|
|
318
|
+
- Service names are case-sensitive (e.g., `"Kratos-Prod"` not `"kratos"`)
|
|
319
|
+
|
|
320
|
+
#### Example Trace Queries
|
|
321
|
+
|
|
322
|
+
```
|
|
323
|
+
# P95 latency for a service
|
|
324
|
+
{service="Kratos-Prod", span_kind="server"} | stats quantile(0.95, duration) as p95_ms
|
|
325
|
+
|
|
326
|
+
# Error count by endpoint
|
|
327
|
+
{service="Kratos-Prod", status_code="ERROR"} | stats count() as errors by (http_route)
|
|
328
|
+
|
|
329
|
+
# Slowest spans
|
|
330
|
+
{service="Kratos-Prod"} | sort by (duration) desc | limit 20
|
|
254
331
|
```
|
|
255
332
|
|
|
256
|
-
## Example Queries
|
|
333
|
+
## Example Natural Language Queries
|
|
257
334
|
|
|
258
335
|
### Logs
|
|
259
336
|
```
|
|
260
337
|
"Show me logs from webhook-lambda-prod"
|
|
261
338
|
"Find all logs containing 'timeout' in the last hour"
|
|
262
|
-
"
|
|
339
|
+
"Count errors per Lambda function in the last 24h"
|
|
263
340
|
```
|
|
264
341
|
|
|
265
342
|
### Metrics
|
|
@@ -271,7 +348,7 @@ Log labels vary by source. Use `*` query first to discover available labels:
|
|
|
271
348
|
|
|
272
349
|
### Traces
|
|
273
350
|
```
|
|
274
|
-
"Find
|
|
351
|
+
"Find the P95 latency for Kratos-Prod using trace stats"
|
|
275
352
|
"Show me traces with errors in the production environment"
|
|
276
353
|
"Get the full waterfall for trace ID abc123"
|
|
277
354
|
```
|
package/dist/index.js
CHANGED
|
@@ -25,6 +25,90 @@ const server = new McpServer({
|
|
|
25
25
|
// ============================================
|
|
26
26
|
// LOGS APIs
|
|
27
27
|
// ============================================
|
|
28
|
+
// Convert RFC3339 string or Unix timestamp (s/ms/μs/ns) to nanoseconds.
|
|
29
|
+
// CubeAPM's prod UI embeds time in queries as `_time:[ns_start, ns_end)`,
|
|
30
|
+
// so we mirror that wire format rather than relying on start/end form params.
|
|
31
|
+
function toNanoseconds(t) {
|
|
32
|
+
if (/^\d+$/.test(t)) {
|
|
33
|
+
const n = BigInt(t);
|
|
34
|
+
if (n < 10000000000n)
|
|
35
|
+
return (n * 1000000000n).toString(); // seconds
|
|
36
|
+
if (n < 10000000000000n)
|
|
37
|
+
return (n * 1000000n).toString(); // milliseconds
|
|
38
|
+
if (n < 10000000000000000n)
|
|
39
|
+
return (n * 1000n).toString(); // microseconds
|
|
40
|
+
return n.toString(); // nanoseconds
|
|
41
|
+
}
|
|
42
|
+
const ms = new Date(t).getTime();
|
|
43
|
+
if (Number.isNaN(ms))
|
|
44
|
+
throw new Error(`Invalid timestamp: ${t}`);
|
|
45
|
+
return (BigInt(ms) * 1000000n).toString();
|
|
46
|
+
}
|
|
47
|
+
// Find the index of the first top-level pipe operator in a LogsQL query,
|
|
48
|
+
// ignoring `|` inside quoted strings (regex alternation like `=~"a|b"`),
|
|
49
|
+
// brace groups `{...}`, or parens `(...)`. Returns -1 if no pipe found.
|
|
50
|
+
function findFirstTopLevelPipe(query) {
|
|
51
|
+
let inDouble = false;
|
|
52
|
+
let inSingle = false;
|
|
53
|
+
let braceDepth = 0;
|
|
54
|
+
let parenDepth = 0;
|
|
55
|
+
for (let i = 0; i < query.length; i++) {
|
|
56
|
+
const c = query[i];
|
|
57
|
+
const prev = i > 0 ? query[i - 1] : "";
|
|
58
|
+
// Honour backslash escapes inside strings (e.g. `"shopify\\-prod"`).
|
|
59
|
+
if (prev === "\\")
|
|
60
|
+
continue;
|
|
61
|
+
if (inDouble) {
|
|
62
|
+
if (c === '"')
|
|
63
|
+
inDouble = false;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (inSingle) {
|
|
67
|
+
if (c === "'")
|
|
68
|
+
inSingle = false;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (c === '"') {
|
|
72
|
+
inDouble = true;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (c === "'") {
|
|
76
|
+
inSingle = true;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (c === "{") {
|
|
80
|
+
braceDepth++;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (c === "}") {
|
|
84
|
+
braceDepth--;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (c === "(") {
|
|
88
|
+
parenDepth++;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (c === ")") {
|
|
92
|
+
parenDepth--;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (c === "|" && braceDepth === 0 && parenDepth === 0)
|
|
96
|
+
return i;
|
|
97
|
+
}
|
|
98
|
+
return -1;
|
|
99
|
+
}
|
|
100
|
+
// Inject `_time:[start_ns,end_ns)` before the first top-level pipe operator
|
|
101
|
+
// (or at end if none). Quote/brace/paren aware to handle regex alternations
|
|
102
|
+
// like `=~"shopify-latest|shopify-prod"` without splitting mid-string.
|
|
103
|
+
function injectTimeFilter(query, startNs, endNs) {
|
|
104
|
+
const timeFilter = `_time:[${startNs},${endNs})`;
|
|
105
|
+
const pipeIdx = findFirstTopLevelPipe(query);
|
|
106
|
+
if (pipeIdx === -1)
|
|
107
|
+
return `${query.trim()} ${timeFilter}`.trim();
|
|
108
|
+
const head = query.slice(0, pipeIdx).trim();
|
|
109
|
+
const tail = query.slice(pipeIdx);
|
|
110
|
+
return `${head} ${timeFilter} ${tail}`;
|
|
111
|
+
}
|
|
28
112
|
server.tool("query_logs", `Query logs from CubeAPM using LogsQL syntax (VictoriaLogs compatible). Returns log entries matching the query.
|
|
29
113
|
|
|
30
114
|
IMPORTANT - Log Label Discovery:
|
|
@@ -41,20 +125,49 @@ LogsQL Query Syntax:
|
|
|
41
125
|
- Negation: {faas.name!="unwanted-service"}
|
|
42
126
|
- Combine filters: {env="UNSET", faas.name=~".*-lambda.*"}
|
|
43
127
|
|
|
128
|
+
PIPE OPERATORS — chain after query with "|":
|
|
129
|
+
| copy src_field AS dst_field — copy field value
|
|
130
|
+
| drop field1, field2 — remove fields from output
|
|
131
|
+
| extract_regexp "(?P<name>regex)" — extract fields via named capture groups
|
|
132
|
+
| join by (field) (...subquery...) — join with subquery results
|
|
133
|
+
| keep field1, field2 — keep only specified fields
|
|
134
|
+
| limit N — return at most N results
|
|
135
|
+
| math result = field1 + field2 — compute derived fields (+, -, *, /, %)
|
|
136
|
+
| rename src AS dst — rename a field
|
|
137
|
+
| replace (field, "old", "new") — replace substring in field
|
|
138
|
+
| replace_regexp (field, "re", "replacement") — regex replacement
|
|
139
|
+
| sort by (field) [asc|desc] — sort results
|
|
140
|
+
| stats <func> as alias [by (group_fields)] — aggregate results
|
|
141
|
+
| unpack_json — extract fields from JSON log body
|
|
142
|
+
|
|
143
|
+
STATS FUNCTIONS (use with | stats pipe):
|
|
144
|
+
avg(field) — arithmetic mean
|
|
145
|
+
count() — total number of matching entries
|
|
146
|
+
count_empty(field) — count entries where field is empty
|
|
147
|
+
count_uniq(field) — count distinct values
|
|
148
|
+
max(field) — maximum value
|
|
149
|
+
median(field) — median (50th percentile)
|
|
150
|
+
min(field) — minimum value
|
|
151
|
+
quantile(p, field) — p-th quantile (e.g., quantile(0.95, duration))
|
|
152
|
+
sum(field) — sum of values
|
|
153
|
+
|
|
44
154
|
Example queries:
|
|
45
155
|
- All logs (discover structure): *
|
|
46
156
|
- Lambda logs: {faas.name="webhook-lambda-prod"}
|
|
47
157
|
- Search errors: {faas.name=~".*"} AND "error"
|
|
48
|
-
- By environment: {env="production"}
|
|
158
|
+
- By environment: {env="production"}
|
|
159
|
+
- Count errors per function: {faas.name=~".*"} AND "error" | stats count() as error_count by (faas.name)
|
|
160
|
+
- Top 10 slowest: {service_name="my-service"} | sort by (duration) desc | limit 10`, {
|
|
49
161
|
query: z.string().describe("The log search query including stream filters (LogsQL syntax)"),
|
|
50
162
|
start: z.string().describe("Start time in RFC3339 format (e.g., 2024-01-01T00:00:00Z) or Unix timestamp in seconds"),
|
|
51
163
|
end: z.string().describe("End time in RFC3339 format or Unix timestamp in seconds"),
|
|
52
164
|
limit: z.number().optional().default(100).describe("Maximum log entries to return (default: 100)"),
|
|
53
165
|
}, async ({ query, start, end, limit }) => {
|
|
166
|
+
const startNs = toNanoseconds(start);
|
|
167
|
+
const endNs = toNanoseconds(end);
|
|
168
|
+
const finalQuery = injectTimeFilter(query, startNs, endNs);
|
|
54
169
|
const params = new URLSearchParams({
|
|
55
|
-
query,
|
|
56
|
-
start,
|
|
57
|
-
end,
|
|
170
|
+
query: finalQuery,
|
|
58
171
|
limit: String(limit),
|
|
59
172
|
});
|
|
60
173
|
const response = await fetch(`${queryBaseUrl}/api/logs/select/logsql/query`, {
|
|
@@ -206,15 +319,45 @@ IMPORTANT - Required Parameters:
|
|
|
206
319
|
- Use env="UNSET" if environment is not configured
|
|
207
320
|
- Service names are case-sensitive (e.g., "Kratos-Prod" not "kratos")
|
|
208
321
|
|
|
209
|
-
|
|
322
|
+
TRACE QUERY SYNTAX:
|
|
323
|
+
The query parameter supports pipe syntax similar to LogsQL:
|
|
324
|
+
{stream_selector} | pipe1 | pipe2
|
|
325
|
+
|
|
326
|
+
Stream selectors filter spans:
|
|
327
|
+
{service="Kratos-Prod"}
|
|
328
|
+
{service="Kratos-Prod", span_kind="server"}
|
|
329
|
+
{http_code=~"5.."}
|
|
330
|
+
|
|
331
|
+
PIPE OPERATORS — same as LogsQL pipes:
|
|
332
|
+
| copy, | drop, | extract_regexp, | join, | keep, | limit,
|
|
333
|
+
| math, | rename, | replace, | replace_regexp, | sort,
|
|
334
|
+
| stats <func> as alias [by (group_fields)], | unpack_json
|
|
335
|
+
|
|
336
|
+
STATS FUNCTIONS (for aggregate trace analysis):
|
|
337
|
+
avg(field) — arithmetic mean
|
|
338
|
+
count() — total matching spans
|
|
339
|
+
count_uniq(field) — distinct values
|
|
340
|
+
max(field) — maximum value
|
|
341
|
+
median(field) — median value
|
|
342
|
+
min(field) — minimum value
|
|
343
|
+
quantile(p, field) — p-th quantile (e.g., quantile(0.95, duration))
|
|
344
|
+
sum(field) — sum of values
|
|
345
|
+
|
|
346
|
+
IMPORTANT: Duration in traces is in MILLISECONDS.
|
|
347
|
+
IMPORTANT: "p95" is NOT a valid function — use quantile(0.95, duration).
|
|
348
|
+
|
|
349
|
+
Example queries:
|
|
350
|
+
Wildcard: query="*"
|
|
351
|
+
P95 latency: query='{service="Kratos-Prod", span_kind="server"} | stats quantile(0.95, duration) as p95_latency'
|
|
352
|
+
Error count by endpoint: query='{service="Kratos-Prod", status_code="ERROR"} | stats count() as errors by (http_route)'
|
|
353
|
+
Slow spans: query='{service="Kratos-Prod"} | sort by (duration) desc | limit 20'
|
|
354
|
+
|
|
355
|
+
Optional filters (applied in addition to query):
|
|
210
356
|
- spanKind: server, client, consumer, producer
|
|
211
357
|
- sortBy: duration (to find slow traces)
|
|
212
358
|
|
|
213
359
|
To discover available service names, first query metrics:
|
|
214
|
-
count by (service) (cube_apm_calls_total{env="UNSET"})
|
|
215
|
-
|
|
216
|
-
Example: Find slow server spans in Shopify-Prod
|
|
217
|
-
query="*", env="UNSET", service="Shopify-Prod", spanKind="server", sortBy="duration"`, {
|
|
360
|
+
count by (service) (cube_apm_calls_total{env="UNSET"})`, {
|
|
218
361
|
query: z.string().default("*").describe("The traces search query (use * for wildcard)"),
|
|
219
362
|
env: z.string().default("UNSET").describe("Environment name (use UNSET if not configured)"),
|
|
220
363
|
service: z.string().describe("Service name to filter by (REQUIRED, case-sensitive)"),
|
|
@@ -224,13 +367,16 @@ Example: Find slow server spans in Shopify-Prod
|
|
|
224
367
|
spanKind: z.string().optional().describe("Filter by span kind: server, client, consumer, producer"),
|
|
225
368
|
sortBy: z.string().optional().describe("Sort results by: duration"),
|
|
226
369
|
}, async ({ query, env, service, start, end, limit, spanKind, sortBy }) => {
|
|
370
|
+
// Generate a random index value for the API call
|
|
371
|
+
const index = Math.random().toString(36).substring(2, 15);
|
|
227
372
|
const params = new URLSearchParams({
|
|
228
373
|
query,
|
|
229
374
|
env,
|
|
230
375
|
service,
|
|
231
376
|
start,
|
|
232
377
|
end,
|
|
233
|
-
limit: String(limit)
|
|
378
|
+
limit: String(limit),
|
|
379
|
+
index
|
|
234
380
|
});
|
|
235
381
|
if (spanKind)
|
|
236
382
|
params.append("spanKind", spanKind);
|
|
@@ -446,34 +592,122 @@ server.resource("query-patterns", "cubeapm://query-patterns", {
|
|
|
446
592
|
{
|
|
447
593
|
uri: "cubeapm://query-patterns",
|
|
448
594
|
mimeType: "text/markdown",
|
|
449
|
-
text: `# CubeAPM Query Patterns
|
|
595
|
+
text: `# CubeAPM Query Patterns — Full Reference
|
|
450
596
|
|
|
451
|
-
## Metrics
|
|
597
|
+
## Metrics (PromQL / MetricsQL)
|
|
598
|
+
|
|
599
|
+
### Naming Conventions
|
|
452
600
|
- Prefix: \`cube_apm_*\` (e.g., cube_apm_calls_total, cube_apm_latency_bucket)
|
|
453
601
|
- Service label: \`service\` (NOT "server" or "service_name")
|
|
454
602
|
- Common labels: env, service, span_kind, status_code, http_code
|
|
455
603
|
|
|
456
|
-
|
|
457
|
-
CubeAPM uses VictoriaMetrics-style histograms with \`vmrange\` label:
|
|
604
|
+
### Histogram Queries (Percentiles)
|
|
605
|
+
CubeAPM uses VictoriaMetrics-style histograms with \`vmrange\` label (NOT Prometheus \`le\` buckets):
|
|
458
606
|
|
|
459
607
|
\`\`\`promql
|
|
460
|
-
#
|
|
608
|
+
# ✅ Correct — use histogram_quantiles() with vmrange
|
|
461
609
|
histogram_quantiles("phi", 0.95, sum by (vmrange, service) (
|
|
462
610
|
increase(cube_apm_latency_bucket{service="MyService", span_kind="server"}[5m])
|
|
463
611
|
))
|
|
612
|
+
|
|
613
|
+
# ❌ Wrong — standard Prometheus syntax won't work
|
|
614
|
+
histogram_quantile(0.95, sum by (le) (rate(http_request_duration_bucket[5m])))
|
|
464
615
|
\`\`\`
|
|
465
616
|
|
|
466
|
-
|
|
617
|
+
Latency values are in SECONDS (0.05 = 50ms).
|
|
618
|
+
|
|
619
|
+
---
|
|
467
620
|
|
|
468
621
|
## Logs (LogsQL)
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
-
|
|
622
|
+
|
|
623
|
+
### Stream Selectors
|
|
624
|
+
- \`*\` — wildcard, discover all labels
|
|
625
|
+
- \`{faas.name="my-function"}\` — exact match
|
|
626
|
+
- \`{faas.name=~".*-prod"}\` — regex match
|
|
627
|
+
- \`{faas.name!="unwanted"}\` — negation
|
|
628
|
+
- \`{env="production", faas.name=~".*"}\` — combine filters
|
|
629
|
+
|
|
630
|
+
### Text Filters
|
|
631
|
+
- \`{stream} AND "error"\` — text search
|
|
632
|
+
- \`{stream} AND "timeout" AND NOT "retry"\` — boolean operators
|
|
633
|
+
|
|
634
|
+
### Pipe Operators
|
|
635
|
+
Chain after query with \`|\`:
|
|
636
|
+
|
|
637
|
+
| Pipe | Syntax | Description |
|
|
638
|
+
|------|--------|-------------|
|
|
639
|
+
| copy | \`copy src AS dst\` | Copy field value |
|
|
640
|
+
| drop | \`drop field1, field2\` | Remove fields from output |
|
|
641
|
+
| extract_regexp | \`extract_regexp "(?P<name>re)"\` | Extract via named capture groups |
|
|
642
|
+
| join | \`join by (field) (...subquery...)\` | Join with subquery |
|
|
643
|
+
| keep | \`keep field1, field2\` | Keep only specified fields |
|
|
644
|
+
| limit | \`limit N\` | Return at most N results |
|
|
645
|
+
| math | \`math result = f1 + f2\` | Arithmetic (+, -, *, /, %) |
|
|
646
|
+
| rename | \`rename src AS dst\` | Rename a field |
|
|
647
|
+
| replace | \`replace (field, "old", "new")\` | Substring replacement |
|
|
648
|
+
| replace_regexp | \`replace_regexp (field, "re", "repl")\` | Regex replacement |
|
|
649
|
+
| sort | \`sort by (field) [asc/desc]\` | Sort results |
|
|
650
|
+
| stats | \`stats <func> as alias [by (fields)]\` | Aggregate results |
|
|
651
|
+
| unpack_json | \`unpack_json\` | Extract fields from JSON body |
|
|
652
|
+
|
|
653
|
+
### Stats Functions
|
|
654
|
+
Used with \`| stats\` pipe:
|
|
655
|
+
|
|
656
|
+
| Function | Description |
|
|
657
|
+
|----------|-------------|
|
|
658
|
+
| \`avg(field)\` | Arithmetic mean |
|
|
659
|
+
| \`count()\` | Total matching entries |
|
|
660
|
+
| \`count_empty(field)\` | Entries where field is empty |
|
|
661
|
+
| \`count_uniq(field)\` | Distinct values |
|
|
662
|
+
| \`max(field)\` | Maximum value |
|
|
663
|
+
| \`median(field)\` | Median (50th percentile) |
|
|
664
|
+
| \`min(field)\` | Minimum value |
|
|
665
|
+
| \`quantile(p, field)\` | p-th quantile (e.g., quantile(0.95, duration)) |
|
|
666
|
+
| \`sum(field)\` | Sum of values |
|
|
667
|
+
|
|
668
|
+
### Example Log Queries
|
|
669
|
+
\`\`\`logsql
|
|
670
|
+
# Discover labels
|
|
671
|
+
*
|
|
672
|
+
|
|
673
|
+
# Count errors per Lambda function
|
|
674
|
+
{faas.name=~".*"} AND "error" | stats count() as errors by (faas.name)
|
|
675
|
+
|
|
676
|
+
# Top 10 slowest requests
|
|
677
|
+
{service_name="my-service"} | sort by (duration) desc | limit 10
|
|
678
|
+
\`\`\`
|
|
679
|
+
|
|
680
|
+
---
|
|
472
681
|
|
|
473
682
|
## Traces
|
|
683
|
+
|
|
684
|
+
### Query Syntax
|
|
685
|
+
Trace queries use the same pipe syntax:
|
|
686
|
+
\`{stream_selector} | pipe1 | pipe2\`
|
|
687
|
+
|
|
688
|
+
### Stream Selectors
|
|
689
|
+
- \`{service="Kratos-Prod"}\`
|
|
690
|
+
- \`{service="Kratos-Prod", span_kind="server"}\`
|
|
691
|
+
- \`{http_code=~"5.."}\`
|
|
692
|
+
|
|
693
|
+
### Important Notes
|
|
474
694
|
- query, env, service are REQUIRED parameters
|
|
475
|
-
-
|
|
695
|
+
- Duration is in MILLISECONDS (not seconds like metrics)
|
|
696
|
+
- \`p95\` is NOT a valid function — use \`quantile(0.95, duration)\`
|
|
697
|
+
- Use \`sortBy=duration\` param to find slow traces
|
|
476
698
|
- Filter by spanKind: server, client, consumer, producer
|
|
699
|
+
|
|
700
|
+
### Example Trace Queries
|
|
701
|
+
\`\`\`
|
|
702
|
+
# P95 latency for a service
|
|
703
|
+
{service="Kratos-Prod", span_kind="server"} | stats quantile(0.95, duration) as p95_ms
|
|
704
|
+
|
|
705
|
+
# Error count by endpoint
|
|
706
|
+
{service="Kratos-Prod", status_code="ERROR"} | stats count() as errors by (http_route)
|
|
707
|
+
|
|
708
|
+
# Slowest spans
|
|
709
|
+
{service="Kratos-Prod"} | sort by (duration) desc | limit 20
|
|
710
|
+
\`\`\`
|
|
477
711
|
`,
|
|
478
712
|
},
|
|
479
713
|
],
|
package/package.json
CHANGED