Macro tor_hsservice::internal_prelude::log_ratelim

source ·
macro_rules! log_ratelim {
    {
    @impl activity_format: ( $act_fmt:literal $(, $act_arg:expr)* ) ;
          result: ($result:expr ) ;
          on_error: (Err($err_pat:pat), $err_level:ident $(, $err_fmt:literal $(, $err_arg:expr)* )? );
          $( on_ok: (Ok($ok_pat:pat), $ok_level:ident, $ok_fmt:literal $(, $ok_arg:expr)*  ); )?
  } => { ... };
    {
    $act_fmt:literal $(, $act_arg:expr )* $(,)? ;
    $result:expr ;
    Err($err_pat:pat) => $err_level:ident $(, $err_fmt:literal $(, $err_arg:expr)* )? $(,)?
    $(; Ok($ok_pat:pat) => $ok_level:ident, $ok_fmt:literal $(, $ok_arg:expr )* $(,)?  )?
    $(;)?
  } => { ... };
    {
    $act_fmt:literal $(, $act_arg:expr )* $(,)? ;
    $result:expr
    $(; Ok($ok_pat:pat) => $ok_level:ident, $ok_fmt:literal $(, $ok_arg:expr )* $(,)? )?
    $(;)?
  } => { ... };
    { @first_nonempty { $($a:tt)+ } { $($b:tt)* }} => { ... };
    { @first_nonempty { } { $($b:tt)* } } => { ... };
}
Expand description

Log a rate-limited failure message.

This macro looks at a single Result<T,E>, and keeps track of how many times the Ok and Err branches are seen. After a delay, it reports via [tracing::event!] how many errors it has seen since its last report. (It only reports an absence of errors when such an absence has followed an error report.)

§A simple example

use tor_log_ratelim::log_ratelim;
 let r: Result<u8, ParseIntError> = s.parse();

log_ratelim!(
  // The activity we were doing.
  "Parsing a value from {}", source;
  // The result we got.
  r;
);

This invocation could report errors like

WARN: Parsing a value from the cache: error (occurred 9/12 times in the last 5 minutes): number too large to fit in target type

After a while without errors, it might log:

WARN: Parsing a value from the cache: now working (occurred 0/100 times in th last hour)

§Important concept: Activities

Every invocation of log_ratelim! defines a set of rate limits with respect to a collection of activities.
Each separate activity value gets its own rate limit. This lets you have separate rate limits for different operations, such as connecting to different parties, or invoking different programs.

Typical activities might be "Connecting to port {}", p or "Trying to start program {}", p

(These activities should be described using a verb ending with “-ing”, to make the output nice.)

§Requirements on the error type.

The error type for each Result passed to this macro must implement:

§Reports are representative

No matter how many failures are seen between log messages, log_ratelim! only records and reports one error for each time it logs.

Its current behavior is to record and report the first error for each logged interval, and discard the others.
This can lead to confusing results if the error is not representative.

§Advanced syntax

The log_ratelim macro can record additional information for its representative error report, and can log information on successes as well.

A full invocation of log_ratelim! looks like this:

use tor_log_ratelim::log_ratelim;
let r: Result<u8, ParseIntError> = s.parse();
log_ratelim!(
  "Parsing a value from {}", source;
  r;
  Err(x) => WARN, "The problem was {}", more_information(x);
  Ok(v) => TRACE, "Parsed {} successfully", v;
);

Here the clause starting with Err(x) tells the logger to include a message along with the error report, and we explicitly specifies the level at which to log our failure reports.

Note that the error itself is always reported; there is no need to say Err(e) => WARN, "{}", e. In fact, doing so will create a redundant report of the error. The clause starting with Ok(v) tells the logger what to do on success: each individual success causes a non-rate-limited message at TRACE level.

The Ok() ... clause and the Err() ... clause are both optional.

Within the Err() clause, the format string and its arguments are optional.