Skip to content

Macho0x/tree-logger

Repository files navigation

Screenshot

Tree Logger

A hierarchical, human-readable logging system for V that turns scattered log lines into visual trees making it easier for both humans and LLMs to debug code with richer context.

Why Tree Logger?

The Problem: Traditional structured loggers (like JSON logging) dump everything in dense, hard-to-scan blobs:

{"timestamp":"2026-03-06T12:34:56","level":"INFO","service":"api","method":"GET","path":"/api/users","status":200,"user":{"id":123,"name":"John","email":"john@example.com"},"duration_ms":45}

The Solution: Tree Logger renders structured data as visual trees with field prioritization — important data expanded, noise collapsed:

[2026-03-06 12:34:56] INFO [api] GET /api/users 200 in 45ms
├─ user: John (id: 123)
│   ├─ id: 123
│   └─ name: John
└─ [3 fields hidden, use --verbose]

Inspired By

  • evlog - The philosophy of wide events and contextual logging
  • logging sucks - Why traditional logging makes debugging painful

Key Features

  • Tree Format - Logs render as visual trees instead of dense JSON
  • Field Prioritization - Headers/important fields expanded, boring/internal fields collapsed
  • Three Display Modes - compact, normal, verbose for different contexts
  • Correlation Tracking - Built-in UUID-based request tracing across distributed systems
  • Data Sanitization - Automatic masking of API keys, passwords, and PII

Installation

v install github.com/Macho0x/tree-logger/

Or copy all .v files into your project.

Quick Start

import logger
import x.json2

fn main() {
    // Initialize
    logger.init_logger(logger.LoggerConfig{
        env: logger.EnvironmentContext{service: 'my-app'}
        mode: .normal
    })

    // Simple logging
    logger.info('startup', 'Server started')

    // Structured logging
    logger.log.info_data({
        'method': json2.Any('GET')
        'path':   json2.Any('/api/users')
        'status': json2.Any(200)
    })
}

API Reference

Initialization

// Initialize with configuration
logger.init_logger(logger.LoggerConfig{
    env: logger.EnvironmentContext{service: 'my-app'}
    mode: .normal  // .compact, .normal, .verbose
})

// Or load from TOML
logger.init_from_toml('setup.toml')!

// Runtime changes
logger.set_log_mode(.verbose)
logger.set_log_level(.debug)

Logging

// Simple logging with automatic file:line
logger.info('tag', 'message')
logger.warn('tag', 'message')
logger.log_error('tag', 'message')
logger.log_debug('tag', 'message')

// Structured logging
logger.log.info_data({'key': json2.Any('value')})

// Scoped logging (build events incrementally)
mut req_log := logger.new_request_logger({
    'method': json2.Any('USER_SIGNUP')
})
req_log.set({'email': json2.Any('user@example.com')})
req_log.info('Sending welcome email', none)
req_log.emit(none)  // Final output

Examples: Before vs After

Example 1: API Request

Before (JSON):

{"timestamp":"2026-03-06T12:34:56","level":"INFO","service":"api","method":"GET","path":"/api/users/123","status":200,"user":{"id":123,"name":"Alice Johnson","email":"alice@example.com","role":"admin"},"timing":{"total_ms":245,"db_ms":45,"cache_hit":false},"request_id":"req_abc123"}

After (Tree Logger - Normal Mode):

[2026-03-06 12:34:56] INFO [api] GET /api/users/123 200 in 245ms
├─ user: Alice Johnson (admin)
│   ├─ id: 123
│   ├─ name: Alice Johnson
│   └─ role: admin
├─ timing: {3 fields}
└─ request_id: req_abc123

Compact Mode:

[2026-03-06 12:34:56] INFO [api] GET /api/users/123 200 in 245ms
└─ user: Alice Johnson (admin)

Verbose Mode:

[2026-03-06 12:34:56] INFO [api] GET /api/users/123 200 in 245ms
├─ method: GET
├─ path: /api/users/123
├─ status: 200
├─ user: Alice Johnson (admin)
│   ├─ id: 123
│   ├─ name: Alice Johnson
│   ├─ email: alice@example.com
│   └─ role: admin
├─ timing: {3 fields}
│   ├─ total_ms: 245
│   ├─ db_ms: 45
│   └─ cache_hit: false
├─ request_id: req_abc123
└─ duration_ms: 245

Example 2: Database Error with Context

Before (Scattered Lines):

2026-03-06 12:34:56 ERROR Connection timeout after 30s
2026-03-06 12:34:56 INFO Table: user_sessions
2026-03-06 12:34:56 INFO Attempt: 3 of 5
2026-03-06 12:34:56 INFO Query: SELECT * FROM user_sessions WHERE user_id = 456

After (Tree Logger):

[2026-03-06 12:34:56] ERROR [api] DB_QUERY_FAILED
├─ operation: cleanup_expired_sessions
├─ query: {1 fields}
│   └─ table: user_sessions
├─ error: Connection timeout after 30s
│   ├─ table: user_sessions
│   └─ attempt: 3 of 5
└─ duration: 30.15s

Example 3: User Checkout with Sensitive Data

Before (Raw JSON with Secrets):

{"timestamp":"2026-03-06T12:34:56","level":"INFO","method":"CHECKOUT_COMPLETE","api_key":"AK1234567890ABCDEFGH","password":"super_secret_pass","user":{"id":789,"email":"customer@example.com"},"payment":{"method":"credit_card","last4":"4242","amount":99.99},"items":[{"product":"Wireless Headphones","price":79.99},{"product":"USB Cable","price":19.99}],"total":99.99}

After (Tree Logger with Sanitization):

[2026-03-06 12:34:56] INFO [shop] CHECKOUT_COMPLETE
├─ api_key: AK1********FGH
├─ password: ********
├─ user: customer@example.com
│   ├─ id: 789
│   └─ email: customer@example.com
├─ payment: {2 fields}
│   ├─ method: credit_card
│   └─ last4: ****4242
├─ items: [2 items]
│   ├─ [0] Wireless Headphones - $79.99
│   └─ [1] USB Cable - $19.99
└─ total: $99.99

Notice: API keys, passwords, and credit card numbers are automatically masked.

Feature Examples

Feature Input Output
Color Coding Log level INFO [2026-03-06 12:34:56] INFO api Request completed
Sanitizer password: "secret123" password: ********
Sanitizer api_key: "AK1234567890" api_key: AK1********90
Sanitizer email: "user@example.com" email: use********com
Field Priority Important field user: Alice (expanded)
Field Priority Boring field meta: {5 fields hidden}
Display Modes compact mode One-line summary only
Display Modes normal mode Important fields + collapsed extras
Display Modes verbose mode All fields fully expanded
Correlation ID New request correlation_id: abc-123-xyz
Scoped Logging Add context Build up event incrementally

⚠️ Performance Disclaimer

This logger prioritizes readability and developer experience over raw performance. Open to PRs to improve raw performance.

Configuration

Create setup.toml:

[logger]
enabled = true
level = "info"
mode = "normal"

[logger.environment]
service = "my-app"
environment = "development"

[logger.sanitizer]
enabled = true
api_key_strategy = "partial"
password_strategy = "full"

Load it:

logger.init_from_toml('setup.toml')!

License

MIT License

About

A hierarchical, human-readable logging system for V with field prioritization, correlation tracking, and data sanitization.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages