Repository navigation
feat(dgw): search session logs across recordings - #2112
irvingouj@Devolutions (irvingoujAtDevolution) wants to merge 3 commits into
Conversation
Add `POST /jet/jrec/search`, so DVLS can search the `.slog` logs of
recordings without downloading every log.
The body is `{ "query": "...", "recordings": "all" | ["<id>", ...] }`,
guarded by a new `gateway.recordings.search` scope. The existing
`gateway.recordings.read` only lists IDs, so reusing it would let every
read token see log contents.
The search reads the `.slog` files and AI analysis logs listed in
`recording.json`, with the rules of the log viewer's parser: a
case-insensitive substring over the shown fields and parameters,
entries the viewer drops are skipped, and long strings match on their
shown part only. Hits are the original lines. A search stops at 1000
hits, 16 MiB of hits or 256 MiB read, and reports it with
`limitReached`.
`api/jrec.rs` moves to `api/jrec/mod.rs` to hold the `search`
submodule.
Issue: DGW-463
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Let maintainers know that an action is required on their side
|
There was a problem hiding this comment.
🟡 Changes recommended
The scan-byte limit can be bypassed, and parsing differences cause missed viewer-visible matches.
4 open issues
What changed in this PR
Adds server-side recording-log search so DVLS can find matches without downloading each log.
Changes:
- Adds
POST /jet/jrec/searchwith a dedicated authorization scope. - Searches recording and artifact logs using viewer-compatible matching and resource limits.
- Adds OpenAPI definitions, .NET scope support, and tests.
| File | Description |
|---|---|
| utils/dotnet/Devolutions.Gateway.Utils/src/AccessScope.cs | Adds the search scope. |
| utils/dotnet/Devolutions.Gateway.Utils.Tests/JsonSerializationTests.cs | Tests scope serialization. |
| testsuite/tests/cli/dgw/recording_search.rs | Tests search results, ordering, deduplication, and authorization. |
| testsuite/tests/cli/dgw/mod.rs | Registers search integration tests. |
| devolutions-gateway/src/token.rs | Defines the search token scope. |
| devolutions-gateway/src/openapi.rs | Registers the endpoint and schemas. |
| devolutions-gateway/src/extract.rs | Enforces search authorization. |
| devolutions-gateway/src/api/jrec/search.rs | Implements log discovery, matching, limits, and unit tests. |
| devolutions-gateway/src/api/jrec/mod.rs | Relocates the recording API module and registers the route. |
| devolutions-gateway/Cargo.toml | Enables raw JSON values for returned entries. |
Review effort: Balanced
| let mut parsed_entries = 0; | ||
| let mut retained_bytes = 0; | ||
|
|
||
| while let Some(read) = read_line(&mut reader, &mut line).await? { |
| // A `serde_json::Map` sorts its keys, and which parameters the viewers keep depends on their order. | ||
| #[derive(Deserialize)] | ||
| struct LineParameters { | ||
| parameters: OrderedObject, | ||
| } | ||
|
|
||
| let Ok(LineParameters { parameters }) = serde_json::from_slice(line) else { | ||
| return Some(false); | ||
| }; |
| .filter_map(|(name, value)| Some((visible_text(name), value.as_str()?))) | ||
| .filter(|(name, _)| kept_names.insert(*name)) |
| if line.len() + chunk.len() <= MAX_LINE_BYTES { | ||
| line.extend_from_slice(chunk); | ||
| } else { | ||
| read.too_long = true; | ||
| line.clear(); |
The log viewer strips a trailing `\r` before it checks a line against the 256 KiB limit, so a CRLF line of exactly 256 KiB is shown there. The search now strips it first too, also on a last line without `\n`. Mark the search invariants with `INVARIANT:` and give each recording selection case its own test. Issue: DGW-463 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Untested properties (marked
The OpenAPI document and the clients come in a separate PR, like the AI analysis endpoints in #2054. |
The derived schema put `"all"` in an `AllRecordings` schema of its own, and the C# client generator (7.9.0) turns that `oneOf` member into a constructor that does not compile. The schema now lists `"all"` inline, like `MonitoringProbeType`; the JSON is unchanged. Also test the line limit one byte over it and on a last line ending with `\r`, and keep `INVARIANT:` for definitions only. Issue: DGW-463 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Add
POST /jet/jrec/search, so DVLS can search the.sloglogs of recordings without downloading every log.The body is
{ "query": "...", "recordings": "all" | ["<id>", ...] }, guarded by a newgateway.recordings.searchscope, whichDevolutions.Gateway.Utilsgets too. The existinggateway.recordings.readonly lists IDs, so reusing it would let every read token see log contents.The search reads the
.slogfiles and AI analysis logs listed inrecording.json, with the rules of the log viewer's parser: a case-insensitive substring over the shown fields and parameters, entries the viewer drops are skipped, and long strings match on their shown part only. Hits are the original lines, newest recording first for"all". A search stops at 1000 hits, 16 MiB of hits or 256 MiB read, and reports it withlimitReached.api/jrec.rsmoves toapi/jrec/mod.rsto hold thesearchsubmodule.🤖 Generated with Claude Code
Issue: DGW-463