log

package
v0.0.6 Latest Latest
Warning

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

Go to latest
Published: May 4, 2018 License: MIT Imports: 12 Imported by: 54

README

Log

Convention

  • library/application MUST have a library/application logger as registry.
  • every package MUST have a package level logger.
  • logger is a registry and can contain children.
  • instance of struct should have their own logger as children of package logger

Usage

package logutil

import (
	"github.com/dyweb/gommon/log"
)

var Registry = log.NewLibraryLogger()

func NewPackageLogger() *log.Logger {
	l := log.NewPackageLoggerWithSkip(1)
	Registry.AddChild(l)
	return l
}
var log = logutil.NewPackageLogger()

func foo() {
	// structual way
	log.DebugF("open", dlog.Fields{"file": file})
	// default handler
	// debug 20180204 open file=test.yml
	// logfmtish handler
	// lvl=debug t=20180204 msg=open file=test.yml
	// json handler
	// {"lvl": "debug", "t": "20180204", "msg": "open", "file": "test.yml"}
	// traditional way
	log.Debugf("open %s", file)
	// debug 20180204 open test.yml
	// a mixed way, this would lose hint from IDE for printf placeholders
	log.DebugFf(dlog.Fields{"file": file}, "open with error %v", err)
	// default handler
	
	// for expensive operation, check before log
	if log.IsDebugEnabled() {
		log.Debug("counter". dlog.Fields{"counter": CountExpensive()})
	}
}

Documentation

Overview

Package log provides structured logging with fine grained control over libraries using a tree hierarchy of loggers.

Conventions

1. no direct use of the log package, MUST create new logger.

2. library/application MUST have a library/application logger as their registry.

3. every package MUST have a package level logger as child of the registry, normally defined in pkg.go

4. logger is a registry and can contain children.

5. instance of struct should have their own logger as children of package logger

Index

Examples

Constants

View Source
const MagicPackageLoggerFunctionName = "init"
View Source
const MagicStructLoggerFunctionName = "LoggerIdentity"

Variables

View Source
var UnknownIdentity = Identity{Package: "unk", Type: UnknownLogger}

Functions

func DisableSourceRecursive added in v0.0.4

func DisableSourceRecursive(root *Logger)

func EnableSourceRecursive added in v0.0.5

func EnableSourceRecursive(root *Logger)

FIXME: this fixed typo requires update in go.ice

func PreOrderDFS

func PreOrderDFS(root *Logger, visited map[*Logger]bool, cb func(l *Logger))

TODO: test it .... map traverse order is random, we need radix tree, it is need for pretty print as well

func SetHandlerRecursive

func SetHandlerRecursive(root *Logger, handler Handler)

func SetLevelRecursive

func SetLevelRecursive(root *Logger, level Level)

func ToStringTree

func ToStringTree(root *Logger) *structure.StringTreeNode

Types

type Field

type Field struct {
	Key       string
	Type      FieldType
	Int       int64
	Str       string
	Interface interface{}
}

Field is based on uber-go/zap https://github.com/uber-go/zap/blob/master/zapcore/field.go It can be treated as a Union, the value is stored in either Int, Str or Interface

func Int added in v0.0.3

func Int(k string, v int) Field

Int creates a field with int value, it uses int64 internally

func Str added in v0.0.3

func Str(k string, v string) Field

Str creates a field with string value

func Stringer added in v0.0.3

func Stringer(k string, v fmt.Stringer) Field

Stringer calls the String() method and stores return value

type FieldType added in v0.0.3

type FieldType uint8

FieldType avoids calling reflection

const (
	UnknownType FieldType = iota
	IntType
	StringType
)

type Fields

type Fields []Field

Fields is a slice of Field

type Handler

type Handler interface {
	// HandleLog accepts level, log time, formatted log message
	HandleLog(level Level, time time.Time, msg string)
	// HandleLogWithSource accepts formatted source line of log i.e., http.go:13
	// TODO: pass frame instead of string so handler can use trace for error handling?
	HandleLogWithSource(level Level, time time.Time, msg string, source string)
	// HandleLogWithFields accepts fields with type hint,
	// implementation should inspect the type field instead of using reflection
	// TODO: pass pointer for fields?
	HandleLogWithFields(level Level, time time.Time, msg string, fields Fields)
	// HandleLogWithSourceFields accepts both source and fields
	HandleLogWithSourceFields(level Level, time time.Time, msg string, source string, fields Fields)
	// Flush writes the buffer to underlying storage
	Flush()
}

Handler formats log message and writes to underlying storage, stdout, file, remote server etc. It MUST be thread safe because logger calls handler concurrently without any locking. There is NO log entry struct in gommon/log, which is used in many logging packages, the reason is if extra field is added to the interface, compiler would throw error on stale handler implementations.

func DefaultHandler

func DefaultHandler() Handler

DefaultHandler returns the singleton defaultHandler instance, which logs to stdout in text format

func NewIOHandler added in v0.0.3

func NewIOHandler(w io.Writer) Handler

NewIOHandler

type IOHandler added in v0.0.5

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

IOHandler writes log to io.Writer, default handler uses os.Stderr

func (*IOHandler) Flush added in v0.0.5

func (h *IOHandler) Flush()

Flush implements Handler interface

func (*IOHandler) HandleLog added in v0.0.5

func (h *IOHandler) HandleLog(level Level, time time.Time, msg string)

HandleLog implements Handler interface

func (*IOHandler) HandleLogWithFields added in v0.0.5

func (h *IOHandler) HandleLogWithFields(level Level, time time.Time, msg string, fields Fields)

HandleLogWithFields implements Handler interface

func (*IOHandler) HandleLogWithSource added in v0.0.5

func (h *IOHandler) HandleLogWithSource(level Level, time time.Time, msg string, source string)

HandleLogWithSource implements Handler interface

func (*IOHandler) HandleLogWithSourceFields added in v0.0.5

func (h *IOHandler) HandleLogWithSourceFields(level Level, time time.Time, msg string, source string, fields Fields)

HandleLogWithSourceFields implements Handler interface

type Identity

type Identity struct {
	Package  string
	Function string
	Struct   string
	File     string
	Line     int
	Type     LoggerType
}

Identity is set based on logger's initialization location, it is close to, but NOT exactly same as location of actual log. It is used for applying filter rules and print logger hierarchy.

func NewIdentityFromCaller

func NewIdentityFromCaller(skip int) *Identity

TODO: document all the black magic here ... https://github.com/dyweb/gommon/issues/32

func (*Identity) Diff

func (id *Identity) Diff(parent *Identity) string

TODO: this is used for print tree like structure ... it's hard to maintain exact parent and child logger due to cycle import

func (*Identity) Hash

func (id *Identity) Hash() uint64

func (*Identity) SourceLocation

func (id *Identity) SourceLocation() string

func (*Identity) String

func (id *Identity) String() string

type Level

type Level uint8

Level is log level TODO: allow change default logging level at compile time

Example
fmt.Println(FatalLevel.String())
fmt.Println(FatalLevel.AlignedUpperString())
Output:

fatal
FATA
const (
	// FatalLevel log error and call `os.Exit(1)`
	// TODO: allow user to add hooks before calling os.Exit?
	FatalLevel Level = iota
	// PanicLevel log error and call `panic`
	PanicLevel
	// ErrorLevel log error and do nothing
	// TODO: add integration with errors package
	ErrorLevel
	// WarnLevel log warning that is often ignored
	WarnLevel
	// InfoLevel log info
	InfoLevel
	// DebugLevel log debug message, user should enable DebugLevel logging when report bug
	DebugLevel
	// TraceLevel is very verbose, user should enable it only on packages they are currently investing instead of globally
	// TODO: add compile flag to use empty trace logger implementation to eliminate the call at runtime
	TraceLevel
)

func (Level) AlignedUpperString added in v0.0.5

func (level Level) AlignedUpperString() string

AlignedUpperString returns fixed length level string in uppercase

func (Level) ColoredAlignedUpperString added in v0.0.4

func (level Level) ColoredAlignedUpperString() string

ColoredAlignedUpperString returns fixed length level string in uppercase, wrapped by terminal color characters, only works on *nix

