https://github.com/ditzdev/cappie
A fast JSON logger for Rust.
https://github.com/ditzdev/cappie
Last synced: 7 months ago
JSON representation
A fast JSON logger for Rust.
- Host: GitHub
- URL: https://github.com/ditzdev/cappie
- Owner: DitzDev
- License: mit
- Created: 2025-06-21T22:34:28.000Z (9 months ago)
- Default Branch: main
- Last Pushed: 2025-06-21T23:17:40.000Z (9 months ago)
- Last Synced: 2025-06-21T23:33:26.491Z (9 months ago)
- Language: Rust
- Size: 0 Bytes
- Stars: 3
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Cappie
[![Latest Version]][crates.io]
[Latest Version]: https://img.shields.io/crates/v/cappie.svg
[crates.io]: https://crates.io/crates/cappie
A fast, flexible JSON logger for Rust, inspired by Pino from Node.js.
## Features
- 🚀 **Fast & Simple** - High-performance logger with minimal overhead
- 📝 **JSON Logging** - Structured JSON output by default
- 🎨 **Pretty Printing** - Human-readable colorized output
- 🔧 **Highly Configurable** - Customize colors, formats, and outputs
- ✨ **Flexible Formatting** - Complete control over log output format and positioning
- 📊 **Structured Logging** - Add custom fields to log entries
- 👶 **Child Loggers** - Create contextual child loggers
- 📁 **Multiple Outputs** - Log to console, files, or multiple destinations
- 🌈 **Custom Colors** - Full control over ANSI color codes for any component
## Installation
Add to your `Cargo.toml`:
```toml
[dependencies]
cappie = "0.1.0"
```
Or install with `cargo` for new version:
```bash
cargo add cappie
```
## Quick Start
### JSON Logging (Default)
```rust
use cappie::{Logger, Level};
fn main() {
let logger = Logger::new("my-app")
.with_level(Level::Debug);
logger.info("Application started");
logger.error("Something went wrong");
// With additional fields
logger.info_with("User logged in", |log| {
log.string("user_id", "12345")
.string("ip", "192.168.1.1")
.bool("first_time", true);
});
}
```
**Output:**
```json
{"level":30,"time":"2024-01-15T10:30:45.123Z","name":"my-app","msg":"Application started"}
{"level":50,"time":"2024-01-15T10:30:45.124Z","name":"my-app","msg":"Something went wrong"}
{"level":30,"time":"2024-01-15T10:30:45.125Z","name":"my-app","msg":"User logged in","user_id":"12345","ip":"192.168.1.1","first_time":true}
```
### Pretty Logging
```rust
use cappie::{Logger, PrettyFormatter};
fn main() {
let logger = Logger::new("my-app")
.with_formatter(Box::new(PrettyFormatter::new()));
logger.info("Server started");
logger.warn("Memory usage high");
logger.error("Database connection failed");
}
```
**Output:**
```
[10:30:45] (my-app) INFO: Server started
[10:30:45] (my-app) WARN: Memory usage high
[10:30:45] (my-app) ERROR: Database connection failed
```
## 🌟 Flexible Formatting (NEW!)
The `FlexibleFormatter` gives you complete control over log output format. You can position any component anywhere, add custom colors, prefixes, suffixes, and even custom text.
### Basic Flexible Usage
```rust
use cappie::{Logger, FlexibleFormatter, ComponentType, ComponentPosition};
// Default format: [HH:mm:SS] (logger) LEVEL: message fields
let logger = Logger::new("app")
.with_formatter(Box::new(FlexibleFormatter::new()));
```
### Custom Positioning Examples
#### 1. Message First Format
```rust
// Output: message LEVEL [HH:mm:SS]: fields
let logger = Logger::new("api")
.with_formatter(Box::new(
FlexibleFormatter::new()
.clear_components()
.add_message(ComponentPosition::Start, None, None, None)
.add_level(ComponentPosition::AfterMessage, Some("\x1b[31m".to_string()), Some(" ".to_string()), None)
.add_timestamp(ComponentPosition::AfterLevel, None, Some(" [".to_string()), Some("]".to_string()))
.add_custom_text(": ", ComponentPosition::AfterTime, None)
.add_fields(ComponentPosition::End, None, None, None)
));
logger.error("Database connection failed");
// Output: Database connection failed ERROR [10:30:45]:
```
#### 2. Minimal Format
```rust
// Output: [LEVEL] message
let logger = Logger::new("minimal")
.with_formatter(Box::new(
FlexibleFormatter::new()
.clear_components()
.add_level(ComponentPosition::Start, Some("\x1b[33m".to_string()), Some("[".to_string()), Some("]".to_string()))
.add_message(ComponentPosition::AfterLevel, None, Some(" ".to_string()), None)
));
logger.warn("This is a warning");
// Output: [WARN] This is a warning
```
#### 3. Time at the End
```rust
// Output: [logger] LEVEL: message | fields (at HH:mm:SS)
let logger = Logger::new("backend")
.with_formatter(Box::new(
FlexibleFormatter::new()
.clear_components()
.add_logger_name(ComponentPosition::Start, Some("\x1b[36m".to_string()), Some("[".to_string()), Some("]".to_string()))
.add_level(ComponentPosition::AfterName, Some("\x1b[32m".to_string()), Some(" ".to_string()), None)
.add_custom_text(": ", ComponentPosition::AfterLevel, None)
.add_message(ComponentPosition::AfterLevel, None, None, None)
.add_fields(ComponentPosition::AfterMessage, None, Some(" | ".to_string()), None)
.add_timestamp(ComponentPosition::End, Some("\x1b[90m".to_string()), Some(" (at ".to_string()), Some(")".to_string()))
));
logger.info_with("Server started", |log| { log.number("port", 8080); });
// Output: [backend] INFO: Server started | port=8080 (at 10:30:45)
```
#### 4. Highly Colorful Format
```rust
// Output: 🚀 [HH:mm:SS] {logger} → message 📊 fields
let logger = Logger::new("colorful")
.with_formatter(Box::new(
FlexibleFormatter::new()
.clear_components()
.add_custom_text("🚀 ", ComponentPosition::Start, Some("\x1b[95m".to_string()))
.add_timestamp(ComponentPosition::Start, Some("\x1b[94m".to_string()), Some("[".to_string()), Some("]".to_string()))
.add_logger_name(ComponentPosition::AfterTime, Some("\x1b[96m".to_string()), Some(" {".to_string()), Some("}".to_string()))
.add_level(ComponentPosition::AfterName, Some("\x1b[93m".to_string()), Some(" <".to_string()), Some(">".to_string()))
.add_custom_text(" → ", ComponentPosition::AfterLevel, Some("\x1b[97m".to_string()))
.add_message(ComponentPosition::AfterLevel, Some("\x1b[92m".to_string()), None, None)
.add_fields(ComponentPosition::End, Some("\x1b[91m".to_string()), Some(" 📊 ".to_string()), None)
));
logger.info_with("App started", |log| { log.string("version", "1.0.0"); });
// Output: 🚀 [10:30:45] {colorful} → App started 📊 version=1.0.0
```
### Component Types
The `FlexibleFormatter` supports these component types:
- **`ComponentType::Timestamp`** - The log timestamp
- **`ComponentType::LoggerName`** - The logger name
- **`ComponentType::Level`** - The log level (INFO, ERROR, etc.)
- **`ComponentType::Message`** - The log message
- **`ComponentType::Fields`** - Additional structured fields
- **`ComponentType::CustomText(String)`** - Custom static text
### Component Positions
You can position components at these locations:
- **`ComponentPosition::Start`** - At the very beginning
- **`ComponentPosition::AfterTime`** - After the timestamp
- **`ComponentPosition::AfterName`** - After the logger name
- **`ComponentPosition::AfterLevel`** - After the log level
- **`ComponentPosition::AfterMessage`** - After the message
- **`ComponentPosition::End`** - At the very end
### FlexibleFormatter Methods
```rust
FlexibleFormatter::new()
// Clear all default components
.clear_components()
// Add components with full control
.add_component(ComponentType::Timestamp, ComponentPosition::Start, color, prefix, suffix)
// Convenience methods for common components
.add_timestamp(position, color, prefix, suffix)
.add_logger_name(position, color, prefix, suffix)
.add_level(position, color, prefix, suffix)
.add_message(position, color, prefix, suffix)
.add_fields(position, color, prefix, suffix)
.add_custom_text("text", position, color)
// Configuration
.with_time_format("%H:%M:%S")
.with_no_colors()
```
## Custom Colors & Traditional Pretty Format
```rust
use cappie::{Logger, PrettyFormatter, Level};
fn main() {
let logger = Logger::new("my-app")
.with_formatter(Box::new(
PrettyFormatter::new()
.with_color(Level::Error, "\x1b[91m") // Bright red
.with_color(Level::Warn, "\x1b[93m") // Bright yellow
.with_time_format("%Y-%m-%d %H:%M:%S")
));
logger.warn("Custom colored warning");
logger.error("Custom colored error");
}
```
## Log Levels
| Level | Value | Description |
|-------|-------|-------------|
| `TRACE` | 10 | Very detailed information |
| `DEBUG` | 20 | Debug information |
| `INFO` | 30 | General information |
| `WARN` | 40 | Warning messages |
| `ERROR` | 50 | Error messages |
| `FATAL` | 60 | Fatal errors |
## Advanced Usage
### Child Loggers
Create contextual loggers that inherit parent configuration:
```rust
let parent = Logger::new("app");
let auth_logger = parent.child("auth");
let db_logger = parent.child("database");
auth_logger.info("User authenticated"); // Outputs: app.auth
db_logger.error("Connection timeout"); // Outputs: app.database
```
### Multiple Outputs
Log to console and file simultaneously:
```rust
use cappie::{Logger, FileOutput, MultiOutput, StdoutOutput};
let logger = Logger::new("my-app")
.with_output(Box::new(
MultiOutput::new()
.add_output(Box::new(StdoutOutput))
.add_output(Box::new(FileOutput::new("app.log")))
));
logger.info("This goes to both console and file");
```
### Base Fields
Add fields that appear in every log entry:
```rust
let logger = Logger::new("my-app")
.with_field("version", "1.0.0")
.with_field("env", "production")
.with_field("service", "api-server");
logger.info("App started"); // Includes version, env, and service fields
```
### Structured Logging
Add contextual information to specific log entries:
```rust
logger.error_with("Database query failed", |log| {
log.string("query", "SELECT * FROM users")
.number("duration_ms", 1500)
.string("table", "users")
.bool("timeout", true);
});
```
## ANSI Color Codes
Cappie supports full ANSI color customization:
```rust
use cappie::{Logger, FlexibleFormatter, ComponentPosition, ComponentType};
let logger = Logger::new("colorful-app")
.with_formatter(Box::new(
FlexibleFormatter::new()
.clear_components()
.add_timestamp(ComponentPosition::Start, Some("\x1b[32m".to_string()), Some("[".to_string()), Some("]".to_string())) // Green
.add_level(ComponentPosition::AfterTime, Some("\x1b[33m".to_string()), Some(" ".to_string()), None) // Yellow
.add_message(ComponentPosition::AfterLevel, Some("\x1b[31m".to_string()), Some(": ".to_string()), None) // Red
));
```
### Common ANSI Colors
| Color | Code | Color | Code |
|-------|------|-------|------|
| Black | `\x1b[30m` | Bright Black | `\x1b[90m` |
| Red | `\x1b[31m` | Bright Red | `\x1b[91m` |
| Green | `\x1b[32m` | Bright Green | `\x1b[92m` |
| Yellow | `\x1b[33m` | Bright Yellow | `\x1b[93m` |
| Blue | `\x1b[34m` | Bright Blue | `\x1b[94m` |
| Magenta | `\x1b[35m` | Bright Magenta | `\x1b[95m` |
| Cyan | `\x1b[36m` | Bright Cyan | `\x1b[96m` |
| White | `\x1b[37m` | Bright White | `\x1b[97m` |
## Examples
Run the included examples:
```bash
# Basic JSON and pretty logging
cargo run --example basic
# Pretty logging with colors and fields
cargo run --example pretty
# Custom colors, outputs, and configurations
cargo run --example custom
# Flexible formatter examples with various layouts
cargo run --example flexible
```
## API Reference
### Logger Methods
```rust
// Create a new logger
Logger::new("app-name")
// Configuration
.with_level(Level::Debug)
.with_formatter(Box::new(FlexibleFormatter::new()))
.with_output(Box::new(StdoutOutput))
.with_field("key", "value")
// Logging methods
.trace("message")
.debug("message")
.info("message")
.warn("message")
.error("message")
.fatal("message")
// Logging with fields
.info_with("message", |log| {
log.string("key", "value")
.number("count", 42)
.bool("success", true);
})
// Create child logger
.child("module-name")
```
### FlexibleFormatter Methods
```rust
FlexibleFormatter::new()
.with_time_format("%H:%M:%S")
.clear_components()
// Add specific components
.add_timestamp(position, color, prefix, suffix)
.add_logger_name(position, color, prefix, suffix)
.add_level(position, color, prefix, suffix)
.add_message(position, color, prefix, suffix)
.add_fields(position, color, prefix, suffix)
.add_custom_text("text", position, color)
// Generic component addition
.add_component(component_type, position, color, prefix, suffix)
.with_no_colors()
```
### PrettyFormatter Methods
```rust
PrettyFormatter::new()
.with_time_format("%H:%M:%S")
.with_color(Level::Error, "\x1b[91m")
.with_no_colors()
```
## Formatter Comparison
| Feature | JsonFormatter | PrettyFormatter | FlexibleFormatter |
|---------|---------------|-----------------|-------------------|
| JSON Output | ✅ | ❌ | ❌ |
| Human Readable | ❌ | ✅ | ✅ |
| Custom Colors | ❌ | ✅ | ✅ |
| Custom Positioning | ❌ | ❌ | ✅ |
| Custom Text | ❌ | ❌ | ✅ |
| Prefixes/Suffixes | ❌ | ❌ | ✅ |
| Performance | Fastest | Fast | Fast |
| Use Case | Machine parsing | Development | Custom layouts |
## Performance
Cappie is designed for high performance:
- **Minimal allocations** - Efficient memory usage
- **Fast JSON serialization** - Uses serde_json for speed
- **Lazy evaluation** - Fields only processed when logging level is enabled
- **Zero-cost abstractions** - No runtime overhead for disabled log levels
- **Flexible without overhead** - FlexibleFormatter adds minimal cost
## Real-World Usage Examples
### Web Server Logging
```rust
// API request logging with flexible format
let api_logger = Logger::new("api")
.with_formatter(Box::new(
FlexibleFormatter::new()
.clear_components()
.add_custom_text("🌐 ", ComponentPosition::Start, Some("\x1b[94m".to_string()))
.add_timestamp(ComponentPosition::Start, Some("\x1b[90m".to_string()), None, None)
.add_level(ComponentPosition::AfterTime, Some("\x1b[32m".to_string()), Some(" [".to_string()), Some("]".to_string()))
.add_message(ComponentPosition::AfterLevel, None, Some(" ".to_string()), None)
.add_fields(ComponentPosition::End, Some("\x1b[36m".to_string()), Some(" → ".to_string()), None)
));
api_logger.info_with("Request processed", |log| {
log.string("method", "GET")
.string("path", "/api/users")
.number("status", 200)
.number("duration_ms", 45);
});
```
### Database Operations
```rust
// Database logging with custom format
let db_logger = Logger::new("database")
.with_formatter(Box::new(
FlexibleFormatter::new()
.clear_components()
.add_custom_text("💾 DB ", ComponentPosition::Start, Some("\x1b[95m".to_string()))
.add_level(ComponentPosition::Start, Some("\x1b[91m".to_string()), Some("[".to_string()), Some("]".to_string()))
.add_message(ComponentPosition::AfterLevel, None, Some(" ".to_string()), None)
.add_timestamp(ComponentPosition::End, Some("\x1b[90m".to_string()), Some(" @".to_string()), None)
.add_fields(ComponentPosition::AfterMessage, Some("\x1b[33m".to_string()), Some(" {".to_string()), Some("}".to_string()))
));
```
## Comparison with Pino
| Feature | Cappie | Pino |
|---------|---------|------|
| JSON Logging | ✅ | ✅ |
| Pretty Printing | ✅ | ✅ |
| Flexible Formatting | ✅ | ❌ |
| Child Loggers | ✅ | ✅ |
| Custom Formatters | ✅ | ✅ |
| Multiple Outputs | ✅ | Limited |
| Custom Colors | ✅ | Limited |
| Custom Positioning | ✅ | ❌ |
| Performance | Very Fast | Fast |
| Language | Rust | JavaScript |
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
This project is licensed under the MIT License - see the LICENSE file for details.
## Acknowledgments
- Inspired by [Pino](https://github.com/pinojs/pino) - The fast JSON logger for Node.js
- Built with [serde](https://serde.rs/) for serialization
- Uses [chrono](https://github.com/chronotope/chrono) for timestamp handling