Documentation ¶
Overview ¶
This is a logger implementation that supports multiple log levels, multiple output destinations with configurable formats and levels for each. It also supports granular output configuration to get more detailed logging for specific files/packages. Timber includes support for standard XML or JSON config files to get you started quickly. It's also easy to configure in code if you want to DIY.
Basic use:
import "timber" timber.LoadConfiguration("timber.xml") timber.Debug("Debug message!")
IMPORTANT: timber has not default destination configured so log messages will be dropped until a destination is configured
It can be used as a drop-in replacement for the standard logger by changing the log import statement from:
import "log"
to
import log "timber"
It can also be used as the output of the standard logger with
log.SetFlags(0) log.SetOutput(timber.Global)
Configuration in code is also simple:
timber.AddLogger(timber.ConfigLogger{ LogWriter: new(timber.ConsoleWriter), Level: timber.DEBUG, Formatter: timber.NewPatFormatter("[%D %T] [%L] %S %M"), })
XML Config file:
<logging> <filter enabled="true"> <tag>stdout</tag> <type>console</type> <!-- level is (:?FINEST|FINE|DEBUG|TRACE|INFO|WARNING|ERROR) --> <level>DEBUG</level> </filter> <filter enabled="true"> <tag>file</tag> <type>file</type> <level>FINEST</level> <granular> <level>INFO</level> <path>path/to/package.FunctionName</path> </granular> <granular> <level>WARNING</level> <path>path/to/package</path> </granular> <property name="filename">log/server.log</property> <property name="format">server [%D %T] [%L] %M</property> </filter> <filter enabled="false"> <tag>syslog</tag> <type>socket</type> <level>FINEST</level> <property name="protocol">unixgram</property> <property name="endpoint">/dev/log</property> <format name="pattern">%L %M</property> </filter> </logging>
The <tag> is ignored.
To configure the pattern formatter all filters accept:
<format name="pattern">[%D %T] %L %M</format>
Pattern format specifiers (not the same as log4go!):
%T - Time: 17:24:05.333 HH:MM:SS.ms %t - Time: 17:24:05 HH:MM:SS %D - Date: 2011-12-25 yyyy-mm-dd %d - Date: 2011/12/25 yyyy/mm/dd %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT) %S - Source: full runtime.Caller line and line number %s - Short Source: just file and line number %x - Extra Short Source: just file without .go suffix %M - Message %% - Percent sign %P - Caller Path: packagePath.CallingFunctionName %p - Caller Path: packagePath
the string number prefixes are allowed e.g.: %10s will pad the source field to 10 spaces pattern defaults to %M Both log4go synatax of <property name="format"> and new <format name=type> are supported the property syntax will only ever support the pattern formatter To configure granulars:
- Create one or many <granular> within a filter
- Define a <level> and <path> within, where path can be path to package or path to package.FunctionName. Function name definitions override package paths.
Code Architecture: A MultiLogger <logging> which consists of many ConfigLoggers <filter>. ConfigLoggers have three properties: LogWriter <type>, Level (as a threshold) <level> and LogFormatter <format>.
In practice, this means that you define ConfigLoggers with a LogWriter (where the log prints to eg. socket, file, stdio etc), the Level threshold, and a LogFormatter which formats the message before writing. Because the LogFormatters and LogWriters are simple interfaces, it is easy to write your own custom implementations.
Once configured, you only deal with the "Logger" interface and use the log methods in your code
The motivation for this package grew from a need to make some changes to the functionality of log4go (which had already been integrated into a larger project). I tried to maintain compatiblity with log4go for the interface and configuration. The main issue I had with log4go was that each of logger types had incisistent and incompatible configuration. I looked at contributing changes to log4go, but I would have needed to break existing use cases so I decided to do a rewrite from scratch.
Index ¶
- Constants
- Variables
- func AddLogger(logger ConfigLogger) int
- func Close()
- func Critical(arg0 interface{}, args ...interface{}) error
- func Debug(arg0 interface{}, args ...interface{})
- func Error(arg0 interface{}, args ...interface{}) error
- func Fatal(v ...interface{})
- func Fatalf(format string, v ...interface{})
- func Fatalln(v ...interface{})
- func Fine(arg0 interface{}, args ...interface{})
- func Finest(arg0 interface{}, args ...interface{})
- func Info(arg0 interface{}, args ...interface{})
- func LoadConfiguration(filename string)
- func LoadJSONConfiguration(filename string)
- func LoadXMLConfiguration(filename string)
- func Log(lvl Level, arg0 interface{}, args ...interface{})
- func Panic(v ...interface{})
- func Panicf(format string, v ...interface{})
- func Panicln(v ...interface{})
- func Print(v ...interface{})
- func Printf(format string, v ...interface{})
- func Println(v ...interface{})
- func Trace(arg0 interface{}, args ...interface{})
- func Warn(arg0 interface{}, args ...interface{}) error
- type BufferedWriter
- type ConfigLogger
- type ConsoleWriter
- type JSONConfig
- type JSONFilter
- type JSONGranular
- type JSONProperty
- type Level
- type LogFormatter
- type LogRecord
- type LogWriter
- type Logger
- type LoggerConfig
- type MultiLogger
- type PatFormatter
- type SocketWriter
- type SyslogFormatter
- type Timber
- func (t *Timber) AddLogger(logger ConfigLogger) int
- func (t *Timber) Close()
- func (t *Timber) Critical(arg0 interface{}, args ...interface{}) error
- func (t *Timber) Debug(arg0 interface{}, args ...interface{})
- func (t *Timber) Error(arg0 interface{}, args ...interface{}) error
- func (t *Timber) Fatal(v ...interface{})
- func (t *Timber) Fatalf(format string, v ...interface{})
- func (t *Timber) Fatalln(v ...interface{})
- func (t *Timber) Fine(arg0 interface{}, args ...interface{})
- func (t *Timber) Finest(arg0 interface{}, args ...interface{})
- func (t *Timber) Info(arg0 interface{}, args ...interface{})
- func (t *Timber) LoadConfig(filename string)
- func (t *Timber) LoadJSONConfig(filename string) error
- func (t *Timber) LoadXMLConfig(filename string) error
- func (t *Timber) Log(lvl Level, arg0 interface{}, args ...interface{})
- func (t *Timber) Panic(v ...interface{})
- func (t *Timber) Panicf(format string, v ...interface{})
- func (t *Timber) Panicln(v ...interface{})
- func (t *Timber) Print(v ...interface{})
- func (t *Timber) Printf(format string, v ...interface{})
- func (t *Timber) Println(v ...interface{})
- func (t *Timber) SetFormatter(index int, formatter LogFormatter)
- func (t *Timber) SetLevel(index int, lvl Level)
- func (t *Timber) Trace(arg0 interface{}, args ...interface{})
- func (t *Timber) Warn(arg0 interface{}, args ...interface{}) error
- func (t *Timber) Write(p []byte) (n int, err error)
- type XMLConfig
- type XMLFilter
- type XMLGranular
- type XMLProperty
Constants ¶
const DefaultFileDepth int = 3
Default level passed to runtime.Caller by Timber, add to this if you wrap Timber in your own logging code
Variables ¶
var DefaultSeverityMap = map[Level]syslog.Priority{ NONE: syslog.LOG_INFO, FINEST: syslog.LOG_DEBUG, FINE: syslog.LOG_DEBUG, DEBUG: syslog.LOG_DEBUG, TRACE: syslog.LOG_INFO, INFO: syslog.LOG_INFO, WARNING: syslog.LOG_WARNING, ERROR: syslog.LOG_ERR, CRITICAL: syslog.LOG_CRIT, }
Mapping from the timber levels to the syslog severity If you override this, make sure all the entries are in the map since the syslog.Priority zero value will cause a message at the Emergency severity
var Global = NewTimber()
Default Timber Instance (used for all the package level function calls)
var LevelStrings = [...]string{"", "FNST", "FINE", "DEBG", "TRAC", "INFO", "WARN", "EROR", "CRIT"}
What gets printed for each Log level
var LongLevelStrings = []string{
"NONE",
"FINEST",
"FINE",
"DEBUG",
"TRACE",
"INFO",
"WARNING",
"ERROR",
"CRITICAL",
}
Full level names
Functions ¶
func AddLogger ¶
func AddLogger(logger ConfigLogger) int
func Finest ¶
func Finest(arg0 interface{}, args ...interface{})
Simple wrappers for Logger interface
func LoadConfiguration ¶
func LoadConfiguration(filename string)
func LoadJSONConfiguration ¶
func LoadJSONConfiguration(filename string)
func LoadXMLConfiguration ¶
func LoadXMLConfiguration(filename string)
Types ¶
type BufferedWriter ¶
type BufferedWriter struct {
// contains filtered or unexported fields
}
Use this of you need some buffering, or not
func NewBufferedWriter ¶
func NewBufferedWriter(writer io.WriteCloser) (*BufferedWriter, error)
func (*BufferedWriter) Close ¶
func (bw *BufferedWriter) Close()
func (*BufferedWriter) LogWrite ¶
func (bw *BufferedWriter) LogWrite(msg string)
type ConfigLogger ¶
type ConfigLogger struct { LogWriter LogWriter // Messages with level < Level will be ignored. It's up to the implementor to keep the contract or not Level Level Formatter LogFormatter Granulars map[string]Level }
Container a single log format/destination
type ConsoleWriter ¶
type ConsoleWriter func(string)
This uses the standard go logger to write the messages
func (ConsoleWriter) Close ¶
func (c ConsoleWriter) Close()
func (ConsoleWriter) LogWrite ¶
func (c ConsoleWriter) LogWrite(msg string)
type JSONConfig ¶
type JSONConfig struct {
Filters []JSONFilter
}
type JSONFilter ¶
type JSONFilter struct { Enabled bool Tag string Type string Level string Format JSONProperty Properties []JSONProperty Granulars []JSONGranular }
type JSONGranular ¶
Granulars are overriding levels that can be either package paths or package path + function name
type JSONProperty ¶
type LogFormatter ¶
Format a log message before writing
type LogRecord ¶
type LogRecord struct { Level Level Timestamp time.Time SourceFile string SourceLine int Message string FuncPath string PackagePath string }
This packs up all the message data and metadata. This structure will be passed to the LogFormatter
type LogWriter ¶
type LogWriter interface { LogWrite(msg string) Close() }
Interface required for a log writer endpoint. It's more or less a io.WriteCloser with no errors allowed to be returned and string instead of []byte.
TODO: Maybe this should just be a standard io.WriteCloser?
func NewFileWriter ¶
This writer has a buffer that I don't ever bother to flush, so it may take a while to see messages
type Logger ¶
type Logger interface { // match log4go interface to drop-in replace Finest(arg0 interface{}, args ...interface{}) Fine(arg0 interface{}, args ...interface{}) Debug(arg0 interface{}, args ...interface{}) Trace(arg0 interface{}, args ...interface{}) Info(arg0 interface{}, args ...interface{}) Warn(arg0 interface{}, args ...interface{}) error Error(arg0 interface{}, args ...interface{}) error Critical(arg0 interface{}, args ...interface{}) error Log(lvl Level, arg0 interface{}, args ...interface{}) // support standard log too Print(v ...interface{}) Printf(format string, v ...interface{}) Println(v ...interface{}) Panic(v ...interface{}) Panicf(format string, v ...interface{}) Panicln(v ...interface{}) Fatal(v ...interface{}) Fatalf(format string, v ...interface{}) Fatalln(v ...interface{}) }
This explicitly defines the contract for a logger Not really useful except for documentation for writing an separate implementation
type LoggerConfig ¶
type LoggerConfig interface { // When set, messages with level < lvl will be ignored. It's up to the implementor to keep the contract or not SetLevel(lvl Level) // Set the formatter for the log SetFormatter(formatter LogFormatter) }
Not used
type MultiLogger ¶
type MultiLogger interface { // returns an int that identifies the logger for future calls to SetLevel and SetFormatter AddLogger(logger ConfigLogger) int // dynamically change level or format SetLevel(index int, lvl Level) SetFormatter(index int, formatter LogFormatter) Close() }
Allow logging to multiple places
type PatFormatter ¶
type PatFormatter struct {
// contains filtered or unexported fields
}
func NewPatFormatter ¶
func NewPatFormatter(format string) *PatFormatter
Format codes:
%T - Time: 17:24:05.333 HH:MM:SS.ms %t - Time: 17:24:05 HH:MM:SS %D - Date: 2011-12-25 yyyy-mm-dd %d - Date: 2011/12/25 %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT) %S - Source: full runtime.Caller line %s - Short Source: just file and line number %x - Extra Short Source: just file without .go suffix %M - Message %% - Percent sign %P - Caller Path: package path + calling function name %p - Caller Path: package path
the string number prefixes are allowed e.g.: %10s will pad the source field to 10 spaces
func (*PatFormatter) Format ¶
func (pf *PatFormatter) Format(rec *LogRecord) string
LogFormatter interface
type SocketWriter ¶
type SocketWriter struct {
// contains filtered or unexported fields
}
This should write to anything that you can write to with net.Dial
func NewSocketWriter ¶
func NewSocketWriter(network, addr string) (*SocketWriter, error)
func (*SocketWriter) Close ¶
func (sw *SocketWriter) Close()
func (*SocketWriter) LogWrite ¶
func (sw *SocketWriter) LogWrite(msg string)
type SyslogFormatter ¶
type SyslogFormatter struct { Hostname string Tag string Facility syslog.Priority SeverityMap map[Level]syslog.Priority // contains filtered or unexported fields }
Syslog formatter wraps a PatFormatter but adds the syslog protocol format to the message. Defaults: Facility: syslog.LOG_USER (1 << 3 for pre-go1.1 compatibility) Hostname: os.Hostname() Tag: os.Args[0]
func NewSyslogFormatter ¶
func NewSyslogFormatter(format string) *SyslogFormatter
func (*SyslogFormatter) Format ¶
func (sf *SyslogFormatter) Format(rec *LogRecord) string
type Timber ¶
type Timber struct { // This value is passed to runtime.Caller to get the file name/line and may require // tweaking if you want to wrap the logger FileDepth int // contains filtered or unexported fields }
The Timber instance is the concrete implementation of the logger interfaces. New instances may be created, but usually you'll just want to use the default instance in Global
NOTE: I don't supporting the log4go special handling of the first parameter based on type mainly cuz I don't think it's particularly useful (I kept passing a data string as the first param and expecting a Println-like output but that would always break expecting a format string) I also don't support the passing of the closure stuff
func NewTimber ¶
func NewTimber() *Timber
Creates a new Timber logger that is ready to be configured With no subsequent configuration, nothing will be logged
func (*Timber) AddLogger ¶
func (t *Timber) AddLogger(logger ConfigLogger) int
MultiLogger interface
func (*Timber) LoadConfig ¶
func (*Timber) LoadJSONConfig ¶
Loads the configuration from an JSON file (as you were probably expecting)
func (*Timber) LoadXMLConfig ¶
Loads the configuration from an XML file (as you were probably expecting)
func (*Timber) Print ¶
func (t *Timber) Print(v ...interface{})
Print won't work well with a pattern_logger because it explicitly adds its own \n; so you'd have to write your own formatter to remove it
func (*Timber) Println ¶
func (t *Timber) Println(v ...interface{})
Println won't work well either with a pattern_logger because it explicitly adds its own \n; so you'd have to write your own formatter to not have 2 \n's
func (*Timber) SetFormatter ¶
func (t *Timber) SetFormatter(index int, formatter LogFormatter)
Not yet implemented
type XMLFilter ¶
type XMLFilter struct { XMLName xml.Name `xml:"filter"` Enabled bool `xml:"enabled,attr"` Tag string `xml:"tag"` Type string `xml:"type"` Level string `xml:"level"` Format XMLProperty `xml:"format"` Properties []XMLProperty `xml:"property"` Granulars []XMLGranular `xml:"granular"` }
type XMLGranular ¶
Granulars are overriding levels that can be either package paths or package path + function name
type XMLProperty ¶
match the log4go structure so i don't have to change my configs