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