Files
mattermost/server
Ben SchumacherandClaude Opus 5.5 d62af319e8 [MM-71392] Search server logs on the server and flag truncated results (#39478)
* [MM-71392] Search server logs on the server and flag truncated results

The structured log viewer only ever loaded each node's latest 10,000
entries and searched them in the browser, so older matches were missed
and nothing said entries were left out.

LogFilter gains an optional search term (max 256 characters), applied
while the log file is scanned: a case-insensitive substring match over
every field except level and timestamp, like the viewer's own search.
A line whose raw bytes can't contain the term is skipped before it is
decoded, so a rare term scans the file several times faster than a rare
level. Remote nodes receive the term through the existing cluster query
and older nodes ignore the unknown field, so the client-side search
stays as a backstop. Since the viewer's search also matches node names
once it lists several nodes, a node whose name holds the term is queried
again unsearched.

The viewer sends the debounced term with each query and live-tail poll,
keeps the list in place instead of flashing the spinner while a search
loads, and shows a notice when a node returned the full page, pointing
to Download for the full file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FpCfx5vEzDnjuW2q3TTD5t

* [MM-71392] Drop the unused getAllLogs selector and duplicate log viewer strings

getAllLogs has had no callers since the log viewer switched to
getAllLogsWithNode, and needed a @ts-expect-error for the node-keyed
response.

The plain text viewer and the Copy log line button had their own IDs for
strings the structured viewer already defines. They now use
admin.logs.ReloadLogs, admin.logs.DownloadLogs, admin.logs.loading and
admin.logs.copied, which keeps the IDs that already have translations.
Labels that read the same but appear in different places, such as the
Node column and the Node filter, stay separate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FpCfx5vEzDnjuW2q3TTD5t

* [MM-71392] Type the logs query response as entries keyed by node

POST /logs/query returns each node's entries keyed by node name, but
AdminState.logs was typed as LogObject[] and Client4.getLogs as
string[], so the selectors had to cast the state. Add LogsByNode and use
it for the client, the reducer, the initial state and AdminState, and
drop the cast.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FpCfx5vEzDnjuW2q3TTD5t

* [MM-71392] Correct the log filter date and log entry types

LogDateFrom and LogDateTo were commented as epochs, but they hold a
formatted date in the server's LogFilterDateLayout. LogObject marked
job_id and worker as required, but only entries logged by background
jobs carry them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FpCfx5vEzDnjuW2q3TTD5t

* [MM-71392] Group every server log level under the viewer's four levels

The server logs panic, fatal, critical, stdlog and trace besides the four
levels the viewer knew. Unchecking any level sent only those four with
the query, so the other levels were dropped on the server and in the
browser. With every level shown, they were styled as debug and missing
from the level counts.

The level filter keeps its four entries, and each now covers a group:
Error is panic, fatal, critical and error; Info is info and stdlog;
Debug is debug and trace. The query sends every level of the checked
groups, and the counts, the browser filter and the row colors use the
same grouping. Rows still show the level's own name. LogLevelEnum gains
the missing levels; silly stays, deprecated, since the server never
logs it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FpCfx5vEzDnjuW2q3TTD5t

* [MM-71392] Add mocked e2e tests for the server logs node, level and search handling

A single-node test server can't log a cluster's response, a failed node,
levels such as fatal or trace, or a full page of entries on demand. The
new spec answers POST /api/v4/logs/query from the test and checks the
viewer and the requests it sends:

- two nodes show the node menu and each row's node, and picking one sends
  server_names
- the failed node entry stays visible under a time range that hides
  older entries
- fatal entries take the error color and stay when Debug is unchecked,
  trace entries are hidden, and the grouped levels are queried
- a full page of 10,000 entries shows the truncation notice
- the search term is sent with the query and the searched response shown

The ServerLogs page object gains the node menu, the levels toggle, the
level badge and the truncation notice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FpCfx5vEzDnjuW2q3TTD5t

* [MM-71392] Cap the server logs search at the length the server accepts

The server rejects search terms over 256 characters, which left the
viewer showing stale results with no sign the search failed.

* [MM-71392] Drop the browser-side search of server logs

The server now searches each node's log file, including node names, so
the viewer listing only entries that also match in the browser just
re-filtered the server's matches. Wherever the two matches disagreed it
hid entries the server found, and it hid the entry for an unreachable
node unless its error contained the term.

During a rolling upgrade, nodes without search support return their
latest entries unsearched, and the viewer now lists them as returned.

* [MM-71392] Simplify the server logs search

With the System Console no longer searching in the browser, the server
no longer has to reproduce its JSON.stringify output exactly. Values
logged as objects are matched as encoding/json wrote them, without
undoing its escapes of <, > and &. The search term comes from a JSON
request, so it is always valid UTF-8 and needs no check. The term
helpers fold into plain functions.

The raw line check and the in-place ASCII comparison stay: without them
a term with a quote took 2.4 s instead of 175 ms on a 64 MB file, and a
rare term allocated 75 MB instead of 135 kB.

Also drop the Server Logs docs note and the API docs sentence on nodes
without search support, and fix comments that still described the
browser search.

* [MM-71392] Stop matching node names in the server logs search

The search matched node names by querying a node whose name held the
term again, unsearched. Hostnames share substrings, such as the ip-10-0-
prefix of every AWS node, so a common term dropped the search for every
node at the cost of a second cluster round trip. The Node selector
already narrows the logs to a node, so the search now matches the log
lines only, and the Node column no longer highlights the term.

* [MM-71392] Rely on the default logs page size for the logs query

Client4.getLogs already requests page 0 with 10,000 entries per node, so
getLogs doesn't pass them again. Also drop the note on nodes without
search support from the LogFilter.Search comment.

* [MM-71392] Scope the server logs truncation notice to the nodes in view

With a node selected, the notice said entries from each node were
limited, because the node list keeps every node seen. It also pointed
to Download with several nodes listed, although Download only saves the
log file of the node serving the request. The per-node title now needs
all nodes in view, and the Download hint only shows with a single node.

* [MM-71392] Fold non-ASCII search terms that lowercase to ASCII

Go lowercases İ to i and the Kelvin sign to k, so a search for İstanbul
became the ASCII term istanbul and was compared byte by byte against a
line still holding İ, which never matched. Whether to compare bytes in
place is now decided from the term before lowercasing.

* [MM-71392] Check each scanned log line against the search term once

A line that passed the raw check before decoding was checked again in
isLogFilteredBySearch. The scan now keeps the first result. Also name
the search length limit and drop two overlapping viewer tests.

* [MM-71392] Pick the server logs search fast path from the line

The in-place ASCII comparison was chosen from the search term, so İ and
the Kelvin sign only matched when the term held them: istanbul missed
İstanbul. The comparison now runs in place only when the line is ASCII,
where it gives the same result as lowercasing the line, and lowercases
any other line. A match is the lowercased value containing the
lowercased term, whichever side holds such characters.

* [MM-71392] Cap stored log entries at the shared logs page size

The truncation notice flags a node once it holds a full page, so the
live tail cap has to match the page size rather than a copy of it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FpCfx22jJ2GZkYnZiLDeMS

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-02 15:13:44 +01:00
..
2023-03-22 17:22:27 -04:00
2023-03-22 17:22:27 -04:00
2023-03-22 17:22:27 -04:00
2023-03-22 17:22:27 -04:00
2024-05-15 12:05:13 -03:00