Logging and diagnostics
This guide explains the default tracing configuration used by the CLI and API at startup and gives copyable filters for narrowing a noisy calculation to the subsystem and work unit that matter.
Prerequisites: a built GammaLoop CLI and a state or run card that reaches the behavior you want to inspect. Filter changes take effect immediately and add negligible setup time, but verbose
inspect or dump events can produce large files and slow a run. The expected invariant is that a narrow directive changes diagnostic visibility, not numerical results. If a filter produces nothing, first use a broader target at debug, confirm that release compilation has not removed the callsite, and then add tag constraints. After isolating the event, record the exact directive with the run card and continue to the CLI/settings reference for persistence and state controls.Default directives
Normal session defaults come from GlobalSettings:
These are the application defaults written into global_settings.toml when defaults are shown or persisted.
In practice, info is the current convention for normal screen-facing output. Selective internal diagnostics should generally stay on debug and be turned on via narrower target/tag filters rather than by raising the whole display logger.
Startup precedence
Startup applies logging in this order:
- Base settings come from
GlobalSettings. Environment overrides replace the base spec:
GL_ALL_LOG_FILTERoverrides both stderr and logfile filters.GL_DISPLAY_FILTERoverrides only the stderr/display filter.GL_LOGFILE_FILTERoverrides only the logfile filter.
CLI overrides are then applied:
-l/--leveloverrides the stderr/display filter for the session.-L/--logfile-leveloverrides the logfile filter for the session.
Some CLI modes hard-disable logfile logging for the session:
--read-only-state--logfile-level off
When logfile logging is hard-disabled at boot, later settings changes cannot re-enable it for that session.
CLI level mapping
-l/--level and -L/--logfile-level expand to explicit crate directives:
off->gammaloop_api=off,gammalooprs=offerror->gammaloop_api=error,gammalooprs=errorwarn->gammaloop_api=warn,gammalooprs=warninfo->gammaloop_api=info,gammalooprs=infodebug->gammaloop_api=debug,gammalooprs=debugtrace->gammaloop_api=trace,gammalooprs=trace
Empty-spec fallback floors
The filter builders also have hardcoded fallback floors used when a spec is empty:
- display/stderr builder fallback:
off - logfile builder fallback:
warn
This is separate from the normal application defaults above. In other words:
- if startup uses the normal settings path, the defaults are
infofor display andofffor logfile - if an empty filter string is explicitly parsed, the display builder falls back to
offand the logfile builder falls back towarn
Runtime updates
Changing global.display_directive or global.logfile_directive after startup reloads the active tracing filters. The CLI stderr override from -l/--level is treated as a session override and is separate from the persisted global setting.
Tag-based debug filtering
For selective debug logging, GammaLoop uses its own logging DSL rather than raw EnvFilter.
This follows the general idea in Tom Mrazik’s tag-based logging note.
The main reason for owning the DSL is that GammaLoop needs composable tag groups and stable semantics around presence, absence, and explicit boolean values.
- Use stable targets for broad subsystems such as
gammalooprs::uv::forestorgammalooprs::integrands::process::cross_section. - Add boolean event fields as composable tags.
- Filter by field presence with
target[{tag_a,tag_b,!tag_c}]=debug, or by displayed boolean values withtarget[{#tag_a,#!tag_c}]=debug.
This is preferable to span-name filtering because span-based filters can admit child-library events emitted while the span is active. GammaLoop tag filters only look at the event target and the boolean tag fields on the event itself.
Supported directive syntax
The supported directive forms are:
leveltarget=leveltarget[{tag_a,tag_b,!tag_c}]=leveltarget[{#tag_a,#!tag_c}]=leveltarget
Notes:
- Directives are comma-separated.
targetis matched as a module-style prefix, sogammalooprs::uv=debugmatchesgammalooprs::uvandgammalooprs::uv::forest.- A directive without an explicit
=levelis treated as=trace. - Tag groups are conjunctions:
[{generation,uv}]means both tags must match. !tagmeans the field is absent.#tagmeans the field is present with boolean valuetrue.#!tagmeans the field is present with boolean valuefalse.field=valuematches an explicit field value.
Tag matching model
Tags are represented by event fields.
#generationat the callsite emitsgeneration = true.- In a directive,
generationmeans the event must carry a field namedgeneration. - In a directive,
#generationmeans the event must carrygeneration = true. - In a directive,
!inspectmeans the event must not carry a field namedinspect. - In a directive,
#!inspectmeans the event must carryinspect = false. - In a directive,
inspect=true,mode=summary, orlabel="soft region"means the event must carry that value specifically.
This means the callsite controls both:
- whether a log is selectable by a presence/absence query
- whether a log can be selected by an explicit value query such as
inspect=falseormode=summary
Static vs dynamic filtering
Presence/absence-only directives stay on the lazy metadata path.
generation!inspectgammalooprs::uv::forest[{generation,uv,!dump}]=debug
These can be decided from the callsite target and declared field names alone, so the filter can answer always or never at callsite registration time.
Value-matching directives are dynamic.
inspect=false#!inspect#generationmode=summarylabel="soft region"
Those depend on the event instance, so they are evaluated in the per-event pass.
Precedence
When multiple directives match the same event, GammaLoop prefers the most specific one:
- longer target prefix wins
- then the directive with more tag requirements wins
- then later directives win if the earlier specificity is identical
This lets broad directives such as gammalooprs=info coexist with narrow tag-specific directives such as gammalooprs::uv::forest[{generation,uv,dump}]=debug.
Copy-pasteable display tags
Display logs render boolean fields next to the source as a directive tag group:
@gammalooprs::uv::forest[{#generation, #uv, #!inspect}] DEBUG: messageEvery boolean field is rendered this way, not only the standard tag vocabulary. true becomes #field_name; false becomes #!field_name. These boolean fields are removed from the normal display field table.
Use the full logging prefix when you want the rendered source to be a valid directive head:
gammaloop --logging-prefix full -l debug ...Then the text after @ and before the log level can be copied into a directive by adding =debug, for example:
display_directive = "gammalooprs::uv::forest[{#generation, #uv, #!inspect}]=debug"This copy-paste form assumes the display source is the module target. Enabling full_line_source adds file and line information to the source display, which is useful for locating code but is not a valid directive target.
Sink-only field prefixes
This repo also has sink-routing prefixes for fields:
file.<name>: only rendered into the logfile/json sinkdisplay.<name>: only rendered into the stderr/display sink
Examples:
file.integrands = %...will appear only in the file logger outputdisplay.progress = %...will appear only in the display logger output
These prefixes are a formatting/routing feature, not a separate event kind. They decide where a field is rendered after the event has been emitted. This lets a callsite attach large payloads such as file.integrands without dumping them to stderr.
Pipeline-wide tag set
Prefer a small stable vocabulary that cuts across the whole pipeline.
Primary phase tags:
generation: work that builds or prepares runtime objects before Monte Carlo integration startsintegration: work performed while evaluating samples or managing the adaptive integration loopprofile: UV/IR/profile-style diagnostic scans and their analysispersistence: reading or writing saved state, manifests, checkpoints, and exported results
Core domain tags:
uv: ultraviolet counterterms, UV forests, UV profiles, or UV-specific generation/evaluation logicir: infrared subtraction, IR profiles, or IR-specific generation/evaluation logicsubtraction: threshold, UV, or IR subtraction logic when the main concern is subtraction rather than the specific regimesampling: channel choice, discrete axes, parameterizations, or sample-generation choicesstability: precision escalation, retries, instability diagnosis, and numerical safety checksobservables: histogramming, event-to-observable projection, and observable snapshot productionselectors: event-selection logic and selector decisionscache: cache lookup, reuse, invalidation, or cache-debug instrumentation
Common work-unit tags:
graph: the log is about one graph or graph-local datagroup: the log is about a graph group or another explicitly grouped aggregateorientation: the log is about orientation-dependent data or choosing/summing orientationschannel: the log is about multi-channeling or a particular channelcut: the log is about a cut, raised cut, or cut-local computationevent: the log is about generated/retained event objects or event-group processingsample: the log is about one evaluation sample, its coordinates, or per-sample intermediate valuesiteration: the log is about one adaptive integration iteration or iteration-level summariesterm: the log is about one algebraic term, summand, or term-local contribution
Common purpose tags:
solver: the log is about root-finding, linear solves, fitting, or similar numerical solver statecompile: the log is about evaluator/code generation or compilation-oriented preparationinspect: the log exposes detailed intermediate state for debugging, rather than a high-level milestonesummary: the log is a compact roll-up rather than a step-by-step tracedump: the log emits large or structured payloads such as expressions, tables, or serialized views
Tag boundaries
Use the most general tag that accurately captures the reason you want to turn the log on or off.
- Prefer
generationovercompilewhen the message is a broad generation milestone. - Add
compileonly when the message is specifically about evaluator construction or compilation-like work. - Prefer
uvorirwhen the regime matters to the user; usesubtractionwhen the subtraction mechanism is the real concern. - Prefer
inspectfor verbose intermediate values that are mainly useful while debugging internals. - Add
dumpwhen the payload is large enough that users may want to suppress it separately from lighter debug logs. - Do not use
graph,cut, orsampleas substitutes forgenerationorintegration; they refine phase tags rather than replace them.
Naming guidance
- Use phase tags first. They answer “when in the pipeline did this happen?”
- Use domain tags second. They answer “what subsystem is this about?”
- Use work-unit tags third. They answer “what object is being worked on?”
- Use purpose tags last for optional refinement.
- Prefer tags that are meaningful across multiple modules.
- Avoid tags that merely restate a function name.
- Avoid tags tied to one internal representation unless that representation is a stable concept in user-facing debugging.
parametric is usually not a core pipeline tag. It is acceptable as a local refinement when needed, but should not be treated as part of the primary vocabulary unless it becomes a consistently useful cross-cutting concept.
Example queries
all generation-time UV forest logs:
gammalooprs::uv::forest[{generation,uv}]=debug
only the large UV forest dumps for orientation-dependent generation logs:
gammalooprs::uv::forest[{generation,uv,orientation,dump}]=debug
UV forest term-by-term generation logs:
gammalooprs::uv::forest[{generation,uv,term}]=debug
all integration-time subtraction debug in amplitude evaluation:
gammalooprs::integrands::process::amplitude[{integration,subtraction}]=debug
per-sample integration inspection in cross-section evaluation:
gammalooprs::integrands::process::cross_section[{integration,sample,inspect}]=debug
all integration-time cut solver debug in cross-section evaluation:
gammalooprs::integrands::process::cross_section[{integration,cut,solver}]=debug
integration-time debug excluding inspect-tagged logs:
gammalooprs::integrands::process::cross_section[{integration,!inspect}]=debug
only events that explicitly set
inspect=true:gammalooprs::integrands::process::cross_section[{integration,inspect=true}]=debug
only events with a specific textual mode field:
gammalooprs::integrands::process::cross_section[{integration,mode=summary}]=debug
Example display directive:
[cli_settings.global]
display_directive = "gammaloop_api=info,gammalooprs=info,symbolica=off,poly::gcd=off,gammalooprs::uv::forest[{generation,uv,orientation,dump}]=debug"Unsupported EnvFilter syntax
GammaLoop no longer treats the directive string as generic EnvFilter syntax.
In particular, do not rely on:
- span-name filters such as
target[span_name]=debug - generic field filters such as
[{field=value}] EnvFilterregex semantics
If those are needed later, they must be added explicitly to the GammaLoop DSL.
Release-build note
gammalooprs is compiled with tracing feature release_max_level_info, so debug! and trace! callsites in that crate are compiled out in release builds.
Authoritative implementation
The startup precedence and sink wiring live in the API tracing setup. The shared directive parser, CLI level mapping, and filter behavior live in the runtime tracing utilities, while persisted defaults are owned by GlobalSettings.