Chapter 9.1 Logging and Diagnostics
RAPx provides two complementary systems for output: a leveled logging framework for development and debugging, and a diagnostic rendering utility for user-facing error reports with annotated source code.
Logging Macros
The logging system is implemented in rapx/src/utils/log.rs, built on the fern and log crates. It provides five macros at increasing severity:
| Macro | Level | Usage |
|---|---|---|
rap_trace! | TRACE | Fine-grained internal state dumps |
rap_debug! | DEBUG | Path enumeration, constraint tracking, fact details |
rap_info! | INFO | Analysis summaries, verification results |
rap_warn! | WARN | Missing contracts, potential unsoundness |
rap_error! | ERROR | Fatal failures; also rap_error_and_exit! to terminate |
All macros use the target "RAPx", are defined as #[macro_export], and format output with timestamps and colored level prefixes:
HH:MM:SS|RAPx|LEVEL|: message
Example output:
21:50:18|RAPx|INFO|: Start analysis with RAP.
21:50:18|RAPx|INFO|: Alias found in Some("::foo"): {(0,1)}
21:50:20|RAPx|WARN|: no safety contracts found for std callee "core::ptr::read"
21:50:21|RAPx|INFO|: SafetyFlow summary: 42 function(s), 87 call edge(s)
Environment Variable
The log level is controlled by RAPX_LOG:
RAPX_LOG=info cargo rapx analyze alias # default — summary output
RAPX_LOG=debug cargo rapx verify # + path/fact detail
RAPX_LOG=trace cargo rapx analyze dataflow # + full internal state
RAPX_LOG=warn cargo rapx check -uaf # warnings only
RAPX_LOG=error cargo rapx verify # errors only
The default level (when RAPX_LOG is unset) is INFO.
Log Format
init_log() configures fern with:
- Timestamps via
chrono::Local - Colored output via
fern::colors::ColoredLevelConfig: Trace=Cyan, Debug=Blue, Info=White+Green, Warn=Yellow, Error=Red - stderr output only (stdout is avoided due to compatibility issues)
Diagnostic Rendering
For user-facing error reports (e.g., "cloning detected here"), RAPx uses the annotate_snippets crate to render annotated source code with underlines and messages. The span utility functions in rapx/src/utils/span.rs bridge rustc's Span objects to annotate_snippets's offset-based API.
Span Utilities
| Function | Input | Output |
|---|---|---|
span_to_source_code(span) | Span | Source string at that span |
span_to_first_line(span) | Span | Span extended to full first line |
span_to_trimmed_span(span) | Span | Span with leading whitespace trimmed |
span_to_filename(span) | Span | File path string |
span_to_line_number(span) | Span | Line number (usize) |
relative_pos_range(span, sub_span) | Span, Span | Byte offset range of sub_span within span |
are_spans_in_same_file(s1, s2) | Span, Span | bool — same source file? |
get_variable_name(body, local) | &Body, usize | Human-readable variable name from debug info |
get_basic_block_span(body, bb) | &Body, usize | Span of a basic block's first statement or terminator |
The key pattern: use relative_pos_range(background_span, target_span) to convert a rustc Span into a byte offset range that annotate_snippets can underline.
Rendering Example
A typical diagnostic report (from the memory-cloning checker in rapx/src/check/opt/memory_cloning/):
#![allow(unused)] fn main() { fn report_used_as_immutable(graph: &Graph, clone_span: Span, use_span: Span) { let code_source = span_to_source_code(graph.span); let filename = span_to_filename(clone_span); let snippet = Snippet::source(&code_source) .line_start(span_to_line_number(graph.span)) .origin(&filename) .fold(true) .annotation( Level::Error .span(relative_pos_range(graph.span, clone_span)) .label("Cloning happens here."), ) .annotation( Level::Error .span(relative_pos_range(graph.span, use_span)) .label("Used here"), ); let message = Level::Warning .title("Unnecessary memory cloning detected") .snippet(snippet) .footer(Level::Help.title("Use borrowings instead.")); let renderer = Renderer::styled(); println!("{}", renderer.render(message)); } }
When clone_span and use_span reference separate lines within a function, the renderer produces text like:
warning: Unnecessary memory cloning detected
--> src/main.rs:15:9
|
15 | let cloned = value.clone();
| ^^^^^^^^^^^^^ Cloning happens here.
...
25 | process(&cloned);
| ^^^^^^^ Used here
|
= help: Use borrowings instead.
Building Messages
annotate_snippets messages are assembled from:
.title(): A one-line summary (Warning, Error, or Info level)..snippet(): ASnippetpointing to source code with.line_start(),.origin()(file path), and one or more.annotation()calls..annotation(): ALevel+.span(byte_range)+.label("explanation")triple..footer(): Optional footer withLevel::Helpfor fix suggestions.Renderer::styled(): Renders with ANSI colors for terminal output.