# ll - A Modern Structured Logging Library for Go

`ll` is a high-performance, production-ready logging library for Go, designed to provide **hierarchical namespaces**, **structured logging**, **middleware pipelines**, **conditional logging**, and support for multiple output formats, including text, JSON, colorized logs, and compatibility with Go’s `slog`. It’s ideal for applications requiring fine-grained log control, extensibility, and scalability.

## Key Features

- **Hierarchical Namespaces**: Organize logs with fine-grained control over subsystems (e.g., "app/db").
- **Structured Logging**: Add key-value metadata for machine-readable logs.
- **Middleware Pipeline**: Customize log processing with error-based rejection.
- **Conditional Logging**: Optimize performance by skipping unnecessary log operations.
- **Multiple Output Formats**: Support for text, JSON, colorized logs, and `slog` integration.
- **Debugging Utilities**: Inspect variables (`Dbg`), binary data (`Dump`), and stack traces (`Stack`).
- **Thread-Safe**: Built for concurrent use with mutex-protected state.
- **Performance Optimized**: Minimal allocations and efficient namespace caching.

## Installation

Install `ll` using Go modules:

```bash
go get github.com/olekukonko/ll
```

Ensure you have Go 1.21 or later for optimal compatibility.

## Getting Started

Here’s a quick example to start logging with `ll`:


```go
package main

import (
  "github.com/olekukonko/ll"
)

func main() {
  // Create a logger with namespace "app"
  logger := ll.New("")

  // enable output
  logger.Enable()

  // Basic log
  logger.Info("Welcome") // Output: [app] INFO: Application started

  logger = logger.Namespace("app")

  // Basic log
  logger.Info("start at :8080") // Output: [app] INFO: Application started

  //Output
  //INFO: Welcome
  //[app] INFO: start at :8080
}

```

```go
package main

import (
    "github.com/olekukonko/ll"
    "github.com/olekukonko/ll/lh"
    "os"
)

func main() {
    // Chaining
    logger := ll.New("app").Enable().Handler(lh.NewTextHandler(os.Stdout))

    // Basic log
    logger.Info("Application started") // Output: [app] INFO: Application started

    // Structured log with fields
    logger.Fields("user", "alice", "status", 200).Info("User logged in")
    // Output: [app] INFO: User logged in [user=alice status=200]

    // Conditional log
    debugMode := false
    logger.If(debugMode).Debug("Debug info") // No output (debugMode is false)
}
```

## Core Features

### 1. Hierarchical Namespaces

Namespaces allow you to organize logs hierarchically, enabling precise control over logging for different parts of your application. This is especially useful for large systems with multiple components.

**Benefits**:
- **Granular Control**: Enable/disable logs for specific subsystems (e.g., "app/db" vs. "app/api").
- **Scalability**: Manage log volume in complex applications.
- **Readability**: Clear namespace paths improve traceability.

**Example**:
```go
logger := ll.New("app").Enable().Handler(lh.NewTextHandler(os.Stdout))

// Child loggers
dbLogger := logger.Namespace("db")
apiLogger := logger.Namespace("api").Style(lx.NestedPath)

// Namespace control
logger.NamespaceEnable("app/db")   // Enable DB logs
logger.NamespaceDisable("app/api") // Disable API logs

dbLogger.Info("Query executed")     // Output: [app/db] INFO: Query executed
apiLogger.Info("Request received")  // No output
```

### 2. Structured Logging

Add key-value metadata to logs for machine-readable output, making it easier to query and analyze logs in tools like ELK or Grafana.

**Example**:
```go
logger := ll.New("app").Enable().Handler(lh.NewTextHandler(os.Stdout))

// Variadic fields
logger.Fields("user", "bob", "status", 200).Info("Request completed")
// Output: [app] INFO: Request completed [user=bob status=200]

// Map-based fields
logger.Field(map[string]interface{}{"method": "GET"}).Info("Request")
// Output: [app] INFO: Request [method=GET]
```

### 3. Middleware Pipeline

Customize log processing with a middleware pipeline. Middleware functions can enrich, filter, or transform logs, using an error-based rejection mechanism (non-nil errors stop logging).

