Log Monitors
Watch the logs you send to Zharp and open an incident as soon as a line matches one of your patterns.
A log monitor doesn't probe anything. It reads the log lines already stored in Zharp — collected by the Zharp agent, drained from your hosting provider, or sent from your code or the API — and goes down when a line from the services you chose matches a pattern.
Before you start
- Logs must already be arriving. You choose services from the names Zharp has seen, so send some logs first — see Logs. Until then the service list says No log services found.
Create a log monitor
- 1Pick the type
On the Monitors page click New, open Monitor type and choose Log monitor.
- 2Choose the services
In Log service names, pick one or more services from the list — the
serviceyou send with each log entry through the API, or the name the agent reports: the--servicename given to it for a log file, the Compose service (or container name) for a Docker container, the unit name without.servicefor a systemd service, and the app name for a PM2 app. Only services Zharp has already received logs from can be picked; typing a new name does nothing. - 3Write the patterns
In Alert when these appear, enter one pattern per line. A pattern is a keyword or a regular expression:
ERROR Failed to connect OutOfMemoryError HTTP/1\.[01]" 50[0-9] payment (declined|timed out)
- 4Create it
Click Create monitor. At least one service and one pattern are required. The monitor scans on its Check frequency (under Advanced settings, 3 minutes by default).
How patterns match
- Every pattern is a regular expression, matched against the line's text and its level — the two you see side by side on the Logs page — anywhere and ignoring case. So
ERRORcatches every line logged at the error level whatever its text says, and also text such asno errors found. Anchor or extend a pattern when that's too broad:^ERRORonly matches text that starts with it (and the levelerror). - A line matches when it matches any of the patterns. There is no count or threshold: one matching line is enough.
- Write the expression on its own. If you wrap it in slashes the JavaScript way, as in
/timeout/i, Zharp removes the slashes and any trailing flags when you save — matching always ignores case anyway. - Characters such as
.([?+*have regex meaning. Escape them with a backslash to match them literally, as inHTTP/1\.1. - Only the text and the level are searched — not the service name or metadata. To narrow by those, use the scope below.
Saving fails with Invalid log pattern: … naming the pattern and the problem when one of them isn't a valid regular expression — an unclosed ( or [, for example — or uses a named group such as (?<code>\d+), which the matcher doesn't support. If a pattern saved earlier is still invalid, the scan skips it and keeps matching the others.
Scope by environment, source and fields
Three optional fields on the form narrow what a monitor scans, on top of its services. Leave them empty to scan everything, as before. All of them must hold for a line to count, and a line that matches the scope still has to match a pattern — use . as the pattern to alert on every line in the scope.
| Field | What it does | Example |
|---|---|---|
| Environments | Only lines tagged with one of these environments (unified service tagging). Untagged lines are left out once any is set. | prod |
| Sources | Only lines from these technologies — declared, or detected from the line's format. | nginx, postgresql |
| Field conditions | One condition per line on the fields Zharp parsed from the line or the attributes it was sent with. | http.status_code>=500 |
A field condition is one field, an operator and a value:
| Write | Matches lines where |
|---|---|
http.method=POST | the field equals the value, compared as text (client.ip:10.0.0.4 means the same) |
http.method!=GET | the field differs from the value, or the line doesn't have it |
duration_ms>1000 | the field is a number and compares so — also >= < <=. Lines where it isn't a number don't match |
error.type:* | the line has the field at all |
Up to 10 conditions; a condition that doesn't parse is refused when you save, with Invalid field filter: … saying why. The incident records the environments and sources the monitor was scoped to, and the incident's log view and the monitor page's stream show only those environments and sources. On the Logs screen, type the same conditions into the search box with an @ in front — payment @http.status_code>=500 searches for “payment” on lines whose status code is 500 or more.
Log levels
Levels are stored in one spelling whatever case or synonym the log used, so ERROR, Error and err are all the same level, and filters in the log viewer treat them alike:
| Stored as | Also accepted |
|---|---|
fatal | FATAL, critical, crit, emerg, alert, panic |
error | ERROR, Error, err, severe |
warn | WARN, warning, Warning |
info | INFO, notice, and lines sent without a level or with one Zharp doesn't recognise |
debug | DEBUG, trace is kept as its own level below debug |
Syslog severity numbers (0–7) and OpenTelemetry severity text such as ERROR2 are folded the same way. A log monitor scans info, warn, error and fatal lines — everything except debug and trace.
The agent sets the level where the source states one: a systemd service's priority (0–2 fatal, 3 error, 4 warn, 5–6 info, 7 debug), or for a container a JSON level/severity field, level=… or a leading ERROR/WARN/INFO/DEBUG. Anything else — including whatever a program writes to stderr — is stored as info, so match on the message to catch errors that don't say so.
When it scans
Each scan covers the log lines timestamped since the previous scan, up to 5 seconds before the scan starts; the first scan looks back 60 seconds. Lines are found by their own timestamp — the one you send, the one the agent reads from the start of the line, or the time Docker or the journal recorded it — so a line that reaches Zharp after a scan has already passed its timestamp isn't picked up.
Down, and back up
When a scan finds matching lines, the monitor goes down and opens an incident:
| Field | Example |
|---|---|
| Monitor message | Log Pattern Matched: Failed to connect |
| Incident title | Pattern Match: "Failed to connect" in Checkout logs |
| Incident cause | Pattern "Failed to connect" matched 4 times in 3 min |
The count is every matching line in the scan, and the time is the span that scan actually covered — normally your check frequency, so a monitor checking every 3 minutes says in 3 min. The incident lists up to 20 of the matching lines, newest first, with the pattern each one matched; the count isn't limited to those 20, and a line that matches several patterns counts once. The monitor page shows the same count and window for the latest scan. While the incident is open, later matches are added to its timeline instead of opening new incidents.
Unlike a check that starts passing again, quiet logs don't prove the problem is gone. The monitor stays down as long as its incident is open. Once you click Resolve on the incident, the next scan with no matches brings the monitor back up.
Maintenance windows
Log monitors follow maintenance windows like every other monitor, including recurring ones. While a window covers the monitor it shows Maintenance: nothing is scanned, it doesn't go down, no incident opens and no alert goes out. Lines logged during the window are skipped for good — the first scan after it ends only looks at what came in after the window.