func (Level) ColoredString added in v0.0.3

func (level Level) ColoredString() string

ColoredString returns level string wrapped by terminal color characters, only works on *nix

func (Level) String

func (level Level) String() string

type LoggableStruct

type LoggableStruct interface {
	GetLogger() *Logger
	SetLogger(logger *Logger)
	LoggerIdentity(justCallMe func() *Identity) *Identity
}

LoggableStruct is used to inject a logger into the struct, the methods for the interface can and should be generated using gommon.

In struct initializer call dlog.NewStruct(pkgLogger, structInstancePointer)

type Logger

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

Logger is a concrete type instead of interface because most logic is in handler. There is NO lock when calling logging methods, handlers may have locks. Lock is used when updating logger attributes like Level.

For Printf style logging (Levelf), Logger formats string using fmt.Sprintf before passing it to handlers.

logger.Debugf("id is %d", id)

For structual logging (LevelF), Logger passes fields to handlers without any processing.

logger.DebugF("hi", log.Fields{log.Str("foo", "bar")})

If you want to mix two styles, call fmt.Sprintf before calling DebugF,

logger.DebugF(fmt.Sprintf("id is %d", id), log.Fields{log.Str("foo", "bar")})

func NewApplicationLogger

func NewApplicationLogger() *Logger
Example
logger := NewApplicationLogger()
fmt.Println(logger.Identity().Package)
fmt.Println(logger.Identity().Type)
Output:

github.com/dyweb/gommon/log
app

func NewFunctionLogger

func NewFunctionLogger(packageLogger *Logger) *Logger

func NewLibraryLogger

func NewLibraryLogger() *Logger

func NewMethodLogger

func NewMethodLogger(structLogger *Logger) *Logger

func NewPackageLogger

func NewPackageLogger() *Logger

func NewPackageLoggerWithSkip

func NewPackageLoggerWithSkip(skip int) *Logger

func NewStructLogger

func NewStructLogger(packageLogger *Logger, loggable LoggableStruct) *Logger

func (*Logger) AddChild

func (l *Logger) AddChild(child *Logger)

TODO: allow release a child logger, this will be a trouble if we created 1,000 Client struct with its own logger...

func (*Logger) Debug

func (l *Logger) Debug(args ...interface{})

func (*Logger) DebugF added in v0.0.3

func (l *Logger) DebugF(msg string, fields Fields)

func (*Logger) Debugf

func (l *Logger) Debugf(format string, args ...interface{})

func (*Logger) DisableSource added in v0.0.3

func (l *Logger) DisableSource()

func (*Logger) EnableSource added in v0.0.3

func (l *Logger) EnableSource()

func (*Logger) Error

func (l *Logger) Error(args ...interface{})

func (*Logger) ErrorF added in v0.0.3

func (l *Logger) ErrorF(msg string, fields Fields)

func (*Logger) Errorf

func (l *Logger) Errorf(format string, args ...interface{})

func (*Logger) Fatal

func (l *Logger) Fatal(args ...interface{})

Fatal calls os.Exit(1) after it writes and flushes the log

func (*Logger) FatalF added in v0.0.3

func (l *Logger) FatalF(msg string, fields Fields)

FatalF duplicates instead of calling Fatal to keep source line correct

func (*Logger) Fatalf

func (l *Logger) Fatalf(format string, args ...interface{})

Fatalf duplicates instead of calling Fatal to keep source line correct

func (*Logger) Identity

func (l *Logger) Identity() *Identity

Identity returns the identity set when the logger is created. NOTE: caller can modify the identity because all fields are public, but they should NOT do this

func (*Logger) Info

func (l *Logger) Info(args ...interface{})

func (*Logger) InfoF added in v0.0.3

func (l *Logger) InfoF(msg string, fields Fields)

func (*Logger) Infof

func (l *Logger) Infof(format string, args ...interface{})

func (*Logger) IsDebugEnabled

func (l *Logger) IsDebugEnabled() bool

func (*Logger) IsErrorEnabled

func (l *Logger) IsErrorEnabled() bool

func (*Logger) IsInfoEnabled

