gologsgo

package module
v0.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 21, 2019 License: MIT Imports: 10 Imported by: 0

README

Go Logs Go

A leveled logger for go with targeted configuration.

Installation

As outlined in https://blog.golang.org/using-go-modules add

import (
  ...
  "github.com/big-squid/go-logs-go"
  ...
)

to your project and run go mod tidy or a build.

Usage

Hard-coded Log Configuration
package main

import (
	logs "github.com/big-squid/go-logs-go"
)

var logger *logs.Logger

func init() {
	logger = logs.New(&logs.RootLogConfig{
		Label: "main",
		Level: logs.Debug,
		Loggers: map[string]*logs.LogConfig{
			"counter": &logs.LogConfig{
				Level: logs.Info,
			},
		},
	})
}

func counter() {
	log := logger.ChildLogger("counter")
	for i := 0; i < 5; i++ {
		log.Warn("Loop %v", i)
	}
}

func main() {
	log := logger
	log.Trace("This will only appear if the level is TRACE")
	log.Debug("Debug msg")
	log.Info("Info msg")

	counter()
}

This example demonstrates the basic usage of a go-logs-go logger:

  1. A root Logger is created using logs.New()
  2. logs.New() takes a *RootLogConfig{} that specifies
  • a log Level indicating which log messages should be written to the logs
  • an optional Label to include in all log messages
  • optional child logger configuration Loggers in a map[string]*LogConfig
  1. Child loggers may be created using logger.ChildLogger(), which requires a name. The name will be used to:
  • create a label, by appending it to the parent logger's label
  • find the child logger's configuration in it's parent logger's Logger's map. The logger's Level may be supplied in this configuration. If not, the parent logger's level will be used.
  1. Loggers export log level functions for logging at a particular level. Log level functions exist for Trace(), Debug(), Info(), Warn(), and Error(). Each of these will generate a log message at the log level that matches their name if the logger's level is less than or equal to that level.
Config-only Log Levels

Astute observes will notice 3 LogLevel constants that do not map to a log level function. These are use only for configuration. They are:

  1. logs.All - this indicates that all log messages should be written to the logs
  2. logs.Off - this indicates that no log messages should be written to the logs
  3. logs.NotSet - this indicates that a log level has not been set for a given logger and the logger should inherit it's parent's log level or use the default logs.Info if no parent exists. This log level is the "zero value" for the LogLevel constants.
Ways to get a RootLogConfig

It's very unlikely that you actually want to hard code your log configuration. go-logging provides several methods for retrieving a configuration from outside the code. Log levels should be set using the case insensitive string equivalent of the constant name.

JsonConfig
cfg, err := logging.JsonConfig([]byte(`
	{ "level": "ERROR",
      "loggers": {
        "main": {
          "level": "INFO",
          "loggers": {
            "child": {
              "level": "DEBUG"
            }
          }
        }
      }
    }
`))

if nil != err {
  panic(err)
}

logger := logs.New(cfg)

JsonConfig() takes JSON as a []byte and Marshalls it in to a *RootLogConfig. It is used by the other configuration functions.

FileConfig

FileConfig() reads a file path and creates a *RootLogConfig{} from it's json data

cfg, err := logs.FileConfig("./log-config.json")
if nil != err {
  panic(err)
}

logger := logs.New(cfg)
PathEnvConfig

PathEnvConfig() gets a file path from the specified environment variable, reads it's contents and creates a *RootLogConfig from it's json data

cfg, err := logs.PathEnvConfig("LOG_CONFIG_PATH")
if nil != err {
  panic(err)
}

logger := logs.New(cfg)
EnvPrefixConfig

EnvPrefixConfig() finds all of the environment variables that start with a specified prefix and uses them to build a *RootLogConfig{}. After the prefix, a single underscore ("_") is treated as a word separator. Two successive underscores ("__") are treated as a struct separator - the left side is the name of the parent struct, the right is a field name. Environment variables that appear to be JSON (because they start with a curly brace - "{") will attempt to be parsed as JSON.

// LOG_CONFIG_LEVEL="ERROR"
// LOG_CONFIG_LABEL="_root_"
// LOG_CONFIG_LOGGERS__CHILD__LEVEL="DEBUG"
// LOG_CONFIG_LOGGERS__CHILD__GRANDCHILD__LEVEL="TRACE"
// LOG_CONFIG_LOGGERS__JSON_CHILD="{\"level\": \"WARN\", \"loggers\": {\"grandchild\": {\"level\": \"ERROR\"}}}"
  
cfg, err := logs.EnvPrefixConfig("LOG_CONFIG")
if nil != err {
  panic(err)
}

logger := logs.New(cfg)
Advanced Usage

It is possible to further customize the logs written by a go-logs-go logger as well as where and how they are written by specifying a LogHandler function. For now, interested parties should review the implementation of the DefaultLogHandler in the source code.

Shhh! Don't tell anyone!

Of particular note, the DefaultLogHandler uses log.PrintLn() to write log messages. (This is how our log output contains timestamps without them being included in the LogMessage struct.) Using the base "log" package in this way allows the user to make use of log.SetOutput() and log.SetFlags() if desired - though this is considered a private implementation detail. Users who want backward compatibility guarantees should implement their own LogHandler instead.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var LogLevels orderedLogLevels

