feat: add -v/-vv verbosity flags and trace-level hex dumps

Add clap CLI argument parsing with three log-level controls:
  -q/--quiet  errors only
  (default)   info — connections and value changes
  -v          debug — every packet's cmd/src/flags logged on TX and RX
  -vv         trace — full 32-byte hex dump of every packet on the wire

RUST_LOG env var continues to work as a fine-grained override for any
of the above levels.

Also expand the TX log line to include src and flags fields to match
the RX line, making the two symmetric.
This commit is contained in:
2026-04-10 14:56:12 -04:00
parent e83bc02563
commit 635a223fd5
3 changed files with 73 additions and 11 deletions
+6 -5
View File
@@ -10,8 +10,9 @@ name = "qmk-host"
path = "src/main.rs"
[dependencies]
hidapi = "2" # Cross-platform Raw HID access
anyhow = "1" # Ergonomic error handling
log = "0.4" # Logging facade
env_logger = "0.11" # Logger implementation (RUST_LOG env var)
ctrlc = "3" # Ctrl+C / SIGINT handler
hidapi = "2" # Cross-platform Raw HID access
anyhow = "1" # Ergonomic error handling
log = "0.4" # Logging facade
env_logger = "0.11" # Logger implementation (RUST_LOG env var)
ctrlc = "3" # Ctrl+C / SIGINT handler
clap = { version = "4", features = ["derive"] } # CLI argument parsing
+9 -3
View File
@@ -15,7 +15,7 @@ use std::time::Duration;
use anyhow::{Context, Result};
use hidapi::HidApi;
use log::{debug, error, info, warn};
use log::{debug, error, info, trace, warn};
use crate::protocol::{Packet, PACKET_SIZE, USAGE_ID, USAGE_PAGE};
@@ -122,10 +122,14 @@ fn io_loop(
// --- Drain the outgoing write queue (non-blocking) -----------------
while let Ok(pkt) = rx.try_recv() {
write_buf[1..].copy_from_slice(&pkt.0);
trace!("[kbd {:02x}] TX {:02x?}", device_id, &write_buf[1..]);
if let Err(e) = device.write(&write_buf) {
error!("[kbd {:02x}] write error: {}", device_id, e);
} else {
debug!("[kbd {:02x}] sent cmd=0x{:02x}", device_id, pkt.cmd());
debug!(
"[kbd {:02x}] TX cmd=0x{:02x} src=0x{:02x} flags=0x{:02x}",
device_id, pkt.cmd(), pkt.src(), pkt.flags()
);
}
}
@@ -136,11 +140,13 @@ fn io_loop(
}
Ok(_n) => {
let pkt = Packet(read_buf);
trace!("[kbd {:02x}] RX {:02x?}", device_id, &read_buf[..]);
if pkt.is_custom() {
debug!(
"[kbd {:02x}] received cmd=0x{:02x} flags=0x{:02x}",
"[kbd {:02x}] RX cmd=0x{:02x} src=0x{:02x} flags=0x{:02x}",
device_id,
pkt.cmd(),
pkt.src(),
pkt.flags()
);
if to_broker.send((device_id, pkt)).is_err() {
+58 -3
View File
@@ -16,7 +16,11 @@
//! # Usage
//!
//! ```
//! RUST_LOG=debug qmk-host
//! qmk-host # info-level output (connections, value changes)
//! qmk-host -v # debug-level: log every packet cmd/src/flags
//! qmk-host -vv # trace-level: full 32-byte hex dump of every packet
//! qmk-host -q # quiet: errors only
//! RUST_LOG=debug qmk-host # env var still works as an override
//! ```
//!
//! # Permissions
@@ -36,6 +40,7 @@ use std::sync::mpsc;
use std::thread;
use anyhow::{Context, Result};
use clap::Parser;
use log::{error, info, warn};
mod app_monitor;
@@ -47,9 +52,59 @@ mod volume;
use protocol::{DEV_HOST, Packet};
// ---------------------------------------------------------------------------
// CLI
// ---------------------------------------------------------------------------
/// Raw HID bridge for QMK keyboards.
///
/// Syncs layer state between keyboards and delivers host data (volume,
/// brightness) via the QMK raw-HID interface.
#[derive(Parser)]
#[command(name = "qmk-host", version)]
struct Args {
/// Increase log verbosity.
///
/// Pass once (-v) for debug output (every packet's command, source and
/// flags). Pass twice (-vv) for trace output (full 32-byte hex dump of
/// every packet sent and received). The RUST_LOG environment variable
/// still overrides this flag when set.
#[arg(short, long, action = clap::ArgAction::Count)]
verbose: u8,
/// Suppress all output except errors.
#[arg(short, long)]
quiet: bool,
}
impl Args {
/// Resolve the effective log-level filter string from the CLI flags.
/// The caller passes this to `env_logger` as the default; `RUST_LOG`
/// will still override it if set.
fn log_level(&self) -> &'static str {
if self.quiet {
"error"
} else {
match self.verbose {
0 => "info",
1 => "debug",
_ => "trace",
}
}
}
}
// ---------------------------------------------------------------------------
// Entry point
// ---------------------------------------------------------------------------
fn main() -> Result<()> {
// Initialise logging. Set RUST_LOG=debug for verbose output.
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
let args = Args::parse();
env_logger::Builder::from_env(
env_logger::Env::default().default_filter_or(args.log_level()),
)
.init();
info!("qmk-host starting");