Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Logging

orion-error logging capabilities are built around OperationContext and OperationScope.

1. Feature

[dependencies]
orion-error = { version = "0.8.0", features = ["log"] }
# or
orion-error = { version = "0.8.0", features = ["tracing"] }

Default features include log.

Behavior:

  • log only: uses log macros
  • tracing enabled: prefers tracing
  • Both enabled: prefers tracing

2. Basic Usage

#![allow(unused)]
fn main() {
use orion_error::OperationContext;

let ctx = OperationContext::doing("order_processing")
    .with_field("order_id", "123")
    .with_field("amount", "100.0")
    .with_meta("component.name", "order_service");

ctx.info("start");
ctx.debug("payload prepared");
ctx.warn("slow upstream");
ctx.error("final failure");
ctx.trace("verbose trace");
}

Aliases: log_info, log_debug, log_warn, log_error, log_trace.

3. Automatic Result Logging

#![allow(unused)]
fn main() {
use orion_error::OperationContext;

let mut ctx = OperationContext::doing("sync_user")
    .with_auto_log()
    .with_field("user_id", "42");

do_sync()?;
ctx.mark_suc();
}

Default result is Fail. If with_auto_log() is enabled but neither mark_suc() nor mark_cancel() is called before drop, a failure log is emitted.

4. OperationScope

OperationScope is a guard for scoped lifecycle management.

#![allow(unused)]
fn main() {
use orion_error::OperationContext;

let mut ctx = OperationContext::doing("sync_user").with_auto_log();

{
    let mut scope = ctx.scope();
    scope.with_field("user_id", "42");
    validate()?;
    scope.mark_success();
}
}

Methods:

  • scope() — default failure; must call mark_success() explicitly
  • scoped_success() — default success; use mark_failure() or cancel() to override
  • mark_success() — mark as success
  • mark_failure() — revert to failure
  • cancel() — mark as cancelled

5. When to Use scoped_success()

scoped_success() is suitable when:

  • The scope already handles failure branches internally
  • Failure is explicitly handled via mark_failure()
  • The code does not use ? to return early

Example:

#![allow(unused)]
fn main() {
let mut ctx = OperationContext::doing("process_order").with_auto_log();

{
    let mut scope = ctx.scoped_success();
    let ok = validate_order();
    if !ok {
        scope.mark_failure();
    }
}
}

Not recommended:

let mut scope = ctx.scoped_success();
validate()?;

Because scoped_success() defaults to success on creation. If ? returns early, the scope is still marked as success on drop.

For fallible flows with early returns, prefer:

#![allow(unused)]
fn main() {
let mut scope = ctx.scope();
validate()?;
scope.mark_success();
}

6. op_context! Macro

#![allow(unused)]
fn main() {
use orion_error::op_context;

let ctx = op_context!("load_config").with_auto_log().with_field("path", "config.toml");
}

This macro expands module_path!() at the call site, adding more accurate module paths to automatic result logs.

7. Best Practices

  • Use doing(...) to name operations
  • Use with_field(...) / with_meta(...) for chained construction
  • Use record_field(...) / record_meta(...) only when a mutable reference already exists
  • Use with_auto_log() only on scopes that need result logging
  • For fallible logic with ?, prefer scope() + mark_success()
  • Use scoped_success() only when failure paths are explicitly handled