Functions

func DefaultLogHandler

func DefaultLogHandler(msg LogMessage)

DefaultLogHandler is a LogHandler that writes color coded log messages to stdout with UTC timestamps.

Types

type Formatter

type Formatter func(string, ...interface{}) string

type LeveledLogHandler

type LeveledLogHandler struct {
	Format     string
	RootFormat string
	Levels     map[LogLevel]Formatter
}

func (*LeveledLogHandler) LogHandler

func (h *LeveledLogHandler) LogHandler(msg LogMessage)

type LogConfig

type LogConfig struct {
	Loggers map[string]*LogConfig `json:"loggers"`
	Level   LogLevel              `json:"level"`
}

type LogHandler

type LogHandler func(LogMessage)

LogHandler receives a LogMessage and ensures it is properly written to the logs. Most consumers of this package will want to use the DefaultLogHandler to write color coded log messages to stdout with timestamps.

type LogLevel

type LogLevel int
const (
	NotSet LogLevel = iota
	All
	Trace
	Debug
	Info
	Warn
	Error
	Off
)

Log Constants NotSet is literally our "zero value" NOTE: go does _not_ recommend using ALL_CAPS for constants, as these would always be exported (see https://stackoverflow.com/questions/22688906/go-naming-conventions-for-const)

func (*LogLevel) UnmarshalJSON

func (ll *LogLevel) UnmarshalJSON(b []byte) error

type LogMessage

type LogMessage struct {
	Level      LogLevel
	LevelLabel string
	Logger     string
	Message    string
}

LogMessage structs will be passed by the logger to the configured LogHandler each time a logging function (`.Trace()`, `.Debug()`,`.Info()`,`.Warn()`, `.Error()`) is called.

type Logger

type Logger struct {
	// contains filtered or unexported fields
}

Logger is the primary structure in this package. It supplies the log level functions. A Logger only has a `parent` if it was created by Logger.ChildLogger(). If so, it's `logConfig` will be a reference to it's config from the parent - the only place it can get a config.

func New

func New(logConfig *RootLogConfig) *Logger

New returns a new root Logger

func (*Logger) ChildLogger

func (logger *Logger) ChildLogger(name string) *Logger

ChildLogger returns a Logger that takes it's configuration from the Logger it was created from. ChildLogger's are named so that configuration can be applied specifically to them. The name of a ChildLogger is also used in it's label along with it's parent's label.

func (*Logger) Debug

func (logger *Logger) Debug(format string, args ...interface{})

Debug logs a message at the DEBUG level

func (*Logger) Error

func (logger *Logger) Error(format string, args ...interface{})

Error logs a message at the ERROR level

func (*Logger) Info

func (logger *Logger) Info(format string, args ...interface{})

Info logs a message at the INFO level

func (*Logger) Label

func (logger *Logger) Label() string

Label returns the label of the logger

func (*Logger) Level

func (logger *Logger) Level() LogLevel

Level returns the effective log level of the Logger below which log messages will be ignored

func (*Logger) PackageLogger

func (logger *Logger) PackageLogger(opts ...PackageLoggerOpts) *Logger

PackageLogger returns a ChildLogger using the basename of the package path of the caller as the name. This allows targetting a package logger in configuration by package name. It is recommended that PackageLogger() only be used when initializing a package. NOTE: the basename of the package path is more readily available at runtime than the actual package name (see https://golang.org/pkg/runtime/#example_Frames), but for well-named packages (see https://blog.golang.org/package-names) should be the same.

func (*Logger) Trace

func (logger *Logger) Trace(format string, args ...interface{})

Trace logs a message at the TRACE level

func (*Logger) Warn

func (logger *Logger) Warn(format string, args ...interface{})

Warn logs a message at the WARN level

type PackageLoggerOpts

type PackageLoggerOpts struct {
	Skip int
}

PackageLoggerOpts allows callers of PackageLogger() to specify options. Currently the only supportted option is Skip, which tells PackageLogger() to skip additional stack frames that shouldn't be included when determining the calling package path.

type RootLogConfig

type RootLogConfig struct {
	Loggers map[string]*LogConfig `json:"loggers"`
	Level   LogLevel              `json:"level"`
	Label   string                `json:"label"`
	// Don't try to Marshall/Unmarshall a function
	LogHandler LogHandler `json:"-"`
}

func EnvPrefixConfig

func EnvPrefixConfig(prefix string) (*RootLogConfig, error)

EnvPrefixConfig finds all the environment variables that start with a specified prefix and uses them to build a RootLogConfig. After the prefix, a single underscore ("_") is treated as a word seperator. Two successive underscores ("__") are treated as a struct seperator - the left side is the parent struct, the right is a field name.

func FileConfig

func FileConfig(configFile string) (*RootLogConfig, error)

FileConfig reads a file path and creates a RootLogConfig from it's JSON data

func JsonConfig

func JsonConfig(data []byte) (*RootLogConfig, error)

JsonConfig creates a RootLogConfig from JSON data

func PathEnvConfig

func PathEnvConfig(env string) (*RootLogConfig, error)

PathEnvConfig gets a file path from the specified environment variable, reads it's contents and creates a RootLogConfig from it's JSON data

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL