
Product
Announcing Precomputed Reachability Analysis in Socket
Socket’s precomputed reachability slashes false positives by flagging up to 80% of vulnerabilities as irrelevant, with no setup and instant results.
github.com/go-pkgz/lgr
go get github.com/go-pkgz/lgr
l := lgr.New(lgr.Msec, lgr.Debug, lgr.CallerFile, lgr.CallerFunc) // allow debug and caller info, timestamp with milliseconds
l.Logf("INFO some important message, %v", err)
l.Logf("DEBUG some less important message, %v", err)
output looks like this:
2018/01/07 13:02:34.000 INFO {svc/handler.go:101 h.MyFunc1} some important message, can't open file myfile.xyz
2018/01/07 13:02:34.015 DEBUG {svc/handler.go:155 h.MyFunc2} some less important message, file is too small`
Without lgr.Caller*
it will drop {caller}
part
lgr
package provides a single interface lgr.L
with a single method Logf(format string, args ...interface{})
. Function wrapper lgr.Func
allows making lgr.L
from a function directly.lgr.New
(see "global logger")lgr.NoOp
(do-nothing logger) and lgr.Std
(passing directly to stdlib log)lgr.New
call accepts functional options:
lgr.Debug
- turn debug mode on to allow messages with "DEBUG" level (filtered otherwise)lgr.Trace
- turn trace mode on to allow messages with "TRACE" abd "DEBUG" levels both (filtered otherwise)lgr.Out(io.Writer)
- sets the output writer, default os.Stdout
lgr.Err(io.Writer)
- sets the error writer, default os.Stderr
lgr.CallerFile
- adds the caller file info (only affects lgr's native text format, not slog output)lgr.CallerFunc
- adds the caller function info (only affects lgr's native text format, not slog output)lgr.CallerPkg
- adds the caller package (only affects lgr's native text format, not slog output)lgr.LevelBraces
- wraps levels with "[" and "]"lgr.Msec
- adds milliseconds to timestamplgr.Format
- sets a custom template, overwrite all other formatting modifiers.lgr.Secret(secret ...)
- sets list of the secrets to hide from the logging outputs.lgr.Map(mapper)
- sets mapper functions to change elements of the logging output based on levels.lgr.StackTraceOnError
- turns on stack trace for ERROR level.lgr.SlogHandler(h slog.Handler)
- delegates logging to the provided slog handler.example: l := lgr.New(lgr.Debug, lgr.Msec)
Several predefined templates provided and can be passed directly to lgr.Format
, i.e. lgr.Format(lgr.WithMsec)
Short = `{{.DT.Format "2006/01/02 15:04:05"}} {{.Level}} {{.Message}}`
WithMsec = `{{.DT.Format "2006/01/02 15:04:05.000"}} {{.Level}} {{.Message}}`
WithPkg = `{{.DT.Format "2006/01/02 15:04:05.000"}} {{.Level}} ({{.CallerPkg}}) {{.Message}}`
ShortDebug = `{{.DT.Format "2006/01/02 15:04:05.000"}} {{.Level}} ({{.CallerFile}}:{{.CallerLine}}) {{.Message}}`
FuncDebug = `{{.DT.Format "2006/01/02 15:04:05.000"}} {{.Level}} ({{.CallerFunc}}) {{.Message}}`
FullDebug = `{{.DT.Format "2006/01/02 15:04:05.000"}} {{.Level}} ({{.CallerFile}}:{{.CallerLine}} {{.CallerFunc}}) {{.Message}}`
User can make a custom template and pass it directly to lgr.Format
. For example:
lgr.Format(`{{.Level}} - {{.DT.Format "2006-01-02T15:04:05Z07:00"}} - {{.CallerPkg}} - {{.Message}}`)
Note: formatter (predefined or custom) adds measurable overhead - the cost will depend on the version of Go, but is between 30
and 50% in recent tests with 1.12. You can validate this in your environment via benchmarks: go test -bench=. -run=Bench
lgr.Logf
recognize prefixes like INFO
or [INFO]
as levels. The full list of supported levels - TRACE
, DEBUG
, INFO
, WARN
, ERROR
, PANIC
and FATAL
.
TRACE
will be filtered unless lgr.Trace
option definedDEBUG
will be filtered unless lgr.Debug
or lgr.Trace
options definedINFO
and WARN
don't have any special behavior attachedERROR
sends messages to both out and err writersFATAL
and send messages to both out and err writers and exit(1)PANIC
does the same as FATAL
but in addition sends dump of callers and runtime info to err.Elements of the output can be altered with a set of user defined function passed as lgr.Map
options. Such a mapper changes
the value of an element (i.e. timestamp, level, message, caller) and has separate functions for each level. Note: both level
and messages elements handled by the same function for a given level.
A typical use-case is to produce colorful output with a user-define colorization library.
example with fatih/color:
colorizer := lgr.Mapper{
ErrorFunc: func(s string) string { return color.New(color.FgHiRed).Sprint(s) },
WarnFunc: func(s string) string { return color.New(color.FgHiYellow).Sprint(s) },
InfoFunc: func(s string) string { return color.New(color.FgHiWhite).Sprint(s) },
DebugFunc: func(s string) string { return color.New(color.FgWhite).Sprint(s) },
CallerFunc: func(s string) string { return color.New(color.FgBlue).Sprint(s) },
TimeFunc: func(s string) string { return color.New(color.FgCyan).Sprint(s) },
}
logOpts := []lgr.Option{lgr.Msec, lgr.LevelBraces, lgr.Map(colorizer)}
lgr
logger can be converted to io.Writer
, *log.Logger
, or slog.Handler
lgr.ToWriter(l lgr.L, level string) io.Writer
- makes io.Writer forwarding write ops to underlying lgr.L
lgr.ToStdLogger(l lgr.L, level string) *log.Logger
- makes standard logger on top of lgr.L
lgr.ToSlogHandler(l lgr.L) slog.Handler
- converts lgr.L to a slog.Handler for use with sloglevel
parameter is optional, if defined (non-empty) will enforce the level.
lgr.SetupStdLogger(opts ...Option)
initializes std global logger (log.std
) with lgr logger and given options.
All standard methods like log.Print
, log.Println
, log.Fatal
and so on will be forwarder to lgr.lgr.SetupWithSlog(logger *slog.Logger)
sets up the global logger with a slog logger.In addition to the standard logger interface, lgr provides seamless integration with Go's log/slog
package:
// Create lgr logger
lgrLogger := lgr.New(lgr.Debug, lgr.Msec)
// Convert to slog handler and create slog logger
handler := lgr.ToSlogHandler(lgrLogger)
logger := slog.New(handler)
// Use standard slog API with lgr formatting
logger.Info("message", "key1", "value1")
// Output: 2023/09/15 10:34:56.789 INFO message key1="value1"
// Create slog handler
jsonHandler := slog.NewJSONHandler(os.Stdout, nil)
// Wrap it with lgr interface
logger := lgr.FromSlogHandler(jsonHandler)
// Use lgr API with slog backend
logger.Logf("INFO message with %s", "structured data")
// Output: {"time":"2023-09-15T10:34:56.789Z","level":"INFO","msg":"message with structured data"}
// Create a logger that uses slog directly
jsonHandler := slog.NewJSONHandler(os.Stdout, nil)
logger := lgr.New(lgr.SlogHandler(jsonHandler))
// Use lgr API with slog backend
logger.Logf("INFO message")
// Output: {"time":"2023-09-15T10:34:56.789Z","level":"INFO","msg":"message"}
To get caller information in JSON output when using slog handlers, create the handler with AddSource: true
:
// Create JSON handler with source information (caller info)
jsonHandler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
AddSource: true, // This enables caller information in JSON output
})
// Use handler with lgr
logger := lgr.New(lgr.SlogHandler(jsonHandler))
logger.Logf("INFO message with caller info")
// Output will include source file, line and function in JSON
Note: The lgr caller options (lgr.CallerFile
, lgr.CallerFunc
, lgr.CallerPkg
) only work with lgr's native text format
and don't affect JSON output from slog handlers. To include caller information in JSON logs:
AddSource: true
as shown aboveThis behavior is designed to respect each logging system's conventions for representing caller information.
Users should avoid global logger and pass the concrete logger as a dependency. However, in some cases a global logger may be needed, for example migration from stdlib log
to lgr
. For such cases log "github.com/go-pkgz/lgr"
can be imported instead of log
package.
Global logger provides lgr.Printf
, lgr.Print
and lgr.Fatalf
functions. User can customize the logger by calling lgr.Setup(options ...)
. The instance of this logger can be retrieved with lgr.Default()
FAQs
Unknown package
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Product
Socket’s precomputed reachability slashes false positives by flagging up to 80% of vulnerabilities as irrelevant, with no setup and instant results.
Product
Socket is launching experimental protection for Chrome extensions, scanning for malware and risky permissions to prevent silent supply chain attacks.
Product
Add secure dependency scanning to Claude Desktop with Socket MCP, a one-click extension that keeps your coding conversations safe from malicious packages.