Platform profiles¶
A platform profile (nimi-service/parsers/local/<name>.yaml, git-ignored) teaches NIMI about one platform's messages
without code changes:
- log layouts: how the platform's log lines look, so logs get a proper service, logger, title and signature;
- subject masks: platform-specific item names to leave out of signatures;
- triage notes: facts that help triage, shown only for messages they apply to.
Profiles are data (patterns). Yours live in parsers/local/, which is git-ignored because it describes your
platforms; parsers/examples/example.yaml is a documented sample that is never loaded (copy it to
local/ to start). The service is the only thing that reads profiles; the RCA skill always works from the
full message text. With no profiles at all, the generic parse still works.
Why signatures matter¶
The parser gives each message a signature, its "kind of message". Triage only sees history and earlier
RCAs with the same signature, and exclude --like matches by signature. If a format isn't recognised, the
generic fallback still groups messages (masking numbers, IDs, timestamps, pod suffixes, Java object hashes
such as Order@1b6d3586, hosts and common item names), so nothing breaks: the cost is extra RCAs, never wrong ones. A layout makes the grouping
precise and the titles readable.
How messages are matched¶
Every message is tried against the layouts of all profiles, regardless of channel, because one channel
can carry messages from several platforms and formats. Layouts are tried in priority order (lower first;
default 100, then file and list order); the first layout whose header matches a line wins. Messages no
layout matches get the generic parse.
When two platforms' lines look alike:
- same fields in the same places: it doesn't matter which layout matches; the service still comes from the pod/service name in the line;
- similar shape, different meaning: narrow a layout with
when:(sender display names, or text it must contain) and/or give it a lowerpriority.
Writing a profile¶
name: myplatform # short lowercase id
description: Example services logging with a common Java/logback layout
log_layouts:
- name: level-first
# [ERROR] [2026-01-15 10:04:31+0000] [shop-api-deployment-5c6cb9696f-6h8mk] [9] [http-nio-8080-exec-3] [] [] com.example.shop.api.CheckoutController Payment failed …
header: '^\[(?P<level>ERROR|WARN|FATAL)\]\s*\[(?P<ts>[^\]]+)\]\s*\[(?P<pod>[^\]]+)\](?:\s*\[[^\]]*\])*\s+(?P<logger>[\w$]+(?:\.[\w$]+)+)\s+(?P<message>.*)$'
message: same_line # the error text is on the header line (next_lines: on the lines after)
# when: {sender: ["Shop API Errors"], contains: ["com.example.shop"]}
# priority: 50
subject_masks: # replaced by <name> in signatures
- '(?<=for cart )[\w-]+' # "Payment failed for cart c-17 …"
triage_notes:
- applies_to: {text_contains: ["Stock Sync Reminder"]}
note: >-
"Stock Sync Reminder" asks the warehouse team to confirm a manual stock sync; it reports no system
failure.
| Key | Meaning |
|---|---|
header |
Regular expression (case-insensitive) matched against each line of the message. Named groups: pod or service (required; a pod's generated suffix is stripped to get the service), logger, level, message. |
message |
next_lines (default): the error is the first exception line after the header. same_line: the message group. |
when |
Optional. sender: display names; contains: texts the message must contain (any). |
priority |
Optional integer, lower first (default 100). |
subject_masks |
Regular expressions for item names (databases, tenants, …) to mask in signatures. |
triage_notes |
applies_to.text_contains (required; any of, case-insensitive) and note. |
The signature of a log is log|<service>|<logger>|<error text with items masked>.
Triage notes are shown to the triage model as trusted guidance from NIMI's maintainers. Write only confirmed facts that help decide reuse vs new vs not applicable, e.g. "this alert lists only newly affected tables" or "this alert is a reminder, not a failure". Root causes and runbook knowledge belong in the project's repo docs, where the RCA skill finds them. The code limits on reuse (only offered candidates, never one rated 👎, the reuse window) always apply.
Testing and applying a change¶
.venv/bin/python -m nimi_service parse-test --text "<a log line or alert>" # one message, plus matching notes
.venv/bin/python -m nimi_service parse-test --channel app-logs --kind other # stored messages still unmatched
.venv/bin/python -m nimi_service parse-test --sender "Shop API Errors" --limit 5
.venv/bin/python -m nimi_service reparse # apply to all stored messages
parse-test writes nothing. For stored messages it shows which layout matched, the title and signature
(with the current one when it would change), and a summary: layouts used, distinct signatures, and how
many messages reparse would change.
A profile is validated when the service starts; an invalid one (unknown key, bad regex, missing group)
stops it with the file and location. The parser version includes a hash of all profiles, so stored parses
show which profile version produced them; run reparse after every profile change.