logs API

logs

package

API reference for the logs package.

S
struct

LogContext

LogContext represents a hierarchical logging context. Contexts can be
nested and will automatically generate a prefix based on their
parent/child relationship.

pkg/v1/logs/context.go:14-21
type LogContext struct

Methods

Prefix
Method

Prefix returns the context prefix in the form "parent:child". An empty context results in an empty string.

Returns

string
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
F
function

NewLogContext

NewLogContext creates a new LogContext optionally linked to a parent.

Parameters

name
string
parent

Returns

pkg/v1/logs/context.go:47-49
func NewLogContext(name string, parent *LogContext) *LogContext

{
	return &LogContext{Name: name, Parent: parent}
}
S
struct

Logger

Logger represents a logger for the application.

pkg/v1/logs/logger.go:19-30
type Logger struct

Methods

nextErrIndex
Method

Parameters

prefix string

Returns

int
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
Method

InfoCtx logs an informational message using the provided context.

Parameters

ctx *LogContext
msg string
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
Method

WarnCtx logs a warning message using the provided context.

Parameters

ctx *LogContext
msg string
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
Method

ErrorCtx logs an error message using the provided context. The error index is automatically incremented per-context to provide consistent progression.

Parameters

ctx *LogContext
msg string
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
Method

Info logs an informational message to both console and file.

Parameters

msg string
func (*Logger) Info(msg string)
{
	l.File.Info().Msg(msg)
	l.Term.Info().Msg(msg)
}
Infof
Method

Infof logs a formatted informational message to both console and file.

Parameters

format string
v ...any
func (*Logger) Infof(format string, v ...any)
{
	msg := fmt.Sprintf(format, v...)
	l.File.Info().Msg(msg)
	l.Term.Info().Msg(msg)
}
Warn
Method

Warn logs a warning message to both console and file.

Parameters

msg string
func (*Logger) Warn(msg string)
{
	l.File.Warn().Msg(msg)
	l.Term.Warn().Msg(msg)
}
Warnf
Method

Warnf logs a formatted warning message to both console and file.

Parameters

format string
v ...any
func (*Logger) Warnf(format string, v ...any)
{
	msg := fmt.Sprintf(format, v...)
	l.File.Warn().Msg(msg)
	l.Term.Warn().Msg(msg)
}
Error
Method

Error logs an error message to both console and file.

Parameters

msg string
func (*Logger) Error(msg string)
{
	l.File.Error().Msg(msg)
	l.Term.Error().Msg(msg)
}
Errorf
Method

Errorf logs a formatted error message to both console and file.

Parameters

format string
v ...any
func (*Logger) Errorf(format string, v ...any)
{
	msg := fmt.Sprintf(format, v...)
	l.File.Error().Msg(msg)
	l.Term.Error().Msg(msg)
}
Debug
Method

Debug logs a debug message to both console and file.

Parameters

msg string
func (*Logger) Debug(msg string)
{
	l.File.Debug().Msg(msg)
	l.Term.Debug().Msg(msg)
}
Debugf
Method

Debugf logs a formatted debug message to both console and file.

Parameters

format string
v ...any
func (*Logger) Debugf(format string, v ...any)
{
	msg := fmt.Sprintf(format, v...)
	l.File.Debug().Msg(msg)
	l.Term.Debug().Msg(msg)
}
Trace
Method

Trace logs a trace message to both console and file.

Parameters

msg string
func (*Logger) Trace(msg string)
{
	l.File.Trace().Msg(msg)
	l.Term.Trace().Msg(msg)
}
Tracef
Method

Tracef logs a formatted trace message to both console and file.

Parameters

format string
v ...any
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
F
function

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

string
error
pkg/v1/logs/logs.go:41-63
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
}
F
function

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

domain
string

Returns

error
pkg/v1/logs/logs.go:84-137
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")
F
function

formatLog

formatLog formats the log message with appropriate colors for log level

Parameters

Returns

int
error
pkg/v1/logs/logs.go:140-169
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)
}