Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 119 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,121 @@
# log

Transitland wrapper for zerolog.
A lightweight logging wrapper for [zerolog](https://github.com/rs/zerolog), providing convenience functions and sensible defaults for [Interline](https://www.interline.io/) and [Transitland](https://www.transit.land/) projects.

> **Note:** This library is primarily intended for internal use across Interline/Transitland repositories—mainly for our convenience and to avoid having `zerolog.Xyz` scattered throughout our codebase and copy-pasting logging middlewares. It's published publicly for our convenience; external users should probably just use zerolog directly.

## Features

- Simple printf-style logging functions (`Infof`, `Debugf`, `Tracef`, `Errorf`)
- Structured logging via zerolog's fluent API
- Pretty console output with colors (automatically disabled when piping/redirecting)
- JSON output mode for production environments
- Context-aware logging with request ID support
- HTTP middleware for request logging

## Installation

```bash
go get github.com/interline-io/log
```

## Usage

### Basic Logging

```go
package main

import "github.com/interline-io/log"

func main() {
// Printf-style logging
log.Infof("Processing %d items", 42)
log.Debugf("Debug info: %s", "details")
log.Errorf("Something went wrong: %v", err)
log.Tracef("Verbose trace: %+v", obj)

// Structured logging (zerolog style)
log.Info().Str("user", "alice").Int("count", 5).Msg("User action")
log.Error().Err(err).Str("op", "save").Msg("Operation failed")

// Simple print (no timestamp, ignores log level)
log.Print("Plain output: %s", "hello")
}
```

### Context-Aware Logging

```go
// Get logger from context
logger := log.For(ctx)
logger.Info().Msg("Using context logger")

// Add logger to context
ctx = log.WithLogger(ctx, customLogger)
```

### HTTP Middleware

```go
import "github.com/interline-io/log"

// Add request ID to each request
r.Use(log.RequestIDMiddleware)

// Add request ID to context logger
r.Use(log.RequestIDLoggingMiddleware)

// Log request duration and details
r.Use(log.LoggingMiddleware(1000, getUserNameFunc))
```

## Configuration

### Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `TL_LOG` | Log level: `TRACE`, `DEBUG`, `INFO`, `ERROR`, `FATAL` | `INFO` |
| `TL_LOG_JSON` | Set to `true` for JSON output (no colors) | `false` |

### Examples

```bash
# Enable debug logging
TL_LOG=DEBUG ./myapp

# Enable trace logging (most verbose)
TL_LOG=TRACE ./myapp

# JSON output for production/log aggregation
TL_LOG_JSON=true ./myapp
```

### Terminal Colors

Console output includes ANSI colors when writing to a terminal. Colors are automatically disabled when output is piped or redirected:

```bash
# Colors enabled (terminal)
./myapp

# Colors disabled (piped)
./myapp | tee output.log

# Colors disabled (redirected)
./myapp > output.log 2>&1
```

## Log Levels

| Level | Function | Use Case |
|-------|----------|----------|
| `TRACE` | `Tracef()`, `Trace()` | Verbose debugging, performance tracing |
| `DEBUG` | `Debugf()`, `Debug()` | Development debugging |
| `INFO` | `Infof()`, `Info()` | General operational messages |
| `ERROR` | `Errorf()`, `Error()` | Error conditions |

## License

See [LICENSE](LICENSE) file.
22 changes: 22 additions & 0 deletions cmd/testlog/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package main

import (
"github.com/interline-io/log"
)

func main() {
log.Print("This is a plain print (no level, no timestamp)")

log.Tracef("This is a trace message: %d", 1)
log.Debugf("This is a debug message: %d", 2)
log.Infof("This is an info message: %d", 3)
log.Errorf("This is an error message: %d", 4)

log.Traceln("Traceln:", "multiple", "args", 123)

log.Info().Str("key", "value").Int("count", 42).Msg("Structured logging example")
log.Debug().Str("user", "alice").Bool("active", true).Msg("User status")
log.Error().Err(nil).Str("op", "test").Msg("Error with nil error")

log.Print("Done!")
}
27 changes: 27 additions & 0 deletions cmd/testlog/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/bash

cd "$(dirname "$0")"

echo "=== Direct to terminal (should have colors) ==="
TL_LOG=TRACE go run main.go

echo ""
echo "=== Piped through cat (no colors) ==="
TL_LOG=TRACE go run main.go 2>&1 | cat

echo ""
echo "=== Piped through tee (no colors) ==="
TL_LOG=TRACE go run main.go 2>&1 | tee /tmp/testlog_output.txt

echo ""
echo "=== Redirected to file (no colors) ==="
TL_LOG=TRACE go run main.go > /tmp/testlog_redirect.txt 2>&1
cat /tmp/testlog_redirect.txt

echo ""
echo "=== JSON mode (TL_LOG_JSON=true) ==="
TL_LOG=TRACE TL_LOG_JSON=true go run main.go

echo ""
echo "=== Default log level (INFO) ==="
go run main.go
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
module github.com/interline-io/log

go 1.20
go 1.24.0

require github.com/rs/zerolog v1.31.0

require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/term v0.38.0 // indirect
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
9 changes: 7 additions & 2 deletions log.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"time"

"github.com/rs/zerolog"
"golang.org/x/term"
)

// Zerolog
Expand All @@ -18,7 +19,7 @@ func With() zerolog.Context {
}

func Fatal() *zerolog.Event {
return Logger.Info()
return Logger.Fatal()
}

func Info() *zerolog.Event {
Expand Down Expand Up @@ -88,7 +89,11 @@ func SetLevel(lvalue zerolog.Level) {
if !jsonLog {
// use console logging
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}
output := zerolog.ConsoleWriter{
Out: os.Stdout,
TimeFormat: time.RFC3339,
NoColor: !term.IsTerminal(int(os.Stdout.Fd())),
}
output.FormatLevel = func(i interface{}) string {
return strings.ToUpper(fmt.Sprintf("[%-5s]", i))
}
Expand Down
Loading