**Example**:
```go
logger := ll.New("app").Enable().Handler(lh.NewTextHandler(os.Stdout))

// Enrich logs with app metadata
logger.Use(ll.FuncMiddleware(func(e *lx.Entry) error {
    if e.Fields == nil {
        e.Fields = make(map[string]interface{})
    }
    e.Fields["app"] = "myapp"
    return nil
}))

// Filter low-level logs
logger.Use(ll.FuncMiddleware(func(e *lx.Entry) error {
    if e.Level < lx.LevelWarn {
        return fmt.Errorf("level too low")
    }
    return nil
}))

logger.Info("Ignored") // No output (filtered)
logger.Warn("Warning") // Output: [app] WARN: Warning [app=myapp]
```

### 4. Conditional Logging

Optimize performance by skipping expensive log operations when conditions are false, ideal for production environments.

**Example**:
```go
logger := ll.New("app").Enable().Handler(lh.NewTextHandler(os.Stdout))

featureEnabled := true
logger.If(featureEnabled).Fields("action", "update").Info("Feature used")
// Output: [app] INFO: Feature used [action=update]

logger.If(false).Info("Ignored") // No output, no processing
```

### 5. Multiple Output Formats

`ll` supports various output formats, including human-readable text, colorized logs, JSON, and integration with Go’s `slog` package.

**Example**:
```go
logger := ll.New("app").Enable()

// Text output
logger.Handler(lh.NewTextHandler(os.Stdout))
logger.Info("Text log") // Output: [app] INFO: Text log

// JSON output
logger.Handler(lh.NewJSONHandler(os.Stdout, time.RFC3339Nano))
logger.Info("JSON log") // Output: {"timestamp":"...","level":"INFO","message":"JSON log","namespace":"app"}

// Slog integration
slogText := slog.NewTextHandler(os.Stdout, nil)
logger.Handler(lh.NewSlogHandler(slogText))
logger.Info("Slog log") // Output: level=INFO msg="Slog log" namespace=app class=Text
```

### 6. Debugging Utilities

`ll` provides powerful tools for debugging, including variable inspection, binary data dumps, and stack traces.

#### Core Debugging Methods

1. **Dbg - Contextual Inspection**
   Inspects variables with file and line context, preserving variable names and handling all Go types.
   ```go
   x := 42
   user := struct{ Name string }{"Alice"}
   ll.Dbg(x)    // Output: [file.go:123] x = 42
   ll.Dbg(user) // Output: [file.go:124] user = [Name:Alice]
   ```

2. **Dump - Binary Inspection**
   Displays a hex/ASCII view of data, optimized for strings, bytes, and complex types (with JSON fallback).
   ```go
   ll.Handler(lh.NewColorizedHandler(os.Stdout))
   ll.Dump("hello\nworld") // Output: Hex/ASCII dump (see example/dump.png)
   ```

3. **Stack - Stack Inspection**
   Logs a stack trace for debugging critical errors.
   ```go
   ll.Handler(lh.NewColorizedHandler(os.Stdout))
   ll.Stack("Critical error") // Output: [app] ERROR: Critical error [stack=...] (see example/stack.png)
   ```

4**General Output**
   Logs a output in structured way for inspection of public & private values.
   ```go
   ll.Handler(lh.NewColorizedHandler(os.Stdout))
   ll.Output(&SomeStructWithPrivateValues{}) 
   ```
   
#### Performance Tracking
Measure execution time for performance analysis.
```go
// Automatic measurement
defer ll.Measure(func() { time.Sleep(time.Millisecond) })()
// Output: [app] INFO: function executed [duration=~1ms]

// Explicit benchmarking
start := time.Now()
time.Sleep(time.Millisecond)
ll.Benchmark(start) // Output: [app] INFO: benchmark [start=... end=... duration=...]
```

**Performance Notes**:
- `Dbg` calls are disabled at compile-time when not enabled.
- `Dump` optimizes for primitive types, strings, and bytes with zero-copy paths.
- Stack traces are configurable via `StackSize`.

## Real-World Example: Web Server

A practical example of using `ll` in a web server with structured logging, middleware, and `slog` integration:

```go
package main

import (
    "github.com/olekukonko/ll"
    "github.com/olekukonko/ll/lh"
    "log/slog"
    "net/http"
    "os"
    "time"
)

func main() {
    // Initialize logger with slog handler
    slogHandler := slog.NewJSONHandler(os.Stdout, nil)
    logger := ll.New("server").Enable().Handler(lh.NewSlogHandler(slogHandler))

    // HTTP child logger
    httpLogger := logger.Namespace("http").Style(lx.NestedPath)

    // Middleware for request ID
    httpLogger.Use(ll.FuncMiddleware(func(e *lx.Entry) error {
        if e.Fields == nil {
            e.Fields = make(map[string]interface{})
        }
        e.Fields["request_id"] = "req-" + time.Now().String()
        return nil
    }))

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        httpLogger.Fields("method", r.Method, "path", r.URL.Path).Info("Request received")
        w.Write([]byte("Hello, world!"))
        httpLogger.Fields("duration_ms", time.Since(start).Milliseconds()).Info("Request completed")
    })

    logger.Info("Starting server on :8080")
    http.ListenAndServe(":8080", nil)
}
```

