update to what is current
This commit is contained in:
13
chk/chk.go
Normal file
13
chk/chk.go
Normal file
@@ -0,0 +1,13 @@
|
||||
// Package chk is a convenience shortcut to use shorter names to access the lol.Logger.
|
||||
package chk
|
||||
|
||||
import (
|
||||
"github.com/mleku/lol"
|
||||
)
|
||||
|
||||
var F, E, W, I, D, T lol.Chk
|
||||
|
||||
func init() {
|
||||
F, E, W, I, D, T = lol.Main.Check.F, lol.Main.Check.E, lol.Main.Check.W, lol.Main.Check.I,
|
||||
lol.Main.Check.D, lol.Main.Check.T
|
||||
}
|
||||
12
errorf/errorf.go
Normal file
12
errorf/errorf.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Package errorf is a convenience shortcut to use shorter names to access the lol.Logger.
|
||||
package errorf
|
||||
|
||||
import (
|
||||
"github.com/mleku/lol"
|
||||
)
|
||||
|
||||
var F, E, W, I, D, T lol.Err
|
||||
|
||||
func init() {
|
||||
F, E, W, I, D, T = lol.Main.Errorf.F, lol.Main.Errorf.E, lol.Main.Errorf.W, lol.Main.Errorf.I, lol.Main.Errorf.D, lol.Main.Errorf.T
|
||||
}
|
||||
10
go.mod
10
go.mod
@@ -1,14 +1,14 @@
|
||||
module github.com/mleku/lol
|
||||
|
||||
go 1.22.4
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/gookit/color v1.5.4
|
||||
go.uber.org/atomic v1.11.0
|
||||
github.com/fatih/color v1.18.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
golang.org/x/sys v0.35.0 // indirect
|
||||
)
|
||||
|
||||
27
go.sum
27
go.sum
@@ -1,18 +1,13 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
|
||||
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
|
||||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
orly.dev v0.8.7 h1:81Dn93vvZM0Q3GmGxmd+m44PkFOZpmeZBszS33vgk5I=
|
||||
orly.dev v0.8.7/go.mod h1:jnbkB9+qxjUdsy8bdiR8Mk0t3Y+UeLnS3pY3dS6Oi/M=
|
||||
|
||||
337
log.go
337
log.go
@@ -1,3 +1,7 @@
|
||||
// Package lol (log of location) is a simple logging library that prints a high
|
||||
// precision unix timestamp and the source location of a log print to make
|
||||
// tracing errors simpler. Includes a set of logging levels and the ability to
|
||||
// filter out higher log levels for a more quiet output.
|
||||
package lol
|
||||
|
||||
import (
|
||||
@@ -5,48 +9,13 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/gookit/color"
|
||||
"go.uber.org/atomic"
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
var l = GetStd()
|
||||
|
||||
func GetStd() (ll *Log) {
|
||||
ll, _ = New(os.Stdout)
|
||||
return
|
||||
}
|
||||
|
||||
func init() {
|
||||
switch strings.ToUpper(os.Getenv("GODEBUG")) {
|
||||
case "1", "TRUE", "ON":
|
||||
SetLogLevel(Debug)
|
||||
l.D.Ln("printing logs at this level and lower")
|
||||
case "INFO":
|
||||
SetLogLevel(Info)
|
||||
case "DEBUG":
|
||||
SetLogLevel(Debug)
|
||||
l.D.Ln("printing logs at this level and lower")
|
||||
case "TRACE":
|
||||
SetLogLevel(Trace)
|
||||
l.T.Ln("printing logs at this level and lower")
|
||||
case "WARN":
|
||||
SetLogLevel(Warn)
|
||||
case "ERROR":
|
||||
SetLogLevel(Error)
|
||||
case "FATAL":
|
||||
SetLogLevel(Fatal)
|
||||
case "0", "OFF", "FALSE":
|
||||
SetLogLevel(Off)
|
||||
default:
|
||||
SetLogLevel(Info)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const (
|
||||
Off = iota
|
||||
Fatal
|
||||
@@ -57,24 +26,36 @@ const (
|
||||
Trace
|
||||
)
|
||||
|
||||
type (
|
||||
// LevelPrinter defines a set of terminal printing primitives that output with
|
||||
// extra data, time, log logLevelList, and code location
|
||||
var LevelNames = []string{
|
||||
"off",
|
||||
"fatal",
|
||||
"error",
|
||||
"warn",
|
||||
"info",
|
||||
"debug",
|
||||
"trace",
|
||||
}
|
||||
|
||||
// Ln prints lists of interfaces with spaces in between
|
||||
type (
|
||||
// LevelPrinter defines a set of terminal printing primitives that output
|
||||
// with extra data, time, log logLevelList, and code location
|
||||
|
||||
// Ln prints lists of server with spaces in between
|
||||
Ln func(a ...interface{})
|
||||
// F prints like fmt.Println surrounded by log details
|
||||
// F prints like fmt.Println surrounded []byte log details
|
||||
F func(format string, a ...interface{})
|
||||
// S prints a spew.Sdump for an interface slice
|
||||
// S prints a spew.Sdump for an enveloper slice
|
||||
S func(a ...interface{})
|
||||
// C accepts a function so that the extra computation can be avoided if it is
|
||||
// not being viewed
|
||||
// C accepts a function so that the extra computation can be avoided if it is not being
|
||||
// viewed
|
||||
C func(closure func() string)
|
||||
// Chk is a shortcut for printing if there is an error, or returning true
|
||||
Chk func(e error) bool
|
||||
// Err is a pass-through function that uses fmt.Errorf to construct an error
|
||||
// and returns the error after printing it to the log
|
||||
Err func(format string, a ...interface{}) error
|
||||
// Err is a pass-through function that uses fmt.Errorf to construct an error and returns the
|
||||
// error after printing it to the log
|
||||
Err func(format string, a ...any) error
|
||||
|
||||
// LevelPrinter is the set of log printers on each log level.
|
||||
LevelPrinter struct {
|
||||
Ln
|
||||
F
|
||||
@@ -83,10 +64,12 @@ type (
|
||||
Chk
|
||||
Err
|
||||
}
|
||||
|
||||
// LevelSpec is the name, ID and Colorizer for a log level.
|
||||
LevelSpec struct {
|
||||
ID int
|
||||
Name string
|
||||
Colorizer func(a ...interface{}) string
|
||||
Colorizer func(a ...any) string
|
||||
}
|
||||
|
||||
// Entry is a log entry to be printed as json to the log file
|
||||
@@ -100,33 +83,98 @@ type (
|
||||
)
|
||||
|
||||
var (
|
||||
// sep is just a convenient shortcut for this very longwinded expression
|
||||
sep = string(os.PathSeparator)
|
||||
currentLevel = atomic.NewInt32(Info)
|
||||
// writer can be swapped out for any io.*writer* that you want to use instead of
|
||||
// stdout.
|
||||
writer io.Writer = os.Stderr
|
||||
// Writer can be swapped out for any io.*Writer* that you want to use instead of stdout.
|
||||
Writer io.Writer = os.Stderr
|
||||
|
||||
// LevelSpecs specifies the id, string name and color-printing function
|
||||
LevelSpecs = []LevelSpec{
|
||||
{Off, " ", color.Bit24(0, 0, 0, false).Sprint},
|
||||
{Fatal, "FTL", color.Bit24(128, 0, 0, false).Sprint},
|
||||
{Error, "ERR", color.Bit24(255, 0, 0, false).Sprint},
|
||||
{Warn, "WRN", color.Bit24(0, 255, 0, false).Sprint},
|
||||
{Info, "INF", color.Bit24(255, 255, 0, false).Sprint},
|
||||
{Debug, "DBG", color.Bit24(0, 125, 255, false).Sprint},
|
||||
{Trace, "TRC", color.Bit24(125, 0, 255, false).Sprint},
|
||||
{Off, "", NoSprint},
|
||||
{Fatal, "FTL", color.New(color.BgRed, color.FgHiWhite).Sprint},
|
||||
{Error, "ERR", color.New(color.FgHiRed).Sprint},
|
||||
{Warn, "WRN", color.New(color.FgHiYellow).Sprint},
|
||||
{Info, "INF", color.New(color.FgHiGreen).Sprint},
|
||||
{Debug, "DBG", color.New(color.FgHiBlue).Sprint},
|
||||
{Trace, "TRC", color.New(color.FgHiMagenta).Sprint},
|
||||
}
|
||||
)
|
||||
|
||||
// NoSprint is a noop for sprint (it returns nothing no matter what is given to it).
|
||||
func NoSprint(_ ...any) string { return "" }
|
||||
|
||||
// Log is a set of log printers for the various Level items.
|
||||
type Log struct {
|
||||
F, E, W, I, D, T LevelPrinter
|
||||
}
|
||||
|
||||
// Check is the set of log levels for a Check operation (prints an error if the error is not
|
||||
// nil).
|
||||
type Check struct {
|
||||
F, E, W, I, D, T Chk
|
||||
}
|
||||
|
||||
// Errorf prints an error that is also returned as an error, so the error is logged at the site.
|
||||
type Errorf struct {
|
||||
F, E, W, I, D, T Err
|
||||
}
|
||||
|
||||
// Logger is a collection of things that creates a logger, including levels.
|
||||
type Logger struct {
|
||||
*Log
|
||||
*Check
|
||||
*Errorf
|
||||
}
|
||||
|
||||
// Level is the level that the logger is printing at.
|
||||
var Level atomic.Int32
|
||||
|
||||
// Main is the main logger.
|
||||
var Main = &Logger{}
|
||||
|
||||
func init() {
|
||||
// Main = &Logger{}
|
||||
Main.Log, Main.Check, Main.Errorf = New(os.Stderr, 2)
|
||||
ll := os.Getenv("LOG_LEVEL")
|
||||
if ll == "" {
|
||||
SetLogLevel("info")
|
||||
} else {
|
||||
for i := range LevelNames {
|
||||
if ll == LevelNames[i] {
|
||||
SetLoggers(i)
|
||||
return
|
||||
}
|
||||
}
|
||||
SetLoggers(Info)
|
||||
}
|
||||
}
|
||||
|
||||
// SetLoggers configures a log level.
|
||||
func SetLoggers(level int) {
|
||||
Main.Log.T.F("log level %s", LevelSpecs[level].Colorizer(LevelNames[level]))
|
||||
Level.Store(int32(level))
|
||||
}
|
||||
|
||||
// GetLogLevel returns the log level number of a string log level.
|
||||
func GetLogLevel(level string) (i int) {
|
||||
for i = range LevelNames {
|
||||
if level == LevelNames[i] {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return Info
|
||||
}
|
||||
|
||||
// SetLogLevel sets the log level of the logger.
|
||||
func SetLogLevel(level string) {
|
||||
for i := range LevelNames {
|
||||
if level == LevelNames[i] {
|
||||
SetLoggers(i)
|
||||
return
|
||||
}
|
||||
}
|
||||
SetLoggers(Trace)
|
||||
}
|
||||
|
||||
// JoinStrings joins together anything into a set of strings with space separating the items.
|
||||
func JoinStrings(a ...any) (s string) {
|
||||
for i := range a {
|
||||
s += fmt.Sprint(a[i])
|
||||
@@ -137,78 +185,124 @@ func JoinStrings(a ...any) (s string) {
|
||||
return
|
||||
}
|
||||
|
||||
func GetPrinter(l int32, writer io.Writer) LevelPrinter {
|
||||
var msgCol = color.New(color.FgBlue).Sprint
|
||||
|
||||
// GetPrinter returns a full logger that writes to the provided io.Writer.
|
||||
func GetPrinter(l int32, writer io.Writer, skip int) LevelPrinter {
|
||||
return LevelPrinter{
|
||||
Ln: func(a ...interface{}) {
|
||||
fmt.Fprintf(writer,
|
||||
"%s %s %s %s\n",
|
||||
UnixNanoAsFloat(),
|
||||
if Level.Load() < l {
|
||||
return
|
||||
}
|
||||
_, _ = fmt.Fprintf(
|
||||
writer,
|
||||
"%s%s %s %s\n",
|
||||
msgCol(TimeStamper()),
|
||||
LevelSpecs[l].Colorizer(LevelSpecs[l].Name),
|
||||
JoinStrings(a...),
|
||||
GetLoc(2),
|
||||
msgCol(GetLoc(skip)),
|
||||
)
|
||||
},
|
||||
F: func(format string, a ...interface{}) {
|
||||
fmt.Fprintf(writer,
|
||||
"%s %s %s %s\n",
|
||||
UnixNanoAsFloat(),
|
||||
if Level.Load() < l {
|
||||
return
|
||||
}
|
||||
_, _ = fmt.Fprintf(
|
||||
writer,
|
||||
"%s%s %s %s\n",
|
||||
msgCol(TimeStamper()),
|
||||
LevelSpecs[l].Colorizer(LevelSpecs[l].Name),
|
||||
fmt.Sprintf(format, a...),
|
||||
GetLoc(2),
|
||||
msgCol(GetLoc(skip)),
|
||||
)
|
||||
},
|
||||
S: func(a ...interface{}) {
|
||||
fmt.Fprintf(writer,
|
||||
"%s %s %s %s\n",
|
||||
UnixNanoAsFloat(),
|
||||
if Level.Load() < l {
|
||||
return
|
||||
}
|
||||
_, _ = fmt.Fprintf(
|
||||
writer,
|
||||
"%s%s %s %s\n",
|
||||
msgCol(TimeStamper()),
|
||||
LevelSpecs[l].Colorizer(LevelSpecs[l].Name),
|
||||
spew.Sdump(a...),
|
||||
GetLoc(2),
|
||||
msgCol(GetLoc(skip)),
|
||||
)
|
||||
},
|
||||
C: func(closure func() string) {
|
||||
fmt.Fprintf(writer,
|
||||
"%s %s %s %s\n",
|
||||
UnixNanoAsFloat(),
|
||||
if Level.Load() < l {
|
||||
return
|
||||
}
|
||||
_, _ = fmt.Fprintf(
|
||||
writer,
|
||||
"%s%s %s %s\n",
|
||||
msgCol(TimeStamper()),
|
||||
LevelSpecs[l].Colorizer(LevelSpecs[l].Name),
|
||||
closure(),
|
||||
GetLoc(2),
|
||||
msgCol(GetLoc(skip)),
|
||||
)
|
||||
},
|
||||
Chk: func(e error) bool {
|
||||
if Level.Load() < l {
|
||||
return e != nil
|
||||
}
|
||||
if e != nil {
|
||||
fmt.Fprintf(writer,
|
||||
"%s %s %s %s\n",
|
||||
UnixNanoAsFloat(),
|
||||
_, _ = fmt.Fprintf(
|
||||
writer,
|
||||
"%s%s %s %s\n",
|
||||
msgCol(TimeStamper()),
|
||||
LevelSpecs[l].Colorizer(LevelSpecs[l].Name),
|
||||
e.Error(),
|
||||
GetLoc(2),
|
||||
msgCol(GetLoc(skip)),
|
||||
)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
Err: func(format string, a ...interface{}) error {
|
||||
fmt.Fprintf(writer,
|
||||
"%s %s %s %s\n",
|
||||
UnixNanoAsFloat(),
|
||||
LevelSpecs[l].Colorizer(LevelSpecs[l].Name, " "),
|
||||
fmt.Sprintf(format, a...),
|
||||
GetLoc(2),
|
||||
)
|
||||
if Level.Load() >= l {
|
||||
_, _ = fmt.Fprintf(
|
||||
writer,
|
||||
"%s%s %s %s\n",
|
||||
msgCol(TimeStamper()),
|
||||
LevelSpecs[l].Colorizer(LevelSpecs[l].Name),
|
||||
fmt.Sprintf(format, a...),
|
||||
msgCol(GetLoc(skip)),
|
||||
)
|
||||
}
|
||||
return fmt.Errorf(format, a...)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func New(writer io.Writer) (l *Log, c *Check) {
|
||||
// GetNullPrinter is a logger that doesn't log.
|
||||
func GetNullPrinter() LevelPrinter {
|
||||
return LevelPrinter{
|
||||
Ln: func(a ...interface{}) {},
|
||||
F: func(format string, a ...interface{}) {},
|
||||
S: func(a ...interface{}) {},
|
||||
C: func(closure func() string) {},
|
||||
Chk: func(e error) bool { return e != nil },
|
||||
Err: func(
|
||||
format string, a ...interface{},
|
||||
) error {
|
||||
return fmt.Errorf(format, a...)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new logger with all the levels and things.
|
||||
func New(writer io.Writer, skip int) (l *Log, c *Check, errorf *Errorf) {
|
||||
if writer == nil {
|
||||
writer = Writer
|
||||
}
|
||||
l = &Log{
|
||||
F: GetPrinter(Fatal, writer),
|
||||
E: GetPrinter(Error, writer),
|
||||
W: GetPrinter(Warn, writer),
|
||||
I: GetPrinter(Info, writer),
|
||||
D: GetPrinter(Debug, writer),
|
||||
T: GetPrinter(Trace, writer),
|
||||
T: GetPrinter(Trace, writer, skip),
|
||||
D: GetPrinter(Debug, writer, skip),
|
||||
I: GetPrinter(Info, writer, skip),
|
||||
W: GetPrinter(Warn, writer, skip),
|
||||
E: GetPrinter(Error, writer, skip),
|
||||
F: GetPrinter(Fatal, writer, skip),
|
||||
}
|
||||
c = &Check{
|
||||
F: l.F.Chk,
|
||||
@@ -218,38 +312,35 @@ func New(writer io.Writer) (l *Log, c *Check) {
|
||||
D: l.D.Chk,
|
||||
T: l.T.Chk,
|
||||
}
|
||||
errorf = &Errorf{
|
||||
F: l.F.Err,
|
||||
E: l.E.Err,
|
||||
W: l.W.Err,
|
||||
I: l.I.Err,
|
||||
D: l.D.Err,
|
||||
T: l.T.Err,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SetLogLevel sets the log level via a string, which can be truncated down to
|
||||
// one character, similar to nmcli's argument processor, as the first letter is
|
||||
// unique. This could be used with a linter to make larger command sets.
|
||||
func SetLogLevel(l int) {
|
||||
currentLevel.Store(int32(l))
|
||||
}
|
||||
|
||||
func GetLogLevel() (l int) {
|
||||
return int(currentLevel.Load())
|
||||
}
|
||||
|
||||
// UnixNanoAsFloat e
|
||||
func UnixNanoAsFloat() (s string) {
|
||||
timeText := fmt.Sprint(time.Now().UnixNano())
|
||||
lt := len(timeText)
|
||||
lb := lt + 1
|
||||
var timeBytes = make([]byte, lb)
|
||||
copy(timeBytes[lb-9:lb], timeText[lt-9:lt])
|
||||
timeBytes[lb-10] = '.'
|
||||
lb -= 10
|
||||
lt -= 9
|
||||
copy(timeBytes[:lb], timeText[:lt])
|
||||
return color.Bit24(0, 128, 255, false).Sprint(string(timeBytes))
|
||||
// TimeStamper generates the timestamp for logs.
|
||||
func TimeStamper() (s string) {
|
||||
ts := time.Now().Format("150405.000000")
|
||||
ds := time.Now().Format("2006-01-02")
|
||||
s += color.New(color.FgBlue).Sprint(ds[0:4])
|
||||
s += color.New(color.FgHiBlue).Sprint(ds[5:7])
|
||||
s += color.New(color.FgBlue).Sprint(ds[8:])
|
||||
s += color.New(color.FgHiBlue).Sprint(ts[0:2])
|
||||
s += color.New(color.FgBlue).Sprint(ts[2:4])
|
||||
s += color.New(color.FgHiBlue).Sprint(ts[4:6])
|
||||
s += color.New(color.FgBlue).Sprint(ts[7:])
|
||||
s += " "
|
||||
return
|
||||
}
|
||||
|
||||
// GetLoc returns the code location of the caller.
|
||||
func GetLoc(skip int) (output string) {
|
||||
_, file, line, _ := runtime.Caller(skip)
|
||||
output = color.Bit24(0, 128, 255, false).Sprint(
|
||||
file, ":", line,
|
||||
)
|
||||
output = fmt.Sprintf("%s:%d", file, line)
|
||||
return
|
||||
}
|
||||
|
||||
12
log/log.go
Normal file
12
log/log.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Package log is a convenience shortcut to use shorter names to access the lol.Logger.
|
||||
package log
|
||||
|
||||
import (
|
||||
"github.com/mleku/lol"
|
||||
)
|
||||
|
||||
var F, E, W, I, D, T lol.LevelPrinter
|
||||
|
||||
func init() {
|
||||
F, E, W, I, D, T = lol.Main.Log.F, lol.Main.Log.E, lol.Main.Log.W, lol.Main.Log.I, lol.Main.Log.D, lol.Main.Log.T
|
||||
}
|
||||
314
log_test.go
314
log_test.go
@@ -1,34 +1,298 @@
|
||||
package lol_test
|
||||
package lol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mleku/lol"
|
||||
)
|
||||
|
||||
var log, chk = lol.New(os.Stdout)
|
||||
func TestLogLevels(t *testing.T) {
|
||||
// Test that log levels are correctly ordered
|
||||
if !(Off < Fatal && Fatal < Error && Error < Warn && Warn < Info && Info < Debug && Debug < Trace) {
|
||||
t.Error("Log levels are not correctly ordered")
|
||||
}
|
||||
|
||||
func TestGetLogger(t *testing.T) {
|
||||
for i := 0; i < 100; i++ {
|
||||
lol.SetLogLevel(lol.Trace)
|
||||
log.T.Ln("testing log level", lol.LevelSpecs[lol.Trace].Name)
|
||||
log.D.Ln("testing log level", lol.LevelSpecs[lol.Debug].Name)
|
||||
log.I.Ln("testing log level", lol.LevelSpecs[lol.Info].Name)
|
||||
log.W.Ln("testing log level", lol.LevelSpecs[lol.Warn].Name)
|
||||
log.E.F("testing log level %s", lol.LevelSpecs[lol.Error].Name)
|
||||
log.F.Ln("testing log level", lol.LevelSpecs[lol.Fatal].Name)
|
||||
chk.F(errors.New("dummy error as fatal"))
|
||||
chk.E(errors.New("dummy error as error"))
|
||||
chk.W(errors.New("dummy error as warning"))
|
||||
chk.I(errors.New("dummy error as info"))
|
||||
chk.D(errors.New("dummy error as debug"))
|
||||
chk.T(errors.New("dummy error as trace"))
|
||||
log.I.Ln("log.I.Err",
|
||||
log.I.Err("format string %d '%s'", 5, "testing") != nil)
|
||||
log.I.Chk(errors.New("dummy information check"))
|
||||
log.I.Chk(nil)
|
||||
log.I.S("`backtick wrapped string`", t)
|
||||
// Test that LevelNames matches the constants
|
||||
expectedLevelNames := []string{
|
||||
"off", "fatal", "error", "warn", "info", "debug", "trace",
|
||||
}
|
||||
for i, name := range expectedLevelNames {
|
||||
if LevelNames[i] != name {
|
||||
t.Errorf("LevelNames[%d] = %s, want %s", i, LevelNames[i], name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetLogLevel(t *testing.T) {
|
||||
tests := []struct {
|
||||
level string
|
||||
expected int
|
||||
}{
|
||||
{"off", Off},
|
||||
{"fatal", Fatal},
|
||||
{"error", Error},
|
||||
{"warn", Warn},
|
||||
{"info", Info},
|
||||
{"debug", Debug},
|
||||
{"trace", Trace},
|
||||
{"unknown", Info}, // Default to Info for unknown levels
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(
|
||||
test.level, func(t *testing.T) {
|
||||
result := GetLogLevel(test.level)
|
||||
if result != test.expected {
|
||||
t.Errorf(
|
||||
"GetLogLevel(%q) = %d, want %d", test.level, result,
|
||||
test.expected,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetLogLevel(t *testing.T) {
|
||||
// Save original level
|
||||
originalLevel := Level.Load()
|
||||
defer SetLoggers(int(originalLevel)) // Restore original level after test
|
||||
|
||||
tests := []struct {
|
||||
level string
|
||||
expected int32
|
||||
}{
|
||||
{"off", Off},
|
||||
{"fatal", Fatal},
|
||||
{"error", Error},
|
||||
{"warn", Warn},
|
||||
{"info", Info},
|
||||
{"debug", Debug},
|
||||
{"trace", Trace},
|
||||
{"unknown", Trace}, // Should default to Trace for unknown levels
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(
|
||||
test.level, func(t *testing.T) {
|
||||
SetLogLevel(test.level)
|
||||
result := Level.Load()
|
||||
if result != test.expected {
|
||||
t.Errorf(
|
||||
"After SetLogLevel(%q), Level = %d, want %d",
|
||||
test.level, result, test.expected,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinStrings(t *testing.T) {
|
||||
tests := []struct {
|
||||
args []any
|
||||
expected string
|
||||
}{
|
||||
{[]any{}, ""},
|
||||
{[]any{"hello"}, "hello"},
|
||||
{[]any{"hello", "world"}, "hello world"},
|
||||
{[]any{1, 2, 3}, "1 2 3"},
|
||||
{[]any{1, "hello", 3.14}, "1 hello 3.14"},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Run(
|
||||
fmt.Sprintf("case_%d", i), func(t *testing.T) {
|
||||
result := JoinStrings(test.args...)
|
||||
if result != test.expected {
|
||||
t.Errorf(
|
||||
"JoinStrings(%v) = %q, want %q", test.args, result,
|
||||
test.expected,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetLoc(t *testing.T) {
|
||||
loc := GetLoc(1)
|
||||
if !strings.Contains(loc, "log_test.go") {
|
||||
t.Errorf("GetLoc(1) = %q, expected to contain 'log_test.go'", loc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPrinter(t *testing.T) {
|
||||
// Create a buffer to capture output
|
||||
var buf bytes.Buffer
|
||||
|
||||
// Set log level to Info
|
||||
originalLevel := Level.Load()
|
||||
Level.Store(int32(Info))
|
||||
defer Level.Store(originalLevel) // Restore original level
|
||||
|
||||
// Create a printer for Debug level
|
||||
printer := GetPrinter(int32(Debug), &buf, 1)
|
||||
|
||||
// Test Ln method - should not print because Debug > Info
|
||||
buf.Reset()
|
||||
printer.Ln("test message")
|
||||
if buf.String() != "" {
|
||||
t.Errorf(
|
||||
"printer.Ln() printed when level is too high: %q", buf.String(),
|
||||
)
|
||||
}
|
||||
|
||||
// Set log level to Debug
|
||||
Level.Store(int32(Debug))
|
||||
|
||||
// Test Ln method - should print now
|
||||
buf.Reset()
|
||||
printer.Ln("test message")
|
||||
output := buf.String()
|
||||
if output == "" {
|
||||
t.Error("printer.Ln() did not print when it should have")
|
||||
}
|
||||
if !strings.Contains(output, "test message") {
|
||||
t.Errorf(
|
||||
"printer.Ln() output %q does not contain 'test message'", output,
|
||||
)
|
||||
}
|
||||
|
||||
// Test F method
|
||||
buf.Reset()
|
||||
printer.F("formatted %s", "message")
|
||||
output = buf.String()
|
||||
if !strings.Contains(output, "formatted message") {
|
||||
t.Errorf(
|
||||
"printer.F() output %q does not contain 'formatted message'",
|
||||
output,
|
||||
)
|
||||
}
|
||||
|
||||
// Test S method
|
||||
buf.Reset()
|
||||
printer.S("spew message")
|
||||
output = buf.String()
|
||||
if !strings.Contains(output, "spew message") {
|
||||
t.Errorf(
|
||||
"printer.S() output %q does not contain 'spew message'", output,
|
||||
)
|
||||
}
|
||||
|
||||
// Test C method
|
||||
buf.Reset()
|
||||
printer.C(func() string { return "closure message" })
|
||||
output = buf.String()
|
||||
if !strings.Contains(output, "closure message") {
|
||||
t.Errorf(
|
||||
"printer.C() output %q does not contain 'closure message'", output,
|
||||
)
|
||||
}
|
||||
|
||||
// Test Chk method with nil error
|
||||
buf.Reset()
|
||||
result := printer.Chk(nil)
|
||||
if result != false {
|
||||
t.Error("printer.Chk(nil) returned true, expected false")
|
||||
}
|
||||
if buf.String() != "" {
|
||||
t.Errorf("printer.Chk(nil) printed output: %q", buf.String())
|
||||
}
|
||||
|
||||
// Test Chk method with error
|
||||
buf.Reset()
|
||||
testErr := errors.New("test error")
|
||||
result = printer.Chk(testErr)
|
||||
if result != true {
|
||||
t.Error("printer.Chk(error) returned false, expected true")
|
||||
}
|
||||
if !strings.Contains(buf.String(), "test error") {
|
||||
t.Errorf(
|
||||
"printer.Chk(error) output %q does not contain 'test error'",
|
||||
buf.String(),
|
||||
)
|
||||
}
|
||||
|
||||
// Test Err method
|
||||
buf.Reset()
|
||||
err := printer.Err("error %s", "message")
|
||||
if err == nil {
|
||||
t.Error("printer.Err() returned nil error")
|
||||
}
|
||||
if err.Error() != "error message" {
|
||||
t.Errorf(
|
||||
"printer.Err() returned error with message %q, expected 'error message'",
|
||||
err.Error(),
|
||||
)
|
||||
}
|
||||
// Check if the message was logged
|
||||
if !strings.Contains(buf.String(), "error message") {
|
||||
t.Errorf(
|
||||
"printer.Err() output %q does not contain 'error message'",
|
||||
buf.String(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNullPrinter(t *testing.T) {
|
||||
printer := GetNullPrinter()
|
||||
|
||||
// Test that Ln, F, S, C methods don't panic
|
||||
printer.Ln("test")
|
||||
printer.F("test %s", "format")
|
||||
printer.S("test")
|
||||
printer.C(func() string { return "test" })
|
||||
|
||||
// Test Chk method
|
||||
if !printer.Chk(errors.New("test")) {
|
||||
t.Error("GetNullPrinter().Chk(error) returned false, expected true")
|
||||
}
|
||||
if printer.Chk(nil) {
|
||||
t.Error("GetNullPrinter().Chk(nil) returned true, expected false")
|
||||
}
|
||||
|
||||
// Test Err method
|
||||
err := printer.Err("test %s", "error")
|
||||
if err == nil {
|
||||
t.Error("GetNullPrinter().Err() returned nil error")
|
||||
}
|
||||
if err.Error() != "test error" {
|
||||
t.Errorf(
|
||||
"GetNullPrinter().Err() returned error with message %q, expected 'test error'",
|
||||
err.Error(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
log, check, errorf := New(&buf, 1)
|
||||
|
||||
// Verify that all components are created
|
||||
if log == nil {
|
||||
t.Error("New() returned nil Log")
|
||||
}
|
||||
if check == nil {
|
||||
t.Error("New() returned nil Check")
|
||||
}
|
||||
if errorf == nil {
|
||||
t.Error("New() returned nil Errorf")
|
||||
}
|
||||
|
||||
// Test that the log functions work
|
||||
originalLevel := Level.Load()
|
||||
Level.Store(int32(Debug))
|
||||
defer Level.Store(originalLevel)
|
||||
|
||||
buf.Reset()
|
||||
log.D.Ln("test message")
|
||||
if !strings.Contains(buf.String(), "test message") {
|
||||
t.Errorf(
|
||||
"log.D.Ln() output %q doesn't contain 'test message'",
|
||||
buf.String(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user