A lightweight, high-performance pipe-to-JSON logger written in C11. Capture stdin streams with extensible metadata injection, configurable buffering, and efficient multiplexing across macOS and Linux.
- Zero-Copy: Platform-specific multiplexing (kqueue on macOS, epoll on Linux) with lock-free queue patterns.
- Extensible Metadata: Inject custom JSON fields at capture time with type inference.
- Configurable Throughput: Tune chunk size, buffer capacity, and overflow policies for your workload.
- Memory-Safe: Error handling and address/thread sanitizer support.
- Production-Ready: Full CI/CD pipeline, high test coverage, and performance benchmarks.
- CLI-First: Simple, powerful command-line interface with help and version commands.
- CMake 3.20 or later
- C11 compiler (GCC 9+ or Clang 10+)
- POSIX-compliant OS (macOS 10.7+, Linux 2.6.32+)
- Threads support (pthread)
- GNU Make
git clone https://github.com/scokmen/jpipe.git
cd jpipe
# Debug build (with tests)
make debug
# Release build (optimized)
make release
# Release with debug symbols
make relwithdebinfoTBD.
cat data.log | jpipe run -o /tmp/outputReads stdin and writes JSON-encoded lines to /tmp/output/jpipe_<timestamp>_<pid>.json.
jpipe run [options]| Option | Short | Default | Description |
|---|---|---|---|
--chunk-size |
-c |
16kb | Chunk size (1kb–128kb). Larger chunks reduce syscalls; smaller chunks lower latency. |
--buffer-size |
-b |
64 | Queue capacity (1–1024). Number of pending chunks before applying overflow policy. |
--policy |
-p |
wait | Overflow policy: wait (block until queue drains) or drop (discard messages). |
--output |
-o |
. |
Output directory for JSON files. |
--field |
-f |
— | Custom JSON field (key=value). Repeatable; supports type inference. |
--dry-run |
-n |
— | Parse and validate inputs without writing. |
--help |
-h |
— | Show help message. |
jpipe [--quiet | -q] [--no-color | -C] <command> [options]| Option | Description |
|---|---|
help |
Show global help message. |
version |
Show version. |
-q, --quiet |
Suppress non-critical logs. |
-C, --no-color |
Disable colored output. |
NO_COLOR env var |
Respects the NO_COLOR standard. |
Use -f key=value to add custom fields to every JSON object:
cat logs.txt | jpipe run -f "app=myapp" -f "env=prod" -f "timeout=30" -f "active=true"Value Type Inference:
| Input | Type | JSON Output |
|---|---|---|
count=42 |
Number | "count": 42 |
rate=1.5e-3 |
Number (float) | "rate": 0.0015 |
enabled=true |
Boolean | "enabled": true |
text=hello |
String | "text": "hello" |
forced="123" |
String (forced) | "forced": "123" |
Key constraints:
- Must contain only:
a-z,A-Z,0-9,_,- - Maximum 64 characters
tail -f /var/log/syslog | jpipe run -c 64kb -b 128 -o /var/log/json_archiveheavy_stream | jpipe run -p drop -b 32 -o /tmp/logs./app | jpipe run \
-f "container=my_service" \
-f "version=1.2.3" \
-f "deployment=prod" \
-o /var/log/app_logs/jpipe run -n -f "bad key!=value" # Validates without writingEach output file contains newline-delimited JSON (NDJSON), one JSON object per line:
{"line":"2025-04-21T10:30:45 INFO Starting service","app":"myapp","env":"prod"}
{"line":"2025-04-21T10:30:46 INFO Worker ready","app":"myapp","env":"prod"}
{"line":"2025-04-21T10:30:47 ERROR Connection timeout","app":"myapp","env":"prod"}
Output filenames follow the pattern: jpipe_<start_timestamp>_<pid>.json
Memory usage is approximately:
estimated_mb = (chunk_size_kb * buffer_size) / 1024
Example: 16kb * 64 = ~1 MB queue overhead
Thread stacks and OS overhead are additional; actual usage will be higher. Check resource estimate on startup:
jpipe run -c 32kb -b 128 -o /tmp # Prints estimated memory on startupwait(default): Block the reader thread if the queue fills. Safe but may slow input processing.drop: Discard incoming messages when the queue is full. Preserves forward progress but loses data.
Choose based on your priorities: data completeness vs. availability.
jpipe uses a producer-consumer architecture:
- Reader Thread: Reads from stdin in configurable chunks. Multiplexed with kqueue (macOS) / epoll (Linux) for non-blocking I/O.
- Queue: Lock-free ring buffer with condition variables. Chunk-sized blocks prevent fragmentation.
- Writer Thread: Consumes blocks from the queue and writes JSON-encoded output files.
- Encoder: Encodes each line to JSON with custom fields injected. Type inference for field values.
Signal Handling: SIGINT and SIGTERM trigger graceful shutdown. Active chunks are flushed to disk before exit.
# Run tests
make test
# Run tests with Address Sanitizer (memory safety)
make test-with-asan
# Run tests with Thread Sanitizer (concurrency)
make test-with-tsan
# Generate code coverage report
make coverage
# Format code
make format
# Static analysis
make check-and-inform # Report all issues
make check-and-enforce # Report critical issues only# Prepare benchmarking data
make bench-prepare
# Run benchmarks
make bench-run- File Rotation: Not yet implemented. Use external tools (logrotate, systemd-rotate) or custom scripts for file management.
- SIGTERM on Empty Queue: Consumer may block briefly if the queue empties during shutdown.
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Write tests for new functionality
- Ensure all tests pass and code is sanitizer-clean
- Open a pull request with a clear description
MIT License — See LICENSE file for details.
Inspired by high-performance logging infrastructure in modern cloud-native systems.
- Issues: GitHub Issues
- Build Help: Run
make helpfor all available build targets