func (l *Logger) IsInfoEnabled() bool

func (*Logger) IsTraceEnabled

func (l *Logger) IsTraceEnabled() bool

func (*Logger) IsWarnEnabled

func (l *Logger) IsWarnEnabled() bool

func (*Logger) Level

func (l *Logger) Level() Level

func (*Logger) Panic

func (l *Logger) Panic(args ...interface{})

Panic calls panic after it writes and flushes the log

func (*Logger) PanicF added in v0.0.3

func (l *Logger) PanicF(msg string, fields Fields)

PanicF duplicates instead of calling Panic to keep source line correct

func (*Logger) Panicf

func (l *Logger) Panicf(format string, args ...interface{})

Panicf duplicates instead of calling Panic to keep source line correct

func (*Logger) PrintTree

func (l *Logger) PrintTree()

func (*Logger) PrintTreeTo

func (l *Logger) PrintTreeTo(w io.Writer)

PrintTreeTo prints logger as a tree, using current logger as root

func (*Logger) SetHandler

func (l *Logger) SetHandler(h Handler)

func (*Logger) SetLevel

func (l *Logger) SetLevel(level Level)

func (*Logger) Trace

func (l *Logger) Trace(args ...interface{})

func (*Logger) TraceF added in v0.0.3

func (l *Logger) TraceF(msg string, fields Fields)

func (*Logger) Tracef

func (l *Logger) Tracef(format string, args ...interface{})

func (*Logger) Warn

func (l *Logger) Warn(args ...interface{})

func (*Logger) WarnF added in v0.0.3

func (l *Logger) WarnF(msg string, fields Fields)

func (*Logger) Warnf

func (l *Logger) Warnf(format string, args ...interface{})

type LoggerType

type LoggerType uint8

LoggerType can be used for filtering loggers, it is set when creating logger

const (
	UnknownLogger LoggerType = iota
	ApplicationLogger
	LibraryLogger
	PackageLogger
	FunctionLogger
	StructLogger
	MethodLogger
)

func (LoggerType) String

func (tpe LoggerType) String() string

type Syncer added in v0.0.3

type Syncer interface {
	Sync() error
}

Syncer is implemented by os.File, handler implementation should check this interface and call Sync if they support using file as sink

type TestHandler added in v0.0.5

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

TestHandler stores log as entry, its slice is protected by a RWMutex and safe for concurrent use

func NewTestHandler

func NewTestHandler() *TestHandler

NewTestHandler returns a test handler, it should only be used in test, a concrete type instead of Handler interface is returned to reduce unnecessary type cast in test

func (*TestHandler) Flush added in v0.0.5

func (h *TestHandler) Flush()

Flush implements Handler interface

func (*TestHandler) HandleLog added in v0.0.5

func (h *TestHandler) HandleLog(level Level, time time.Time, msg string)

HandleLog implements Handler interface

func (*TestHandler) HandleLogWithFields added in v0.0.5

func (h *TestHandler) HandleLogWithFields(level Level, time time.Time, msg string, fields Fields)

HandleLogWithFields implements Handler interface

func (*TestHandler) HandleLogWithSource added in v0.0.5

func (h *TestHandler) HandleLogWithSource(level Level, time time.Time, msg string, source string)

HandleLogWithSource implements Handler interface

func (*TestHandler) HandleLogWithSourceFields added in v0.0.5

func (h *TestHandler) HandleLogWithSourceFields(level Level, time time.Time, msg string, source string, fields Fields)

HandleLogWithSourceFields implements Handler interface

func (*TestHandler) HasLog added in v0.0.5

func (h *TestHandler) HasLog(level Level, msg string) bool

HasLog checks if a log with specified level and message exists in slice TODO: support field, source etc.

Directories

Path Synopsis
_examples
simple command
uselib command
handlers
cli
Package cli writes is same as builtin IOHandler except color and delta time.
Package cli writes is same as builtin IOHandler except color and delta time.
json
Package json writes log in JSON format, it concatenates string directly and does not use encoding/json.
Package json writes log in JSON format, it concatenates string directly and does not use encoding/json.

Jump to

Keyboard shortcuts

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