Categorygithub.com/effective-security/xlog
modulepackage
0.9.39
Repository: https://github.com/effective-security/xlog.git
Documentation: pkg.go.dev

# README

Coverage Status

xlog logging package

Cloned from https://github.com/coreos/pkg/tree/master/capnslog

This clone has slight modifications on the original code, adding ability to specify log lever per package, and exposing Logger interface, not an implementation structure.

In this implementation the DEBUG level is above TRACE as trace is used to trace important functions calls and maybe enable on the cloud more friequently than DEBUG

How to use

var logger = xlog.NewPackageLogger("github.com/yourorg/yourrepo", "yourpackage")

logger.KV(xlog.INFO, "version", v1, "any", override)

How to configure

	if withStackdriver {
		formatter := stackdriver.NewFormatter(os.Stderr, cfg.Logs.LogsName)
		xlog.SetFormatter(formatter)
	} else {
		formatter := xlog.NewColorFormatter(os.Stderr, true)
		xlog.SetFormatter(formatter)
	}

Set log level for different packages

Config example:

log_levels: 
  - repo: "*"
    level: INFO
  - repo: github.com/effective-security/server
    package: "*"
    level: TRACE

Configuration at start up:

	// Set log levels for each repo
	if cfg.LogLevels != nil {
		for _, ll := range cfg.LogLevels {
			l, _ := xlog.ParseLevel(ll.Level)
			if ll.Repo == "*" {
				xlog.SetGlobalLogLevel(l)
			} else {
				xlog.SetPackageLogLevel(ll.Repo, ll.Package, l)
			}
			logger.Infof("logger=%q, level=%v", ll.Repo, l)
		}
	}

Need to log to files?

This example shows how to use with logrotate package

	if cfg.Logs.Directory != "" && cfg.Logs.Directory != nullDevName {
		os.MkdirAll(cfg.Logs.Directory, 0644)

		var sink io.Writer
		if flags.isStderr {
			// This will allow to also print the logs on stderr
			sink = os.Stderr
			xlog.SetFormatter(xlog.NewColorFormatter(sink, true))
		} else {
			// do not redirect stderr to our log files
			log.SetOutput(os.Stderr)
		}

		logRotate, err := logrotate.Initialize(cfg.Logs.Directory, cfg.ServiceName, cfg.Logs.MaxAgeDays, cfg.Logs.MaxSizeMb, true, sink)
		if err != nil {
			logger.Errorf("reason=logrotate, folder=%q, err=[%+v]", cfg.Logs.Directory, err)
			return errors.WithMessage(err, "failed to initialize log rotate")
		}
		// Close logRotate when application terminates
		app.OnClose(logRotate)
	}

Design Principles

package main is the place where logging gets turned on and routed

A library should not touch log options, only generate log entries. Libraries are silent until main lets them speak.

All log options are runtime-configurable

Still the job of main to expose these configurations. main may delegate this to, say, a configuration webhook, but does so explicitly.

There is one log object per package. It is registered under its repository and package name

main activates logging for its repository and any dependency repositories it would also like to have output in its logstream. main also dictates at which level each subpackage logs.

There is one output stream, and it is an io.Writer composed with a formatter

Splitting streams is probably not the job of your program, but rather, your log aggregation framework. If you must split output streams, again, main configures this and you can write a very simple two-output struct that satisfies io.Writer.

Fancy colorful formatting and JSON output are beyond the scope of a basic logging framework -- they're application/log-collector dependant. These are, at best, provided as options, but more likely, provided by your application.

Log objects are an interface

An object knows best how to print itself. Log objects can collect more interesting metadata if they wish, however, because text isn't going away anytime soon, they must all be marshalable to text. The simplest log object is a string, which returns itself. If you wish to do more fancy tricks for printing your log objects, see also JSON output -- introspect and write a formatter which can handle your advanced log interface. Making strings is the only thing guaranteed.

Log levels have specific meanings:

  • CRITICAL: Unrecoverable. Must fail.
  • ERROR: Data has been lost, a request has failed for a bad reason, or a required resource has been lost
  • WARNING: (Hopefully) Temporary conditions that may cause errors, but may work fine. A replica disappearing (that may reconnect) is a warning.
  • NOTICE: Normal, but important (uncommon) log information.
  • INFO: Normal, working log information, everything is fine, but helpful notices for auditing or common operations.
  • TRACE: Anything goes, from logging every function call as part of a common operation, to tracing execution of a query.
  • DEBUG: Print debug data.

# Packages

Package logrotate implements additional functionality for io writers & closers.
No description provided by the author

# Functions

Caller returns caller function name, and location.
ContextEntries returns log entries.
ContextWithKV returns context with values to be added to logs, entries in "key1=value1, ..., keyN=valueN" format.
EscapedString returns string value stuitable for logging.
GetFormatter returns current formatter.
GetRepoLevels returns currently configured levels.
GetRepoLogger may return the handle to the repository's set of packages' loggers.
MustRepoLogger returns the handle to the repository's packages' loggers.
NewDefaultFormatter returns an instance of default formatter.
NewJSONFormatter returns an instance of JsonFormatter.
NewNilFormatter is a helper to produce a new LogFormatter struct.
NewNilLogger creates new nil logger.
NewPackageLogger creates a package logger object.
NewPrettyFormatter returns an instance of PrettyFormatter.
NewStringFormatter returns string-based formatter.
OnError allows to specify a callback for ERROR levels.
ParseLevel translates some potential loglevel strings into their corresponding levels.
SetFormatter sets the formatting function for all logs.
SetGlobalLogLevel sets the log level for all packages in all repositories registered with PackageLogger.
SetPackageLogLevel sets the log level for a package in repo logger.
SetRepoLevel sets repo log level.
SetRepoLevels sets repo log levels per package.
SetRepoLogLevel sets the log level for all packages in repo logger.

# Constants

CRITICAL is the lowest log level; only errors which will end the program will be propagated.
DEBUG is the default hidden level for more verbose updates about internal processes.
ERROR is for errors that are not fatal but lead to troubling behavior.
FormatNoCaller disables log the caller.
FormatPrintEmpty allows to print empty values.
FormatSkipLevel allows to configure skipping the level log.
FormatSkipTime allows to configure skipping the time log.
FormatWithCaller allows to configure if the caller shall be logged.
FormatWithColor allows to print color logs.
FormatWithLocation allows to print the file:line for each log.
INFO is a log level for common, everyday log updates.
NOTICE is for normal but significant conditions.
TRACE is for (potentially) call by call tracing of programs.
WARNING is for errors which are not fatal and not errors, but are unusual.

# Variables

color pallete map.
ExitFunc can be overriten.
LevelColors provides colors map.
Stderr is an instance of standard logger to Stderr.
TimeNowFn to override in unit tests.

# Structs

JSONFormatter provides default logs format.
NilFormatter is a no-op log formatter that does nothing.
NilLogger does not produce any output.
PackageLogger is logger implementation for packages.
PrettyFormatter provides default logs format.
RepoLogLevel contains information about the log level per repo.
StringFormatter defines string-based formatter.

# Interfaces

Formatter defines an interface for formatting logs.
KeyValueLogger interface for generic logger.
Logger interface for generic logger.
StdLogger interface for generic logger.

# Type aliases

FormatterOption specifies additional formatter options.
LogLevel is the set of all log levels.
OnErrorFn allows to be called when an error is logged in a package.
RepoLogger specifies a map of repo => PackageLogger.