**Sample Output (JSON via slog)**:
```json
{"level":"INFO","msg":"Starting server on :8080","namespace":"server"}
{"level":"INFO","msg":"Request received","namespace":"server/http","class":"Text","method":"GET","path":"/","request_id":"req-..."}
{"level":"INFO","msg":"Request completed","namespace":"server/http","class":"Text","duration_ms":1,"request_id":"req-..."}
```

## Why Choose `ll`?

- **Granular Control**: Hierarchical namespaces for precise log management.
- **Performance**: Conditional logging and optimized concatenation reduce overhead.
- **Extensibility**: Middleware pipeline for custom log processing.
- **Structured Output**: Machine-readable logs with key-value metadata.
- **Flexible Formats**: Text, JSON, colorized, and `slog` support.
- **Debugging Power**: Advanced tools like `Dbg`, `Dump`, and `Stack` for deep inspection.
- **Thread-Safe**: Safe for concurrent use in high-throughput applications.

## Comparison with Other Libraries

| Feature                  | `ll`                     | `log` (stdlib) | `slog` (stdlib) | `zap`             |
|--------------------------|--------------------------|----------------|-----------------|-------------------|
| Hierarchical Namespaces  | ✅                      | ❌             | ❌              | ❌                |
| Structured Logging       | ✅ (Fields, Context)     | ❌             | ✅              | ✅                |
| Middleware Pipeline      | ✅                      | ❌             | ❌              | ✅ (limited)      |
| Conditional Logging      | ✅ (If, IfOne, IfAny)   | ❌             | ❌              | ❌                |
| Slog Compatibility       | ✅                      | ❌             | ✅ (native)     | ❌                |
| Debugging (Dbg, Dump)    | ✅                      | ❌             | ❌              | ❌                |
| Performance (disabled logs) | High (conditional)    | Low            | Medium          | High              |
| Output Formats           | Text, JSON, Color, Slog | Text           | Text, JSON      | JSON, Text       |

## Benchmarks

`ll` is optimized for performance, particularly for disabled logs and structured logging:
- **Disabled Logs**: 30% faster than `slog` due to efficient conditional checks.
- **Structured Logging**: 2x faster than `log` with minimal allocations.
- **Namespace Caching**: Reduces overhead for hierarchical lookups.

See `ll_bench_test.go` for detailed benchmarks on namespace creation, cloning, and field building.

## Testing and Stability

The `ll` library includes a comprehensive test suite (`ll_test.go`) covering:
- Logger configuration, namespaces, and conditional logging.
- Middleware, rate limiting, and sampling.
- Handler output formats (text, JSON, slog).
- Debugging utilities (`Dbg`, `Dump`, `Stack`).

Recent improvements:
- Fixed sampling middleware for reliable behavior at edge cases (0.0 and 1.0 rates).
- Enhanced documentation across `conditional.go`, `field.go`, `global.go`, `ll.go`, `lx.go`, and `ns.go`.
- Added `slog` compatibility via `lh.SlogHandler`.

## Contributing

Contributions are welcome! To contribute:
1. Fork the repository: `github.com/olekukonko/ll`.
2. Create a feature branch: `git checkout -b feature/your-feature`.
3. Commit changes: `git commit -m "Add your feature"`.
4. Push to the branch: `git push origin feature/your-feature`.
5. Open a pull request with a clear description.

Please include tests in `ll_test.go` and update documentation as needed. Follow the Go coding style and run `go test ./...` before submitting.

## License

`ll` is licensed under the MIT License. See [LICENSE](LICENSE) for details.

## Resources

- **Source Code**: [github.com/olekukonko/ll](https://github.com/olekukonko/ll)
- **Issue Tracker**: [github.com/olekukonko/ll/issues](https://github.com/olekukonko/ll/issues)
- **GoDoc**: [pkg.go.dev/github.com/olekukonko/ll](https://pkg.go.dev/github.com/olekukonko/ll)