claude-plugins: add a swarm-logs skill
`swarm-logs --help` documents the CLI surface but not the LogsQL constructs an agent needs to build a bounded query with it: no time predicate, and no way to know a bare word matches across the whole swarm rather than one hive. Add a skill teaching `_time:` predicates, hive/service scoping via the collector's stream fields, message vs json format, distinguishing an empty result from a refused request, and running a control query before trusting an empty one. Refs #4460
This commit is contained in:
parent
02f90b7bc6
commit
c95f721f81
1 changed files with 84 additions and 0 deletions
84
claude-plugins/plugins/base/skills/swarm-logs/SKILL.md
Normal file
84
claude-plugins/plugins/base/skills/swarm-logs/SKILL.md
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
---
|
||||
name: swarm-logs
|
||||
description: Build a bounded `swarm-logs query` LogsQL call when investigating an error, confirming a deploy or restart actually happened, checking whether a service emitted something, or chasing any "why did/didn't X happen" question that logs would answer. `swarm-logs --help` documents no time predicate and no per-hive scoping, so an agent copying only what `--help` shows ends up either scanning the whole swarm's history or trusting an empty result it never confirmed. Use this before running `swarm-logs query` for the first time in a session, and any time you're about to call an empty result "nothing happened."
|
||||
---
|
||||
|
||||
# swarm-logs
|
||||
|
||||
`swarm-logs query '<LogsQL>'` is the one verb (plus `--limit` and
|
||||
`--format`). Everything below fills in what `--help` doesn't say, from
|
||||
actually using the tool.
|
||||
|
||||
## Bound the query with `_time:`
|
||||
|
||||
`--help` shows no time predicate at all — there's no flag for it, because
|
||||
it's part of the LogsQL string itself, not a CLI option. Without one, a
|
||||
query runs over the store's whole retention window.
|
||||
|
||||
LogsQL's `_time:` filter (from the store's own query language, VictoriaLogs'
|
||||
LogsQL — this isn't `swarm-logs`-specific syntax):
|
||||
|
||||
- **Relative**: `_time:5m` (last 5 minutes), `_time:1h`, `_time:2d`.
|
||||
- **Absolute range**: `_time:[2026-09-16T00:00:00Z, 2026-09-16T12:00:00Z]`.
|
||||
- **Combine with a search term** — juxtaposition is an implicit `AND`:
|
||||
|
||||
```console
|
||||
$ swarm-logs query '_time:1h "connection refused"' --limit 50
|
||||
$ swarm-logs query '_time:[2026-09-16Z, 2026-09-17Z] deploy AND atlas'
|
||||
```
|
||||
|
||||
Reach for `_time:` on every query you write by hand, not just the slow
|
||||
ones — it's the only thing in this tool that keeps a broad question from
|
||||
becoming a full-retention scan.
|
||||
|
||||
## A bare word searches every hive, not just yours
|
||||
|
||||
`--help` says it plainly: "nothing between here and the store narrows
|
||||
one, so a bare word matches across every hive in the swarm." There's no
|
||||
per-hive flag to fix this from the CLI side — scope it in the query
|
||||
string instead. The collector tags every log line with `_HOSTNAME`,
|
||||
`_MACHINE_ID`, and `_SYSTEMD_UNIT` as stream fields, so add one of those
|
||||
to a query you want confined to your own hive or service:
|
||||
|
||||
```console
|
||||
$ swarm-logs query '_time:1h _stream:{_HOSTNAME="atlas"} "connection refused"'
|
||||
$ swarm-logs query '_time:1h _SYSTEMD_UNIT:="hive-agent.service"' --format json
|
||||
```
|
||||
|
||||
If you don't know your own hostname or unit name offhand, a first query
|
||||
scoped only by `_time:` and a distinctive search term is fine — just
|
||||
don't leave a bare word unscoped and assume the result is about you.
|
||||
|
||||
## `--format message` vs `--format json`
|
||||
|
||||
`message` (the default) prints exactly `_msg` — nothing else, deliberately:
|
||||
the ask this tool answers is "pipe and grep like any other command," and a
|
||||
grep pattern targets the message text alone. If you're missing
|
||||
`_time`, `_stream`, or another field, that's the format choice, not a
|
||||
missing capability — pass `--format json` and read the store's response
|
||||
body straight, one JSON object per line, for `jq` or for the timestamp.
|
||||
|
||||
## Telling "nothing matched" from "the store refused you"
|
||||
|
||||
These look nothing alike once you know the shape, and identical if you
|
||||
don't:
|
||||
|
||||
- **No matching records**: exit 0, empty stdout.
|
||||
- **The store refused the request** (bad auth, malformed query, etc.):
|
||||
non-zero exit, and the error text on stderr includes the HTTP status
|
||||
and the response body. A bare `401` with no further explanation is a
|
||||
real, distinct failure mode here — treat any non-zero exit as "this
|
||||
query didn't run," not as "this query found nothing."
|
||||
|
||||
So: empty output with exit 0 is a real empty result. Anything else is a
|
||||
tooling or auth problem to fix, not a log finding to report.
|
||||
|
||||
## Before you believe an empty result, run a control
|
||||
|
||||
An empty result *looks* like proof of absence, but it's just as
|
||||
consistent with a `_time:` window that missed the event, a scoping field
|
||||
that doesn't match what you expected, or a typo in the search term. Before
|
||||
reporting "no matching logs," run a second query you know must return
|
||||
rows — widen the `_time:` window, or search a bare word you just logged
|
||||
yourself — and confirm it actually returns something. If the control also
|
||||
comes back empty, the query is wrong, not the log store.
|
||||
Loading…
Reference in a new issue