pyrtan / sourcev0.13.0 · main

pyrtan / examples / log-errors / log-errors.toml

1,886 bytes · last changed 2026-10-26 in The catalog grows up: namespaces, publishing by pull request through the gate, gap reports gossiped back to authors; release 0.8.0

1# Error lines in the tail of a log on the box this tree runs on. A file node
2# reads a path (whole, or its last `tail` lines); `paths` is the allow-list,
3# and a path from the sentence is refused at run time when it falls outside
4# /var/log, ".." included.
5
6name = "log-errors"
7version = 2
8namespace = "pyrtan"
9description = "Error lines in the tail of a log file on this box."
10tags = ["ops", "box", "read-only"]
11license = "MIT"
12paths = ["/var/log"]
13start = "read"
14
15[[triggers]]
16pattern = "errors in the log"
17[[triggers]]
18pattern = "errors in {path:word}"
19
20[inputs.path]
21type = "str"
22default = "/var/log/syslog"
23
24[inputs.lines]
25type = "int"
26default = 200
27min = 1
28max = 5000
29description = "How many lines from the end to look at"
30
31[nodes.read]
32kind = "observe"
33file = { path = "{{ inputs.path }}", tail = "{{ inputs.lines }}" }
34into = "log" # state.log = { path, exists, size, modified, body, json, lines }
35extract = [{ into = "errors", regex = "(?im)^.*\\berror\\b.*$", all = true }]
36next = "exists"
37
38[nodes.exists]
39kind = "decide"
40on = "log.exists"
41branches = [{ when = { eq = true }, then = "count" }, { else = "missing" }]
42
43[nodes.count]
44kind = "set"
45set = { n = "{{ state.errors | length }}", seen = "{{ state.log.lines | length }}" }
46next = "any"
47
48[nodes.any]
49kind = "decide"
50on = "n"
51branches = [{ when = { eq = 0 }, then = "clean" }, { else = "some" }]
52
53[nodes.clean]
54kind = "done"
55say = "No error lines in the last {{ state.seen }} of {{ inputs.path }}."
56output = { path = "{{ inputs.path }}", errors = 0 }
57
58[nodes.some]
59kind = "done"
60say = "{{ state.n }} error line(s) in the last {{ state.seen }} of {{ inputs.path }}; latest: {{ state.errors | last | strip }}"
61output = { path = "{{ inputs.path }}", errors = "{{ state.n }}", lines = "{{ state.errors }}" }
62
63[nodes.missing]
64kind = "fail"
65reason = "no-such-file"
66say = "{{ inputs.path }} is not on this box."