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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
*.prof

# Browse-audit output
.gstack/
33 changes: 32 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,36 @@ lockstep with the root module, e.g. `v1.0.7`, `logsentry/v1.0.7`, `goslog/v1.0.7

## [Unreleased]

## [1.1.1] - 2026-06-17

### Added

- Runnable `Example` functions for the primary `Logger` API (`Example`,
`ExampleLogger`) and the `log` subpackage, so the common usage paths appear
on pkg.go.dev and are verified by `go test`.
- Package documentation comment for the `log` subpackage.

### Changed

- **logsentry:** bump the indirect `golang.org/x/text` dependency from v0.34.0
to v0.37.0, aligning it with the `tools` submodule.

### Fixed

- A nil `*Logger` no longer panics on the level methods (`Trace`, `Debug`,
`Info`, `Warn`, `Error`, `Fatal` and their `At`/`Ctx`/`f`/`fCtx` variants).
They now return a nil `*Message`, honoring the nil-safe contract documented
in `doc.go`. Previously they dereferenced `l.config` while reading the level
and crashed with a nil pointer dereference.

### Documentation

- Clarify that duplicate-key prevention applies to keys inherited from the
logger, a sub-logger, or the context (the inherited value wins), not to
duplicate keys added within a single message chain.
- Fix the `doc.go` context example to use `golog.NewString` (there is no
`golog.Str` constructor).

## [1.1.0] - 2026-06-16

### Changed
Expand Down Expand Up @@ -109,7 +139,8 @@ lockstep with the root module, e.g. `v1.0.7`, `logsentry/v1.0.7`, `goslog/v1.0.7
attrib type for zero-allocation `time.Time` logging, and the `tag-release`
versioning script.

[Unreleased]: https://github.com/domonda/golog/compare/v1.1.0...HEAD
[Unreleased]: https://github.com/domonda/golog/compare/v1.1.1...HEAD
[1.1.1]: https://github.com/domonda/golog/compare/v1.1.0...v1.1.1
[1.1.0]: https://github.com/domonda/golog/compare/v1.0.7...v1.1.0
[1.0.7]: https://github.com/domonda/golog/compare/v1.0.6...v1.0.7
[1.0.6]: https://github.com/domonda/golog/compare/v1.0.5...v1.0.6
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Fast and feature-rich structured logging library for Go
- **Terminal Auto-Detection**: Automatically switches between colored text (TTY) and JSON (non-TTY)
- **Configurable Log Levels**: TRACE, DEBUG, INFO, WARN, ERROR, FATAL with flexible filtering
- **Context Support**: Log attributes can be stored in and retrieved from context automatically
- **Duplicate Key Prevention**: Prevents accidental duplicate keys in log output
- **Duplicate Key Prevention**: A message attribute is dropped when the same key was already set by the logger, a sub-logger, or the context (the inherited value wins), keeping inherited structured data clean
- **Colorized Output**: Beautiful colored console output with customizable colorizers
- **Multi-Writer Architecture**: Log to multiple destinations with different formats and filters
- **Rotating Log Files**: Automatic file rotation based on size thresholds
Expand Down Expand Up @@ -730,7 +730,7 @@ golog is designed to strike a balance between performance and flexibility. While
| Feature | zerolog | zap | golog |
|------------------------------------------|------------------|------------------|-----------------------|
| **Multi-writer support** | Single output | Limited | Native, unlimited |
| **Duplicate key prevention** | No | No | Yes |
| **Duplicate key prevention** (inherited) | No | No | Yes |
| **Context attribute integration** | Manual | Manual | Automatic |
| **Sub-logger with inherited attributes** | Basic | Basic | Full support with attrib recording |
| **Zero allocations (simple message)** | Yes | Yes | Yes |
Expand All @@ -755,7 +755,7 @@ golog is designed to strike a balance between performance and flexibility. While
- **Native multi-writer architecture**: Log to console, files, and external services simultaneously with different formats and filters per destination
- **Automatic context integration**: Attributes added to `context.Context` are automatically included in log messages without manual plumbing
- **Sub-logger attribute recording**: The `With().SubLogger()` pattern creates child loggers that efficiently inherit and extend parent attributes
- **Duplicate key prevention**: Prevents accidental duplicate keys in log output, ensuring clean structured data
- **Duplicate key prevention**: When a message sets a key already provided by the logger, a sub-logger, or the context, the inherited value is kept and the message-level duplicate is dropped, ensuring clean inherited structured data
- **Zero allocations for standard logging**: Despite the richer feature set, golog achieves zero allocations for JSON logging with fields
- **Nil-safe design**: A nil logger is safe to use and won't panic, simplifying error handling

Expand All @@ -766,7 +766,7 @@ golog is the right choice when you need:
- **Multiple output destinations**: Log to stdout with colors for development and JSON files for production simultaneously
- **Request-scoped logging**: Automatically propagate correlation IDs, user IDs, and other context through your application
- **Sub-loggers with inherited context**: Create child loggers for specific components that include parent attributes
- **Clean structured data**: Prevent duplicate keys from appearing in your logs
- **Clean structured data**: Prevent keys inherited from sub-loggers or context from being duplicated by message-level attributes
- **slog compatibility**: Use golog as a backend for Go's standard library logging interface
- **Rotating log files**: Built-in support for size-based log rotation

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v1.1.0
v1.1.1
4 changes: 2 additions & 2 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ Store and retrieve log attributes in context:

// Add attributes to context
ctx = golog.ContextWithAttribs(ctx,
golog.Str("correlation_id", corrID),
golog.Str("user_id", userID),
golog.NewString("correlation_id", corrID),
golog.NewString("user_id", userID),
)

// Create logger from context
Expand Down
49 changes: 49 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package golog

import (
"errors"
"os"
)

// Example shows the minimal logging setup: build a Config, create a Logger,
// and emit messages with the level methods. Passing a nil Format uses
// NewDefaultFormat, which prefixes each line with a timestamp; this example
// sets a Format without a TimestampKey so the output is reproducible.
func Example() {
config := NewConfig(
&DefaultLevels,
AllLevelsActive,
NewJSONWriterConfig(os.Stdout, &Format{LevelKey: "level", MessageKey: "message"}),
)
log := NewLogger(config)

log.Info("Application started").Log()
log.Error("Connection failed").Err(errors.New("timeout")).Log()

// Output:
// {"level":"INFO","message":"Application started"}
// {"level":"ERROR","message":"Connection failed","error":"timeout"}
}

// ExampleLogger demonstrates structured logging with typed field methods and a
// sub-logger whose attributes are inherited by every message it emits. The
// Format omits the TimestampKey so the output is reproducible.
func ExampleLogger() {
format := &Format{LevelKey: "level", MessageKey: "message"}
log := NewLogger(NewConfig(&DefaultLevels, AllLevelsActive, NewJSONWriterConfig(os.Stdout, format)))

// Typed field methods avoid reflection and stay zero-allocation.
log.Info("user login").
Str("user", "john_doe").
Int("attempt", 2).
Bool("admin", false).
Log()

// A sub-logger inherits attributes for every message it emits.
svc := log.With().Str("service", "auth").SubLogger()
svc.Warn("token expiring soon").Log()

// Output:
// {"level":"INFO","message":"user login","user":"john_doe","attempt":2,"admin":false}
// {"level":"WARN","message":"token expiring soon","service":"auth"}
}
Loading