| Crates.io | rsnx |
| lib.rs | rsnx |
| version | 0.1.0 |
| created_at | 2025-10-13 08:34:03.709924+00 |
| updated_at | 2025-10-13 08:34:03.709924+00 |
| description | A Rust library for parsing nginx access logs, inspired by gonx |
| homepage | |
| repository | https://github.com/kktsuiac_770/rsnx |
| max_upload_size | |
| id | 1880190 |
| size | 86,534 |
A Rust library for parsing nginx access logs, inspired by the Go library gonx.
Add this to your Cargo.toml:
[dependencies]
rsnx = "0.1.0"
use rsnx::Reader;
use std::io::Cursor;
let log_data = r#"127.0.0.1 [08/Nov/2013:13:39:18 +0000] "GET /api/foo HTTP/1.1" 200 612"#;
let format = r#"$remote_addr [$time_local] "$request" $status $body_bytes_sent"#;
let cursor = Cursor::new(log_data);
let reader = Reader::new(cursor, format)?;
for entry in reader {
let entry = entry?;
println!("IP: {}", entry.field("remote_addr")?);
println!("Status: {}", entry.int_field("status")?);
println!("Bytes: {}", entry.int_field("body_bytes_sent")?);
}
use rsnx::NginxReader;
use std::io::Cursor;
let nginx_config = r#"
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
"#;
let log_data = r#"127.0.0.1 - - [08/Nov/2013:13:39:18 +0000] "GET /api/foo HTTP/1.1" 200 612 "-" "curl/7.64.1" "-""#;
let config_cursor = Cursor::new(nginx_config);
let log_cursor = Cursor::new(log_data);
let reader = NginxReader::new(log_cursor, config_cursor, "main")?;
for entry in reader {
let entry = entry?;
println!("Request: {}", entry.field("request")?);
println!("User Agent: {}", entry.field("http_user_agent")?);
}
The library supports any nginx log format that uses $variable syntax. Common formats include:
$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent
$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"
$remote_addr [$time_local] "$request" $status $request_time "$http_user_agent"
The Entry struct represents a parsed log line with methods for type-safe field access:
// String access
let ip = entry.field("remote_addr")?;
// Integer access
let status = entry.int_field("status")?; // i32
let bytes = entry.int64_field("body_bytes_sent")?; // i64
// Float access
let request_time = entry.float_field("request_time")?; // f64
// Field manipulation
entry.set_field("custom_field", "value");
entry.set_uint_field("count", 42u64);
entry.set_float_field("ratio", 3.14);
// Utility methods
let partial = entry.partial(&["remote_addr", "status"]);
let hash = entry.fields_hash(&["remote_addr", "request"]);
entry.merge(&other_entry);
The Reader struct provides an iterator interface for processing log files:
// Create reader
let reader = Reader::new(input, format)?;
// Iterator interface
for entry in reader {
let entry = entry?;
// process entry
}
// Collect all entries
let entries = reader.collect_all()?;
// Process with closure
reader.process_entries(|entry| {
println!("{}", entry.field("remote_addr")?);
Ok(())
})?;
The NginxReader extracts log formats from nginx configuration files:
let reader = NginxReader::new(log_input, nginx_config, "format_name")?;
The library provides comprehensive error handling with detailed error messages:
use rsnx::Error;
match entry.field("nonexistent") {
Ok(value) => println!("Value: {}", value),
Err(Error::FieldNotFound { field }) => {
println!("Field '{}' not found", field);
}
Err(e) => println!("Other error: {}", e),
}
Error types include:
FieldNotFound: When a requested field doesn't existFieldParseError: When type conversion failsLineFormatMismatch: When a log line doesn't match the expected formatInvalidFormat: When a format string is invalidNginxFormatNotFound: When a log format isn't found in nginx configIo: For I/O related errorsThe library is designed for efficient log processing:
See the examples directory for more comprehensive usage examples:
basic.rs: Basic usage patterns and error handlingRun examples with:
cargo run --example basic
Run the test suite:
# Run all tests
cargo test
# Run with output
cargo test -- --nocapture
# Run integration tests only
cargo test --test integration_tests
serde: Enable serialization/deserialization support for Entry[dependencies]
rsnx = { version = "0.1.0", features = ["serde"] }
This library aims to provide similar functionality to the Go library gonx while following Rust idioms:
| Feature | gonx (Go) | rsnx (Rust) |
|---|---|---|
| Format parsing | ✅ | ✅ |
| Nginx config parsing | ✅ | ✅ |
| Type-safe field access | ✅ | ✅ |
| Iterator interface | ✅ | ✅ |
| Error handling | (value, error) |
Result<T, Error> |
| Memory management | GC | Ownership |
| Concurrency | Goroutines | (Future: async/await) |
This project is licensed under the MIT License - see the LICENSE file for details.
# Clone the repository
git clone https://github.com/kktsuiac770/rsnx.git
cd rsnx
# Run tests to verify everything works
cargo test
# Run the example to see it in action
cargo run --example basic
# Build the library
cargo build --release
Add to your Cargo.toml:
[dependencies]
rsnx = { git = "https://github.com/kktsuiac770/rsnx.git", tag = "v0.1.0" }
v0.1.0 - Initial stable release with full functionalitymain - Latest development version (may include unreleased features)Create a new Rust project and use rsnx:
# Create new project
cargo new my-log-parser
cd my-log-parser
# Add rsnx to Cargo.toml
echo 'rsnx = { git = "https://github.com/kktsuiac770/rsnx.git", tag = "v0.1.0" }' >> Cargo.toml
Then use it in your src/main.rs:
use rsnx::Reader;
use std::fs::File;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let file = File::open("access.log")?;
let format = r#"$remote_addr [$time_local] "$request" $status $body_bytes_sent"#;
let reader = Reader::new(file, format)?;
for entry in reader {
let entry = entry?;
println!("IP: {} - Status: {}",
entry.field("remote_addr")?,
entry.int_field("status")?);
}
Ok(())
}
Contributions are welcome! Please feel free to submit a Pull Request.