Adding upstream version 0.3.1.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
4da56737a9
commit
93a9a63b70
11 changed files with 438 additions and 0 deletions
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
* text=auto eol=lf
|
6
.github/dependabot.yml
vendored
Normal file
6
.github/dependabot.yml
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
21
.github/workflows/test.yml
vendored
Normal file
21
.github/workflows/test.yml
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
name: test
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '*.md'
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths-ignore:
|
||||||
|
- '*.md'
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 3
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: 1.23.3
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: go test -mod vendor ./...
|
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# IDE
|
||||||
|
*.iml
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# JS
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Go
|
||||||
|
/vendor
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 TwiN
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
27
README.md
Normal file
27
README.md
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# logr
|
||||||
|
Go logging library with levels.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
```console
|
||||||
|
go get -u github.com/TwiN/logr
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/TwiN/logr"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
logr.Debug("This is a debug message")
|
||||||
|
logr.Infof("This is an %s message", "info")
|
||||||
|
logr.Warn("This is a warn message")
|
||||||
|
logr.Error("This is an error message")
|
||||||
|
logr.Fatal("This is a fatal message") // Exits with code 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can set the default logger's threshold like so:
|
||||||
|
```go
|
||||||
|
logr.SetThreshold(logr.LevelWarn)
|
||||||
|
```
|
||||||
|
The above would make it so only `WARN`, `ERROR` and `FATAL` messages are logged, while `DEBUG` and `INFO` messages are ignored.
|
||||||
|
|
||||||
|
TODO: Finish documentation
|
3
go.mod
Normal file
3
go.mod
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module github.com/TwiN/logr
|
||||||
|
|
||||||
|
go 1.23.3
|
58
levels.go
Normal file
58
levels.go
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package logr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Level string
|
||||||
|
|
||||||
|
const (
|
||||||
|
LevelDebug Level = "DEBUG"
|
||||||
|
LevelInfo Level = "INFO"
|
||||||
|
LevelWarn Level = "WARN"
|
||||||
|
LevelError Level = "ERROR"
|
||||||
|
LevelFatal Level = "FATAL"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidLevelString = errors.New("invalid level, must be one of DEBUG, INFO, WARN, ERROR or FATAL")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (level Level) Value() int {
|
||||||
|
switch level {
|
||||||
|
case LevelDebug:
|
||||||
|
return 0
|
||||||
|
case LevelInfo:
|
||||||
|
return 1
|
||||||
|
case LevelWarn:
|
||||||
|
return 2
|
||||||
|
case LevelError:
|
||||||
|
return 3
|
||||||
|
case LevelFatal:
|
||||||
|
return 4
|
||||||
|
default:
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (level Level) IsValid() bool {
|
||||||
|
return level.Value() != -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func LevelFromString(level string) (Level, error) {
|
||||||
|
switch strings.ToUpper(level) {
|
||||||
|
case "DEBUG":
|
||||||
|
return LevelDebug, nil
|
||||||
|
case "INFO":
|
||||||
|
return LevelInfo, nil
|
||||||
|
case "WARN":
|
||||||
|
return LevelWarn, nil
|
||||||
|
case "ERROR":
|
||||||
|
return LevelError, nil
|
||||||
|
case "FATAL":
|
||||||
|
return LevelFatal, nil
|
||||||
|
default:
|
||||||
|
return LevelDebug, ErrInvalidLevelString
|
||||||
|
}
|
||||||
|
}
|
35
levels_test.go
Normal file
35
levels_test.go
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package logr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLevelFromString(t *testing.T) {
|
||||||
|
scenarios := []struct {
|
||||||
|
input string
|
||||||
|
expected Level
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{"DEBUG", LevelDebug, nil},
|
||||||
|
{"debug", LevelDebug, nil},
|
||||||
|
{"INFO", LevelInfo, nil},
|
||||||
|
{"info", LevelInfo, nil},
|
||||||
|
{"WARN", LevelWarn, nil},
|
||||||
|
{"warn", LevelWarn, nil},
|
||||||
|
{"ERROR", LevelError, nil},
|
||||||
|
{"error", LevelError, nil},
|
||||||
|
{"", LevelDebug, ErrInvalidLevelString},
|
||||||
|
{"invalid", LevelDebug, ErrInvalidLevelString},
|
||||||
|
}
|
||||||
|
for _, scenario := range scenarios {
|
||||||
|
t.Run(scenario.input, func(t *testing.T) {
|
||||||
|
actual, err := LevelFromString(scenario.input)
|
||||||
|
if actual != scenario.expected {
|
||||||
|
t.Errorf("expected %s, got %s", scenario.expected, actual)
|
||||||
|
}
|
||||||
|
if err != scenario.expectedErr {
|
||||||
|
t.Errorf("expected %v, got %v", scenario.expectedErr, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
178
logr.go
Normal file
178
logr.go
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
package logr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var defaultLogger = New(LevelInfo, false, os.Stdout)
|
||||||
|
|
||||||
|
type Logger struct {
|
||||||
|
// threshold is the minimum level output by this logger.
|
||||||
|
//
|
||||||
|
// For example, if the threshold is set to LevelWarn, then only logs of LevelWarn and LevelError will be output
|
||||||
|
// while logs of LevelDebug and LevelInfo will be ignored.
|
||||||
|
//
|
||||||
|
// Defaults to LevelInfo.
|
||||||
|
threshold Level
|
||||||
|
|
||||||
|
// shouldPrefixMessageWithLevel is whether to include the log level prefix in each log.
|
||||||
|
//
|
||||||
|
// For example, if this is set to true, then a debug log would output "<date> DEBUG <message>"
|
||||||
|
//
|
||||||
|
// Defaults to false.
|
||||||
|
shouldPrefixMessageWithLevel bool
|
||||||
|
|
||||||
|
stdLogger *log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new logger with the given threshold and output.
|
||||||
|
func New(threshold Level, shouldPrefixMessageWithLevel bool, output io.Writer) *Logger {
|
||||||
|
if !threshold.IsValid() {
|
||||||
|
threshold = LevelInfo // Default to LevelInfo if the threshold is invalid
|
||||||
|
}
|
||||||
|
return &Logger{
|
||||||
|
threshold: threshold,
|
||||||
|
shouldPrefixMessageWithLevel: shouldPrefixMessageWithLevel,
|
||||||
|
stdLogger: log.New(output, "", log.LstdFlags),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) SetOutput(output io.Writer) {
|
||||||
|
// Because we're using the standard log package under the hood, we can just directly pass this
|
||||||
|
// to the logger and call it a day. It already takes care of locking and discarding the previous writer.
|
||||||
|
logger.stdLogger.SetOutput(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) SetThreshold(threshold Level) {
|
||||||
|
if !threshold.IsValid() {
|
||||||
|
threshold = LevelInfo // Default to LevelInfo if the threshold is invalid
|
||||||
|
} else {
|
||||||
|
logger.threshold = threshold
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) GetThreshold() Level {
|
||||||
|
return logger.threshold
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Log(level Level, message string) {
|
||||||
|
logger.Logf(level, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Logf(level Level, format string, args ...any) {
|
||||||
|
if level.Value() < logger.threshold.Value() {
|
||||||
|
// The log level is below the threshold, so ignore the log
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if logger.shouldPrefixMessageWithLevel {
|
||||||
|
format = "- " + string(level) + " - " + format
|
||||||
|
}
|
||||||
|
logger.stdLogger.Printf(format, args...)
|
||||||
|
if level == LevelFatal {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Debug(message string) {
|
||||||
|
logger.Log(LevelDebug, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Debugf(format string, args ...any) {
|
||||||
|
logger.Logf(LevelDebug, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Info(message string) {
|
||||||
|
logger.Log(LevelInfo, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Infof(format string, args ...any) {
|
||||||
|
logger.Logf(LevelInfo, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warn(message string) {
|
||||||
|
logger.Log(LevelWarn, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warnf(format string, args ...any) {
|
||||||
|
logger.Logf(LevelWarn, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Error(message string) {
|
||||||
|
logger.Log(LevelError, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Errorf(format string, args ...any) {
|
||||||
|
logger.Logf(LevelError, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Fatal(message string) {
|
||||||
|
logger.Log(LevelFatal, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Fatalf(format string, args ...any) {
|
||||||
|
logger.Logf(LevelFatal, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOutput sets the output of the default logger
|
||||||
|
func SetOutput(output io.Writer) {
|
||||||
|
defaultLogger.SetOutput(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetThreshold sets the minimum level output by the default logger
|
||||||
|
func SetThreshold(threshold Level) {
|
||||||
|
defaultLogger.SetThreshold(threshold)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetThreshold() Level {
|
||||||
|
return defaultLogger.GetThreshold()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Log(level Level, message string) {
|
||||||
|
defaultLogger.Log(level, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Logf(level Level, message string, args ...any) {
|
||||||
|
defaultLogger.Logf(level, message, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Debug(message string) {
|
||||||
|
defaultLogger.Debug(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Debugf(format string, args ...any) {
|
||||||
|
defaultLogger.Debugf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Info(message string) {
|
||||||
|
defaultLogger.Info(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Infof(format string, args ...any) {
|
||||||
|
defaultLogger.Infof(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Warn(message string) {
|
||||||
|
defaultLogger.Warn(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Warnf(format string, args ...any) {
|
||||||
|
defaultLogger.Warnf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Error(message string) {
|
||||||
|
defaultLogger.Error(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Errorf(format string, args ...any) {
|
||||||
|
defaultLogger.Errorf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Fatal(message string) {
|
||||||
|
defaultLogger.Fatal(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Fatalf(format string, args ...any) {
|
||||||
|
defaultLogger.Fatalf(format, args...)
|
||||||
|
}
|
75
logr_test.go
Normal file
75
logr_test.go
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
package logr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNew(t *testing.T) {
|
||||||
|
logger := New("what", false, nil)
|
||||||
|
if logger.threshold != LevelInfo {
|
||||||
|
t.Error("expected invalid threshold to be silently set to INFO, got", logger.threshold)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogger(t *testing.T) {
|
||||||
|
var writer bytes.Buffer
|
||||||
|
logger := New(LevelWarn, false, &writer)
|
||||||
|
logger.Debug("this is debug")
|
||||||
|
logger.Info("this is info")
|
||||||
|
logger.Warn("this is warn")
|
||||||
|
logger.Error("this is error")
|
||||||
|
output := writer.String()
|
||||||
|
if numberOfNewLines := strings.Count(output, "\n"); numberOfNewLines != 2 {
|
||||||
|
t.Error("expected 2 newlines, got", numberOfNewLines)
|
||||||
|
}
|
||||||
|
if strings.Contains(output, "this is debug") {
|
||||||
|
t.Error("expected no debug message, got", output)
|
||||||
|
}
|
||||||
|
if strings.Contains(output, "this is info") {
|
||||||
|
t.Error("expected no info message, got", output)
|
||||||
|
}
|
||||||
|
if !strings.Contains(output, "this is warn") {
|
||||||
|
t.Error("expected warn message, got", output)
|
||||||
|
}
|
||||||
|
if !strings.Contains(output, "this is error") {
|
||||||
|
t.Error("expected error message, got", output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogger_SetThreshold(t *testing.T) {
|
||||||
|
var writer bytes.Buffer
|
||||||
|
logger := New(LevelDebug, false, &writer)
|
||||||
|
logger.SetThreshold(LevelError)
|
||||||
|
logger.Debug("this is debug")
|
||||||
|
logger.Info("this is info")
|
||||||
|
logger.Warn("this is warn")
|
||||||
|
logger.Error("this is error")
|
||||||
|
output := writer.String()
|
||||||
|
if numberOfNewLines := strings.Count(output, "\n"); numberOfNewLines != 1 {
|
||||||
|
t.Error("expected 1 newline, got", numberOfNewLines)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoggerFormatting(t *testing.T) {
|
||||||
|
var writer bytes.Buffer
|
||||||
|
logger := New(LevelDebug, true, &writer)
|
||||||
|
logger.Debugf("hello, %s", "world")
|
||||||
|
logger.Infof("%d", 11111)
|
||||||
|
logger.Warnf("%s got %d%% in math", "John Doe", 87)
|
||||||
|
logger.Errorf("%s sent %s$%.02f to %s", "John", "CAD", 69.1, "Jane")
|
||||||
|
output := writer.String()
|
||||||
|
if !strings.Contains(output, "- DEBUG - hello, world") {
|
||||||
|
t.Error("expected '- DEBUG - hello, world.', got", output)
|
||||||
|
}
|
||||||
|
if !strings.Contains(output, "- INFO - 11111") {
|
||||||
|
t.Error("expected '- INFO - John Doe got 87% in math', got", output)
|
||||||
|
}
|
||||||
|
if !strings.Contains(output, "- WARN - John Doe got 87% in math") {
|
||||||
|
t.Error("expected '- WARN - John Doe got 87% in math', got", output)
|
||||||
|
}
|
||||||
|
if !strings.Contains(output, "- ERROR - John sent CAD$69.10 to Jane") {
|
||||||
|
t.Error("expected '- ERROR - John sent USD$123.40 to Jane', got", output)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue