#logging #tracing #proc-macro #log-level #calls #parameters #procedural

macro funlog

A procedural macro for tracing Rust function calls

3 releases

0.1.2 Nov 29, 2024
0.1.1 Nov 28, 2024
0.1.0 Nov 28, 2024

#354 in Debugging

Download history 302/week @ 2024-11-25 22/week @ 2024-12-02

324 downloads per month

MIT license

15KB
200 lines

funlog

中文文档

A Rust attribute macro for function logging with configurable options.

Features

  • Log function entry and exit with file and line information
  • Parameter value logging with type information
  • Return value logging with type information
  • Support for Result and Option types
  • Generic type support with Debug trait
  • Configurable log levels (debug, info, warn, error, trace)
  • Output length limits for parameters and return values
  • Support for nested function calls
  • Module path information in logs

Installation

Add this to your Cargo.toml:

[dependencies]
funlog = "0.1.0"
log = "0.4"

And initialize a logger in your main function:

fn main() {
    env_logger::init();
    // Your code here
}

Usage Examples

Basic Function Logging

use funlog::funlog;

#[funlog(param, ret)]
fn add(a: i32, b: i32) -> i32 {
    a + b
}

fn main() {
    let result = add(5, 3);
}

Output:

[2024-11-29T13:31:35Z INFO  my_app] src/main.rs:4 my_app::add(a: 5, b: 3) begin
[2024-11-29T13:31:35Z INFO  my_app] src/main.rs:4 my_app::add(a: 5, b: 3)->8 end

Result Type Handling

#[derive(Debug)]
struct Person {
    name: String,
    age: u32,
}

impl Person {
    #[funlog(param, ret)]
    fn new(name: String, age: u32) -> Result<Person, String> {
        if age > 150 {
            Err("Age is too high".to_string())
        } else {
            Ok(Person { name, age })
        }
    }
}

fn main() {
    let person = Person::new("Alice".to_string(), 25);
}

Output:

[2024-11-29T13:31:35Z INFO  my_app] src/main.rs:16 my_app::new(name: "Alice", age: 25) begin
[2024-11-29T13:31:35Z INFO  my_app] src/main.rs:16 my_app::new(name: "Alice", age: 25)->Ok(Person { name: "Alice", age: 25 }) end

Option Type Handling

#[derive(Debug)]
struct Book {
    title: String,
}

impl Book {
    #[funlog(param, ret)]
    fn new(title: String) -> Option<Book> {
        if title.is_empty() {
            None
        } else {
            Some(Book { title })
        }
    }
}

fn main() {
    let book = Book::new("The Great Gatsby".to_string());
}

Output:

[2024-11-29T13:31:35Z INFO  my_app] src/main.rs:32 my_app::new(title: "The Great Gatsby") begin
[2024-11-29T13:31:35Z INFO  my_app] src/main.rs:32 my_app::new(title: "The Great Gatsby")->Some(Book { title: "The Great Gatsby" }) end

Generic Functions

#[funlog(param, ret)]
fn process_data<T: std::fmt::Debug>(data: Vec<T>) -> Result<Option<Vec<T>>, String> {
    if data.is_empty() {
        Ok(None)
    } else {
        Ok(Some(data))
    }
}

fn main() {
    let numbers = vec![1, 2, 3, 4, 5];
    let processed = process_data(numbers);
}

Output:

[2024-11-29T13:31:35Z INFO  my_app] src/main.rs:42 my_app::process_data(data: [1, 2, 3, 4, 5]) begin
[2024-11-29T13:31:35Z INFO  my_app] src/main.rs:42 my_app::process_data(data: [1, 2, 3, 4, 5])->Ok(Some([1, 2, 3, 4, 5])) end

Complex Type Handling

#[funlog(param, ret)]
fn handle_result(result: Result<Option<String>, i32>) -> Option<Result<String, i32>> {
    match result {
        Ok(Some(s)) => Some(Ok(s)),
        Ok(None) => None,
        Err(e) => Some(Err(e)),
    }
}

fn main() {
    let test_result = Ok(Some("Hello".to_string()));
    let handled = handle_result(test_result);
}

Output:

[2024-11-29T13:31:35Z INFO  my_app] src/main.rs:51 my_app::handle_result(result: Ok(Some("Hello"))) begin
[2024-11-29T13:31:35Z INFO  my_app] src/main.rs:51 my_app::handle_result(result: Ok(Some("Hello")))->Some(Ok("Hello")) end

Log Level Configuration

#[funlog(param, ret, debug)]
fn debug_function(x: i32) -> i32 {
    x * 2
}

#[funlog(param, ret, warn)]
fn warn_function(x: i32) -> i32 {
    x * 3
}

Output:

[2024-11-29T13:31:35Z DEBUG my_app] src/main.rs:60 my_app::debug_function(x: 10) begin
[2024-11-29T13:31:35Z DEBUG my_app] src/main.rs:60 my_app::debug_function(x: 10)->20 end
[2024-11-29T13:31:35Z WARN  my_app] src/main.rs:65 my_app::warn_function(x: 10) begin
[2024-11-29T13:31:35Z WARN  my_app] src/main.rs:65 my_app::warn_function(x: 10)->30 end

Notes

  1. For generic types and custom structs, the Debug trait must be implemented:
#[derive(Debug)]
struct MyStruct { /* ... */ }
  1. The macro automatically handles:

    • Result types (Ok/Err)
    • Option types (Some/None)
    • Generic types (with Debug trait)
    • Custom structs (with Debug trait)
  2. Log format includes:

    • Timestamp
    • Log level
    • Module path
    • File name and line number
    • Function name
    • Parameter names and values
    • Return value
  3. Available log levels:

    • trace
    • debug
    • info (default)
    • warn
    • error

License

This project is licensed under the MIT License - see the LICENSE file for details.

Dependencies

~0.3–0.8MB
~18K SLoC