log_args
A procedural macro for structured logging of function arguments using the tracing
crate, with special support for async contexts.
This crate provides a procedural macro attribute #[params]
that can be applied to functions to automatically log their arguments. It seamlessly integrates with the tracing
ecosystem for structured logging, offering enhanced flexibility for both synchronous and asynchronous functions.
๐ Overview
- Macro name:
#[params]
- Purpose: Automatically logs function arguments and custom fields using the
tracing
macros (info!
,debug!
, etc.) - Works with: Both synchronous and asynchronous functions, with special support for async contexts
- Flexible logging: Control exactly what fields are logged, including nested struct fields
- Async-friendly: Special
clone_upfront
option for handling ownership in async blocks
โจ Features
- Log all function arguments by default
- Select specific arguments to log using
fields(...)
- Log nested fields of struct arguments (e.g.,
user.id
) - Add custom key-value pairs to the log output using
custom(...)
- Async support with special
clone_upfront
option for ownership in async contexts - Supports both synchronous and asynchronous functions
- All logging is done through the
tracing
macros (info!
,debug!
,warn!
,error!
,trace!
) - No
level
attribute: Use the desiredtracing
macro directly in your function body
๐ฆ Installation
Add log_args
to your Cargo.toml
:
[]
= "0.1" # Replace with the latest version from crates.io
= "0.1"
You'll also need a compatible tracing
subscriber to process and display logs. For example:
// Initialize a simple console subscriber
fmt.init;
๐ง Basic Usage
Log All Arguments
By default, #[params]
logs all arguments of a function.
use params;
use info;
// Log output will be similar to:
// INFO process_user: Processing task user=User { id: 42, name: "Alice" } task_id=100
Log Specific Fields
You can specify which arguments or fields to log using the fields(...)
attribute:
use params;
use info;
// Log output will be similar to:
// INFO process_user: Processing task user.id=42 user.name="Alice"
Add Custom Key-Value Pairs
Use custom(...)
to add static key-value pairs to your logs:
use params;
use info;
// Log output will be similar to:
// INFO process_user: Processing task user=User { id: 42, name: "Alice" } task_id=100 service_name="user-service" version="1.0"
Combine Multiple Options
You can combine fields
and custom
options:
use params;
use info;
// Log output will be similar to:
// INFO process_user: Processing task user.id=42 service_name="user-service"
๐ Advanced Usage: Async Functions
The #[params]
macro works seamlessly with async functions:
use params;
use info;
use sleep;
use Duration;
async
Using clone_upfront
for Async Contexts
When working with async code, especially when moving values into async move
blocks or tokio::spawn
, you might encounter ownership issues. The clone_upfront
option addresses this by ensuring fields can be safely used throughout your async function:
use params;
use info;
async
The clone_upfront
option is particularly useful when:
- You need to move values into
async move
blocks - You're using
tokio::spawn
or similar functions - You want to log values even after they've been moved
๐ ๏ธ Feature Reference
Attribute Options
Option | Description | Example |
---|---|---|
fields(...) |
Specify which fields to log | #[params(fields(user.id, count))] |
custom(...) |
Add custom key-value pairs | #[params(custom(version = "1.0"))] |
clone_upfront |
Clone fields for safe use in async contexts | #[params(clone_upfront)] |
Logging Options
The #[params]
macro redefines the following tracing
macros within the function body:
info!
warn!
error!
debug!
trace!
Use these macros as you normally would - the function arguments will be automatically included in the output.
๐ Examples
Check out the examples directory for more detailed usage patterns:
examples/basic.rs
: Basic usage with all argumentsexamples/selected_fields.rs
: Logging specific fieldsexamples/custom_fields.rs
: Adding custom key-value pairsexamples/async_function.rs
: Usage with async functionsexamples/async_clone_upfront.rs
: Usingclone_upfront
with async functionsexamples/subfields.rs
: Logging nested struct fields
๐ How It Works
The #[params]
macro:
- Analyzes the function signature to find available arguments
- Processes attribute options like
fields(...)
andcustom(...)
- Redefines tracing macros within the function scope to automatically include the specified fields
- With
clone_upfront
, ensures values are safely cloned to prevent ownership issues in async contexts
The macro does not add overhead beyond the normal cost of logging and cloning when needed.
โ ๏ธ Limitations
- The
#[params]
macro redefines tracing macros within function scope, which may generate unused macro warnings if not all redefined macros are used (these are suppressed internally) - When using
clone_upfront
, fields must implementClone
- Deeply nested or complex field expressions may not be properly captured
- Currently, directly using a struct in a field expression (e.g.,
fields(user)
instead offields(user.id)
) may not work as expected; use individual fields instead
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
๐งต Example: End-to-End
use log_args;
use tracing_subscriber;
Logs:
INFO login: user_id=42 user_name="Alice" service="auth" Login started
WARN login: user_id=42 user_name="Alice" service="auth" Invalid password
โ Incorrect usage:
Always import the macros you use:
use ;
๐ฎ Future Enhancements
#[log_args(span = true)]
: Optional span-based logging for subfunction support#[log_args(log_return)]
: Auto-log return values- Integration with
opentelemetry
and structured span hierarchy
โ License
MIT or Apache 2.0 โ your choice.
๐ Contributions
PRs, issues, and feedback are welcome. Letโs make logging in Rust ergonomic and powerful, together.
๐ซ Contact
Maintained by [MKJS Tech](mailto:mkjsm57@gmail.com) โข Feel free to reach out via mail or GitHub Issues.