Why progressive verbosity? Developers want different levels of detail at different times. Simple mental model: more v's = more info. LLMs can use higher verbosity for better context.
Progressive verbosity pattern for QNTX CLI commands using the -v flag.
qntx <command> # Level 0 (default) - Results and errors only
qntx <command> -v # Level 1 - Progress, startup, plugin status
qntx <command> -vv # Level 2 - Queries, timing, config details
qntx <command> -vvv # Level 3 - Plugin logs, SQL, gRPC calls
qntx <command> -vvvv # Level 4 - Full request/response bodies
Verbosity controls what categories of output are shown, not just log severity. The system uses semantic output categories defined in logger/output.go.
| Category | Level | Description |
|---|---|---|
| Results, Errors | 0 | Final results and critical errors (always shown) |
| Progress | 1 | Startup messages, plugin status, progress updates |
| Ax AST | 1 | Parsed query AST for Ax queries |
| Plugin Status | 1 | Plugin load/unload notifications |
| Ax Matches | 2 | What attestations matched the query |
| Timing | 2 | Operation timing (opt-in, auto-shown if slow) |
| Config | 2 | Configuration details |
| Plugin Stdout | 3 | Standard output from plugins |
| Plugin Stderr | 3 | Standard error from plugins |
| SQL Queries | 4 | Raw SQL queries executed |
| gRPC Calls | 4 | gRPC method calls |
| Full Payloads | 4 | Complete request/response bodies |
Timing information has special handling via logger.ShouldShowTiming():
-vv or higher: Always show timingif logger.ShouldShowTiming(verbosity, durationMS) {
fmt.Printf("Operation took %dms\n", durationMS)
}
Zero and default values add noise at lower verbosity levels. They're only shown at level 4 (-vvvv) where confirming "this is actually zero" matters for full data dumps.
Filtered at levels 0-3:
count=0, total=0, group=0cost_estimate=0.0, cost_actual=0.0type="", name=""type="untyped", status="unknown"Only shown at level 4: All values including zeros for complete data dumps.
// Only log non-zero counts at lower verbosity
if count > 0 || logger.ShouldShowZeroInt(verbosity) {
log.Infow("Processed items", "count", count)
}
// Only log if value differs from default
if status != "unknown" || logger.ShouldShowDefaultValue(verbosity) {
log.Infow("Status check", "status", status)
}
Ax queries have granular output control:
Use logger.ShouldOutput() to check if output should be shown:
import "github.com/teranos/QNTX/logger"
func executeQuery(verbosity int, query string) {
// Level 1+: Show parsed AST
if logger.ShouldOutput(verbosity, logger.OutputAxAST) {
fmt.Printf("Parsed AST: %v\n", ast)
}
// Level 2+: Show what matched
if logger.ShouldOutput(verbosity, logger.OutputAxMatches) {
fmt.Printf("Matched %d attestations\n", len(matches))
}
// Level 4+: Show raw SQL
if logger.ShouldOutput(verbosity, logger.OutputSQLQueries) {
fmt.Printf("SQL: %s\n", sqlQuery)
}
}
Verbosity also maps to zap log levels via logger.VerbosityToLevel():
| Verbosity | Log Level |
|---|---|
| 0 (none) | WarnLevel |
| 1 (-v) | InfoLevel |
| 2+ (-vv) | DebugLevel |
Use structured symbol logging instead of embedding symbols in messages:
// Instead of:
logger.Infow(sym.Pulse + " Job started", "job_id", id)
// Use:
logger.PulseInfow("Job started", "job_id", id)
// Or with instance loggers:
pulseLog := logger.AddPulseSymbol(s.logger)
pulseLog.Infow("Job started", "job_id", id)
This keeps log messages clean and makes symbols queryable as structured fields.
Clean, user-facing output only:
Hides parsing details, internal processing, SQL queries, and debug information.
-v)Progress and status information:
-vv)Debug information for troubleshooting:
-vvv)Deep debugging context:
-vvvv)Ultra-verbose troubleshooting:
Subject: "user_123"
Predicate: "has_skill"
Object: "Go"
1 attestation found
-v)Parsing query...
Subject: "user_123"
Predicate: "has_skill"
Object: "Go"
Context: (none)
Executing query...
Found 1 matching attestation
Results:
[attestation details...]
-vv)Parsing query...
Subject: "user_123"
Predicate: "has_skill"
Object: "Go"
Matched attestations:
- ats_123abc... (created 2024-01-15)
Execution time: 2.3ms
Results with full context:
[Complete attestation data with metadata...]
-vvvv)[All Level 2 output plus:]
SQL: SELECT * FROM attestations WHERE subject = ? AND predicate = ? AND object = ?
Parameters: ["user_123", "has_skill", "Go"]
The verbosity flag is defined globally in the root command and available to all subcommands. Commands interpret levels based on their specific needs while following the general pattern above.
See logger/output.go for the complete list of output categories and their verbosity requirements.