logs
packageAPI reference for the logs
package.
Imports
(10)LogContext
LogContext represents a hierarchical logging context. Contexts can be
nested and will automatically generate a prefix based on their
parent/child relationship.
type LogContext struct
Methods
Prefix returns the context prefix in the form "parent:child". An empty context results in an empty string.
Returns
func (*LogContext) Prefix() string
{
if c == nil {
return ""
}
if c.Parent == nil {
return c.Name
}
parent := c.Parent.Prefix()
if parent == "" {
return c.Name
}
if c.Name == "" {
return parent
}
return parent + ":" + c.Name
}
Fields
| Name | Type | Description |
|---|---|---|
| Name | string | |
| Parent | *LogContext |
NewLogContext
NewLogContext creates a new LogContext optionally linked to a parent.
Parameters
Returns
func NewLogContext(name string, parent *LogContext) *LogContext
{
return &LogContext{Name: name, Parent: parent}
}
Logger
Logger represents a logger for the application.
type Logger struct
Methods
Parameters
Returns
func (*Logger) nextErrIndex(prefix string) int
{
l.mu.Lock()
defer l.mu.Unlock()
if l.ErrIndex == nil {
l.ErrIndex = make(map[string]int)
}
idx := l.ErrIndex[prefix]
l.ErrIndex[prefix] = idx + 1
return idx
}
InfoCtx logs an informational message using the provided context.
Parameters
func (*Logger) InfoCtx(ctx *LogContext, msg string)
{
prefix := ctx.Prefix()
formatted := fmt.Sprintf("%s:info:%s", prefix, msg)
l.File.Info().Msg(formatted)
l.Term.Info().Msg(formatted)
}
WarnCtx logs a warning message using the provided context.
Parameters
func (*Logger) WarnCtx(ctx *LogContext, msg string)
{
prefix := ctx.Prefix()
formatted := fmt.Sprintf("%s:warn:%s", prefix, msg)
l.File.Warn().Msg(formatted)
l.Term.Warn().Msg(formatted)
}
ErrorCtx logs an error message using the provided context. The error index is automatically incremented per-context to provide consistent progression.
Parameters
func (*Logger) ErrorCtx(ctx *LogContext, msg string)
{
prefix := ctx.Prefix()
idx := l.nextErrIndex(prefix)
formatted := fmt.Sprintf("%s:err(%d):%s", prefix, idx, msg)
l.File.Error().Msg(formatted)
l.Term.Error().Msg(formatted)
}
Info logs an informational message to both console and file.
Parameters
func (*Logger) Info(msg string)
{
l.File.Info().Msg(msg)
l.Term.Info().Msg(msg)
}
Infof logs a formatted informational message to both console and file.
Parameters
func (*Logger) Infof(format string, v ...any)
{
msg := fmt.Sprintf(format, v...)
l.File.Info().Msg(msg)
l.Term.Info().Msg(msg)
}
Warn logs a warning message to both console and file.
Parameters
func (*Logger) Warn(msg string)
{
l.File.Warn().Msg(msg)
l.Term.Warn().Msg(msg)
}
Warnf logs a formatted warning message to both console and file.
Parameters
func (*Logger) Warnf(format string, v ...any)
{
msg := fmt.Sprintf(format, v...)
l.File.Warn().Msg(msg)
l.Term.Warn().Msg(msg)
}
Error logs an error message to both console and file.
Parameters
func (*Logger) Error(msg string)
{
l.File.Error().Msg(msg)
l.Term.Error().Msg(msg)
}
Errorf logs a formatted error message to both console and file.
Parameters
func (*Logger) Errorf(format string, v ...any)
{
msg := fmt.Sprintf(format, v...)
l.File.Error().Msg(msg)
l.Term.Error().Msg(msg)
}
Debug logs a debug message to both console and file.
Parameters
func (*Logger) Debug(msg string)
{
l.File.Debug().Msg(msg)
l.Term.Debug().Msg(msg)
}
Debugf logs a formatted debug message to both console and file.
Parameters
func (*Logger) Debugf(format string, v ...any)
{
msg := fmt.Sprintf(format, v...)
l.File.Debug().Msg(msg)
l.Term.Debug().Msg(msg)
}
Trace logs a trace message to both console and file.
Parameters
func (*Logger) Trace(msg string)
{
l.File.Trace().Msg(msg)
l.Term.Trace().Msg(msg)
}
Tracef logs a formatted trace message to both console and file.
Parameters
func (*Logger) Tracef(format string, v ...any)
{
msg := fmt.Sprintf(format, v...)
l.File.Trace().Msg(msg)
l.Term.Trace().Msg(msg)
}
Fields
| Name | Type | Description |
|---|---|---|
| Term | log.Logger | |
| File | log.Logger | |
| mu | sync.Mutex | |
| ErrIndex | map[string]int |
getLogPath
getLogPath returns the path to the log directory, if the user is running as
root, the logs will be stored in /var/vlogs/, otherwise the logs will be
stored in ~/.vlogs.
Returns
func getLogPath() (string, error)
{
var logPath string
if os.Geteuid() == 0 {
logPath = "/var/vlogs/"
} else {
homeDir, err := os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("failed to get user home directory: %v", err)
}
logPath = filepath.Join(homeDir, ".vlogs")
}
// we have to create the directory if it doesn't exist
if _, err := os.Stat(logPath); os.IsNotExist(err) {
err := os.MkdirAll(logPath, 0755)
if err != nil {
return "", fmt.Errorf("failed to create log directory: %v", err)
}
}
return logPath, nil
}
NewLogger
NewLogger creates a new logger for the application, each logger has
a file logger and a console logger. The file logger is used to log
to the vlogs directory, while the console logger is used to log to
the console.
Parameters
Returns
func NewLogger(domain string) (Logger, error)
{
vLogger := Logger{}
vLogger.ErrIndex = make(map[string]int)
// preparing the file logger
logPath, err := getLogPath()
if err != nil {
return vLogger, err
}
vLogFile := filepath.Join(logPath, domain, "log.json")
vLogger.File = log.Logger{
Level: log.ParseLevel("info"),
Writer: &log.FileWriter{
Filename: vLogFile,
FileMode: 0600,
MaxSize: 500 * 1024 * 1024,
MaxBackups: 7,
EnsureFolder: true,
LocalTime: true,
TimeFormat: "15:04:05",
Cleaner: func(filename string, maxBackups int, matches []os.FileInfo) {
var dir = filepath.Dir(filename)
for i, fi := range matches {
filename := filepath.Join(dir, fi.Name())
switch {
case i > maxBackups:
os.Remove(filename)
case !strings.HasSuffix(filename, ".gz"):
go exec.Command("nice", "gzip", filename).Run()
}
}
},
},
}
// setting up the rotation for the file logger
runner := cron.New(cron.WithLocation(time.Local))
runner.AddFunc("0 0 * * *", func() { vLogger.File.Writer.(*log.FileWriter).Rotate() })
go runner.Run()
// preparing the console logger
vLogger.Term = log.Logger{
TimeFormat: "15:04:05",
Caller: 1,
Writer: &log.ConsoleWriter{
Formatter: formatLog,
EndWithMessage: true,
},
}
return vLogger, nil
}
Example
logger, err := logs.NewLogger(app)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
logger.File.Info().Msg("Batman reached the file logger")
logger.Console.Info().Msg("Batman reached the console logger")
since we use structured logging, we can also log with fields:
logger.File.Info().Str("where", "file").Msg("Batman is saving Gotham")
logger.Console.Info().Str("where", "console").Msg("Batman is saving Gotham")
Uses
formatLog
formatLog formats the log message with appropriate colors for log level
Parameters
Returns
func formatLog(w io.Writer, a *log.FormatterArgs) (int, error)
{
var color, three string
// Determine color and abbreviation for log level
switch a.Level {
case "trace":
color, three = Magenta, "TRC"
case "debug":
color, three = Yellow, "DBG"
case "info":
color, three = Green, "INF"
case "warn":
color, three = Red, "WRN"
case "error":
color, three = Red, "ERR"
case "fatal":
color, three = Red, "FTL"
case "panic":
color, three = Red, "PNC"
default:
color, three = Gray, "???"
}
// Format the log message
formattedLog := fmt.Sprintf("%s%s%s ", color, three, Reset)
formattedLog += fmt.Sprintf("%s>%s", Cyan, Reset)
formattedLog += fmt.Sprintf(" %s\n", a.Message)
return fmt.Fprint(w, formattedLog)
}