Skip to content

Commit a127df5

Browse files
committed
oxlog: Glob match zone names
`oxlog logs` currently requires a precise match for the zone name. This is inconvenient when attempting to search across multiple zones with similar names. For example, when searching all Crucible downstairs, or using Pilot to query Nexus logs on multiple sleds. Use the `glob` crate to take a pattern to match against the zone name for the `services` and `logs` subcommands, potentially allowing multiple zones to be returned. If more than one zone is matched, then the logs and services will be sorted globally. Example usage: $ oxlog services 'oxz_crucible_[!p]*' # All non-pantry Crucible zones $ oxlog logs 'oxz_nexus_*' --current # The nexus zone
1 parent 267d3d8 commit a127df5

File tree

4 files changed

+63
-39
lines changed

4 files changed

+63
-39
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dev-tools/oxlog/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ anyhow.workspace = true
1212
camino.workspace = true
1313
chrono.workspace = true
1414
clap.workspace = true
15+
glob.workspace = true
1516
jiff.workspace = true
1617
sigpipe.workspace = true
1718
uuid.workspace = true

dev-tools/oxlog/src/bin/oxlog.rs

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
//! Tool for discovering oxide related logfiles on sleds
66
77
use clap::{ArgAction, Args, Parser, Subcommand};
8+
use glob::Pattern;
89
use jiff::civil::DateTime;
910
use jiff::tz::TimeZone;
1011
use jiff::{Span, Timestamp};
1112
use oxlog::{DateRange, Filter, LogFile, Zones};
1213
use std::collections::BTreeSet;
14+
use std::str::FromStr;
1315

1416
#[derive(Debug, Parser)]
1517
#[command(version)]
@@ -25,8 +27,8 @@ enum Commands {
2527

2628
/// List logs for a given service
2729
Logs {
28-
/// The name of the zone
29-
zone: String,
30+
/// The glob pattern to match against zone names
31+
zone_glob: GlobPattern,
3032

3133
/// The name of the service to list logs for
3234
service: Option<String>,
@@ -49,15 +51,26 @@ enum Commands {
4951
after: Option<Timestamp>,
5052
},
5153

52-
/// List the names of all services in a zone, from the perspective of oxlog.
54+
/// List the names of all services in matching zones, from the perspective of oxlog.
5355
/// Use these names with `oxlog logs` to filter output to logs from a
5456
/// specific service.
5557
Services {
56-
/// The name of the zone
57-
zone: String,
58+
/// The glob pattern to match against zone names
59+
zone_glob: GlobPattern,
5860
},
5961
}
6062

63+
#[derive(Clone, Debug)]
64+
struct GlobPattern(Pattern);
65+
66+
impl FromStr for GlobPattern {
67+
type Err = glob::PatternError;
68+
69+
fn from_str(s: &str) -> Result<Self, Self::Err> {
70+
Pattern::new(s).map(GlobPattern)
71+
}
72+
}
73+
6174
#[derive(Args, Debug)]
6275
#[group(required = true, multiple = true)]
6376
struct FilterArgs {
@@ -118,7 +131,14 @@ fn main() -> Result<(), anyhow::Error> {
118131
}
119132
Ok(())
120133
}
121-
Commands::Logs { zone, service, metadata, filter, before, after } => {
134+
Commands::Logs {
135+
zone_glob,
136+
service,
137+
metadata,
138+
filter,
139+
before,
140+
after,
141+
} => {
122142
let zones = Zones::load()?;
123143
let date_range = match (before, after) {
124144
(None, None) => None,
@@ -145,7 +165,7 @@ fn main() -> Result<(), anyhow::Error> {
145165
);
146166
};
147167

148-
let logs = zones.zone_logs(&zone, filter);
168+
let logs = zones.zone_logs(&zone_glob.0, filter);
149169
for (svc_name, svc_logs) in logs {
150170
if let Some(service) = &service {
151171
if svc_name != service.as_str() {
@@ -182,7 +202,7 @@ fn main() -> Result<(), anyhow::Error> {
182202
}
183203
Ok(())
184204
}
185-
Commands::Services { zone } => {
205+
Commands::Services { zone_glob } => {
186206
let zones = Zones::load()?;
187207

188208
// We want all logs that exist, anywhere, so we can find their
@@ -198,7 +218,7 @@ fn main() -> Result<(), anyhow::Error> {
198218
// Collect a unique set of services, based on the logs in the
199219
// specified zone
200220
let services: BTreeSet<String> =
201-
zones.zone_logs(&zone, filter).into_keys().collect();
221+
zones.zone_logs(&zone_glob.0, filter).into_keys().collect();
202222

203223
for svc in services {
204224
println!("{}", svc);

dev-tools/oxlog/src/lib.rs

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
99
use anyhow::Context;
1010
use camino::{Utf8DirEntry, Utf8Path, Utf8PathBuf};
11+
use glob::Pattern;
1112
use jiff::Timestamp;
1213
use std::collections::BTreeMap;
1314
use std::io;
@@ -302,47 +303,48 @@ impl Zones {
302303
Ok(Zones { zones })
303304
}
304305

305-
/// Return log files organized by service name
306+
/// Return log files for all matching zone names organized by service name
306307
pub fn zone_logs(
307308
&self,
308-
zone: &str,
309+
zone_pattern: &Pattern,
309310
filter: Filter,
310311
) -> BTreeMap<ServiceName, SvcLogs> {
311312
let mut output = BTreeMap::new();
312-
let Some(paths) = self.zones.get(zone) else {
313-
return BTreeMap::new();
314-
};
315-
// Some rotated files exist in `paths.primary` that we track as
316-
// 'archived'. These files have not yet been migrated into the debug
317-
// directory.
318-
if filter.current || filter.archived {
319-
load_svc_logs(
320-
paths.primary.clone(),
321-
&mut output,
322-
filter.show_empty,
323-
filter.date_range,
324-
);
325-
}
326-
327-
if filter.archived {
328-
for dir in paths.debug.clone() {
313+
for (_, paths) in
314+
self.zones.iter().filter(|(zone, _)| zone_pattern.matches(zone))
315+
{
316+
// Some rotated files exist in `paths.primary` that we track as
317+
// 'archived'. These files have not yet been migrated into the debug
318+
// directory.
319+
if filter.current || filter.archived {
329320
load_svc_logs(
330-
dir,
321+
paths.primary.clone(),
331322
&mut output,
332323
filter.show_empty,
333324
filter.date_range,
334325
);
335326
}
336-
}
337-
if filter.extra {
338-
for (svc_name, dir) in paths.extra.clone() {
339-
load_extra_logs(
340-
dir,
341-
svc_name,
342-
&mut output,
343-
filter.show_empty,
344-
filter.date_range,
345-
);
327+
328+
if filter.archived {
329+
for dir in paths.debug.clone() {
330+
load_svc_logs(
331+
dir,
332+
&mut output,
333+
filter.show_empty,
334+
filter.date_range,
335+
);
336+
}
337+
}
338+
if filter.extra {
339+
for (svc_name, dir) in paths.extra.clone() {
340+
load_extra_logs(
341+
dir,
342+
svc_name,
343+
&mut output,
344+
filter.show_empty,
345+
filter.date_range,
346+
);
347+
}
346348
}
347349
}
348350

0 commit comments

Comments
 (0)