Documentation
¶
Overview ¶
CircularLogger: A Thread-Safe Circular Buffer Logger for Go ¶
## Description CircularLogger is a Go package that provides a thread-safe logging mechanism using a circular buffer. It is designed to store a fixed number of log entries, making it ideal for applications where memory usage needs to be controlled. The logger supports multiple log levels (TRACE, INFO, WARN, FATAL), optional file and line information, and customizable log formatting, including JSON output.
## Motivation I want to be able to log within packages. The standard to build go apps should be to write packages and one or more simple "mains" in cmd/... I like this approach but logging was always a bit of a painpoint since writing somewhere would need to make assumptions I don't want make when in package. Storing in memory creates the risk that it will be hogging a potentially infinite amount of ram depending on how its used. A circular buffer will never take more than what has initially been assigned to it. You decide how many entries you want to keep.
## Features - Thread-safe logging - Circular buffer to limit the number of stored logs - Support for log levels: TRACE, INFO, WARN, FATAL - Optional inclusion of file name and line number in logs - Customizable log formatting, including JSON - Methods for formatted logging (e.g., Infof, Warnf)
## Installation To use CircularLogger in your Go project, first ensure your project is set up as a Go module: ```sh go get codeberg.org/slaxor/circularlogger ``` ## Usage
In your package you can do this: ```go package thefoo
import (
"codeberg.org/slaxor/circularlogger"
)
var log = circularlogger.NewCircularLogger(10) var Log = log
func init() {
log.SetShortFile(true) // Enable file and line info
log.SetFormatter(circularlogger.AnsiFormatter) // Color lines
log.SetLevel(circularlogger.DEBUG) // Log debug or more severe
}
func Foo(s string) {
log.Debugf("called Foo(%q)", s)
}
```
And then in your main: ```go package main
import (
"fmt" "thefoo"
)
func main() {
thefoo.Foo("bar")
fmt.Print(thefoo.Log.String())
}
```
A nice trick to do realtime logging is:
```go package main
import (
"context" "thefoo"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
thefoo.Log.Watch(ctx)
for i := range 12 {
thefoo.Foo("bar")
thefoo.Log.Infof("% 2d", i)
}
}
```
Index ¶
- Constants
- func AnsiFormatter(entry LogEntry) string
- func DefaultFormatter(entry LogEntry) string
- func JSONFormatter(entry LogEntry) string
- func Redact(msg string) string
- type CircularLogger
- func (cl *CircularLogger) Debug(message string)
- func (cl *CircularLogger) Debugf(format string, args ...any)
- func (cl *CircularLogger) Error(message string)
- func (cl *CircularLogger) Errorf(format string, args ...any)
- func (cl *CircularLogger) Fatal(message string)
- func (cl *CircularLogger) Fatalf(format string, args ...any)
- func (cl *CircularLogger) GetLogs() []LogEntry
- func (cl *CircularLogger) Info(message string)
- func (cl *CircularLogger) Infof(format string, args ...any)
- func (cl *CircularLogger) Log(message string)
- func (cl *CircularLogger) SetFormatter(formatter Formatter)
- func (cl *CircularLogger) SetLevel(l Level)
- func (cl *CircularLogger) SetShortFile(enabled bool)
- func (cl *CircularLogger) String() string
- func (cl *CircularLogger) Trace(message string)
- func (cl *CircularLogger) Tracef(format string, args ...any)
- func (cl *CircularLogger) Warn(message string)
- func (cl *CircularLogger) Warnf(format string, args ...any)
- func (cl *CircularLogger) Watch(ctx context.Context)
- type Formatter
- type Level
- type LogEntry
Examples ¶
Constants ¶
const ( FATAL = Level(iota) ERROR WARN INFO DEBUG TRACE )
Variables ¶
This section is empty.
Functions ¶
func AnsiFormatter ¶
func DefaultFormatter ¶
DefaultFormatter is the Formatter that is used if none was defined
func JSONFormatter ¶
JSONFormatter formats a LogEntry as a JSON string
Types ¶
type CircularLogger ¶
type CircularLogger struct {
Formatter Formatter
// contains filtered or unexported fields
}
CircularLogger is a thread-safe circular buffer for logging
Example ¶
log := NewCircularLogger(10)
log.SetShortFile(true) // Enable file and line info
log.SetFormatter(JSONFormatter) // Optional: for line-by-line JSON in String()
log.Info("Starting application")
log.Warnf("Value is %d", 42)
fmt.Println("Line-by-line output:")
fmt.Print(log.String())
fmt.Println("\nJSON array output:")
js, err := json.Marshal(log.GetLogs())
if err != nil {
log.Fatalf("%s", err)
}
fmt.Printf("%s", js)
func NewCircularLogger ¶
func NewCircularLogger(capacity int) *CircularLogger
Update NewCircularLogger to initialize the channel
func (*CircularLogger) Debug ¶
func (cl *CircularLogger) Debug(message string)
Debug logs a message at DEBUG level
func (*CircularLogger) Debugf ¶
func (cl *CircularLogger) Debugf(format string, args ...any)
Debugf logs a formatted message at DEBUG level
func (*CircularLogger) Error ¶
func (cl *CircularLogger) Error(message string)
Error logs a message at ERROR level
func (*CircularLogger) Errorf ¶
func (cl *CircularLogger) Errorf(format string, args ...any)
Errorf logs a formatted message at ERROR level
func (*CircularLogger) Fatal ¶
func (cl *CircularLogger) Fatal(message string)
Fatal logs a message at FATAL level and exits the program
func (*CircularLogger) Fatalf ¶
func (cl *CircularLogger) Fatalf(format string, args ...any)
Fatalf logs a formatted message at FATAL level and exits the program
func (*CircularLogger) GetLogs ¶
func (cl *CircularLogger) GetLogs() []LogEntry
GetLogs returns all current log entries in order
func (*CircularLogger) Info ¶
func (cl *CircularLogger) Info(message string)
Info logs a message at INFO level
func (*CircularLogger) Infof ¶
func (cl *CircularLogger) Infof(format string, args ...any)
Infof logs a formatted message at INFO level
func (*CircularLogger) Log ¶
func (cl *CircularLogger) Log(message string)
Log adds a message to the circular buffer (for compatibility)
func (*CircularLogger) SetFormatter ¶
func (cl *CircularLogger) SetFormatter(formatter Formatter)
SetFormatter sets a custom formatter function for log entries
func (*CircularLogger) SetLevel ¶
func (cl *CircularLogger) SetLevel(l Level)
SetShortFile enables or disables including file and line information in logs
func (*CircularLogger) SetShortFile ¶
func (cl *CircularLogger) SetShortFile(enabled bool)
SetShortFile enables or disables including file and line information in logs
func (*CircularLogger) String ¶
func (cl *CircularLogger) String() string
String returns a formatted string of all log entries
func (*CircularLogger) Trace ¶
func (cl *CircularLogger) Trace(message string)
Trace logs a message at TRACE level
func (*CircularLogger) Tracef ¶
func (cl *CircularLogger) Tracef(format string, args ...any)
Tracef logs a formatted message at TRACE level
func (*CircularLogger) Warn ¶
func (cl *CircularLogger) Warn(message string)
Warn logs a message at WARN level
func (*CircularLogger) Warnf ¶
func (cl *CircularLogger) Warnf(format string, args ...any)
Warnf logs a formatted message at WARN level
func (*CircularLogger) Watch ¶
func (cl *CircularLogger) Watch(ctx context.Context)
Watch adds a watcher that runs as a goroutine
ctx, cancel := context.WithCancel(context.Background()) defer cancel() log.Watch(ctx)
type Level ¶
type Level int
func ParseLevel ¶
func (Level) MarshalJSON ¶
func (*Level) UnmarshalJSON ¶
type LogEntry ¶
type LogEntry struct {
Timestamp time.Time `json:"timestamp"`
Level Level `json:"level"`
File string `json:"file,omitempty"`
Line int `json:"line,omitempty"`
Message string `json:"message"`
}
LogEntry represents a single log entry with timestamp, level, message, and optional file info. A word of caution: The file:line info is really costly (~10 times). So if performance is an issue consider perhaps not using it.