Documentation ¶
Overview ¶
Package clog implements a python-like, module-based logger with a variety of backends and formats.
This logger is based on Python's fantastic logging package, though it intends to be far simpler. Every part of the system should have its own individual logger, specific by a module name, so that its messages can be easily distinguished from other parts. In golang's log package, people have typically resorted to prefixing messages with things like "[INFO module]", but that is tedious, error prone, rather un-enforceable for external pacakges, and impossible to serialize for all logging services.
Clog takes the approach that data should be represented as data, not strings (there are methods such as Infod, Warnd, etc), and this data is properly serialized for all output formats. Each message includes a timestamp, the module that generated the message, the level, and the source file and line.
Like python's logging, clog modules are heirachical, in that rules for something like "server.http" will be applied to "server.http.requests" and "server.http.responses" as the messages propogate. Each module may have multiple outputs, so you may log to both a file and logstash for the same module, if you wish.
Configuration ¶
Configuring logging packages has always been a rather daunting affair, but clog takes great pains to be as simple as possible.
First, there are 2 concepts in logging: Outputs and Modules. Outputs are where log messages are written to, ie. a file, an external service, syslog, etc. Modules are where messages come from, ie. "http" for the http interface, "library.name" for some library named "name". You may choose your own values for these, just know that they're arranged in a tree, such that "http.request" is a child of "http" and messages from it propagate to "http".
Since examples are worth more than descriptions, let's take a look at a pretty complex configuration, with comments explaining how it all works together (this is much simpler to write in JSON, but you get the point).
Config{ // If set, this creates a new root module (the module named "" (the empty // string)), and it records any message level >= Info to the named file in // JSON format. File: "/var/log/app.log", Outputs: map[string]*OutputConfig{ // Only errors with level >= Error will be logged here "errors": { Prod: "file", ProdArgs: eio.Args{ "path": "/var/log/app.error.json.log", }, Fmt: "json", Level: Error, Filters: []FilterConfig{ FilterConfig{ Which: "exampleFilter", Args: eio.Args{ "exampleConfig": "someValue", }, }, }, }, // All messages will be accepted here "debug": { Prod: "file", ProdArgs: eio.Args{ "path": "/var/log/app.log.json", }, Fmt: "human", // Or "logfmt", or any other valid formatter Level: Debug, }, // Only errors level >= Warn will be accepted here "heroku": { Prod: "file", ProdArgs: eio.Args{ "path": "/var/log/app.logfmt", }, Fmt: "logfmt", FmtArgs: eio.Args{ "ShortTime": true, }, Level: Warn, }, }, Modules: map[string]*ModuleConfig{ // All messages eventually reach here, unless DontPropagate==true in a // module "": { Outputs: []string{"errors"}, }, // This logs all messages level >= Info, where the filter allows the // message through, to the debug log. These messages do not propagate to // the root. "http": { Outputs: []string{"debug"}, Level: Info, Filters: []FilterConfig{ FilterConfig{ Which: "exampleFilter", Args: eio.Args{ "exampleConfig": "someValue", }, }, }, DontPropagate: true, }, // This logs all messages level >= Warn, to both the heroku and errors // outputs. These messages do not propagate to the root. "templates": { Outputs: []string{"heroku", "errors"}, Level: Warn, DontPropagate: true, }, // This logs all messages from the external library to the debug log. // These messages also propagate to the root, which will log any error // messages. So, effectively, errors from this module will be logged // twice. "external.library": { Outputs: []string{"debug"}, Level: Debug, }, }, }
Producers ¶
A full list of Producers can be found at: https://godoc.org/github.com/iheartradio/cog/cio/eio
Filters ¶
The following filters are available (their names are case-insensitive):
"Level" LevelFilter
Formatters ¶
The following formatters exist; their arguments are specified in the listed classes. Like everything else, these names are case-insensitive.
"Human" HumanFormat "JSON" JSONFormat "LogFmt" LogFmtFormat
Testing ¶
If you want log output to go to a test's log, check out: https://godoc.org/github.com/iheartradio/cog/check/chlog
Example (File) ¶
package main import ( "fmt" "io/ioutil" "os" "strings" "github.com/iheartradio/cog/clog" ) func main() { path := "example_file.log" cfg := clog.Config{ File: path, } l, err := clog.New(cfg) if err != nil { panic(err) } defer os.Remove(path) lg := l.Get("example") lg.Debug("Bug bug bug bug") lg.Info("This is a very informative message") lg.WithKV("variable", 1234).Warn("I must warn you, numbers are scary") contents, err := ioutil.ReadFile(path) if err != nil { panic(err) } log := string(contents) fmt.Println("contains debug", strings.Contains(log, "Bug bug bug bug")) fmt.Println("contains info", strings.Contains(log, "informative message")) fmt.Println("contains warn", strings.Contains(log, "I must warn you")) }
Output: contains debug false contains info true contains warn true
Example (Stdout) ¶
package main import ( "strings" "github.com/iheartradio/cog/cio/eio" "github.com/iheartradio/cog/clog" ) // Rejects any messages that might be insulting type insultFilter struct{} func (insultFilter) Accept(e clog.Entry) bool { return !strings.Contains(strings.ToLower(e.Msg), "i hate you") } func (insultFilter) Exit() { // This filter has nothing to cleanup, so nothing to do here } func init() { clog.RegisterFilter("insult", func(args eio.Args) (clog.Filter, error) { // If args were used here, args.ApplyTo might come in handy return insultFilter{}, nil }) } func main() { cfg := clog.Config{ Outputs: map[string]*clog.OutputConfig{ "stdout": { Prod: "stdout", Fmt: "human", FmtArgs: eio.Args{ "ShortTime": true, }, Level: clog.Info, }, }, Modules: map[string]*clog.ModuleConfig{ "": { Outputs: []string{"stdout"}, }, "rude.module": { Outputs: []string{"stdout"}, Filters: []clog.FilterConfig{ clog.FilterConfig{ Which: "insult", }, }, DontPropagate: true, }, }, } l, err := clog.New(cfg) if err != nil { panic(err) } polite := l.Get("polite.module") polite.Info("You're very pretty") polite.Info("I like you") rude := l.Get("rude.module") rude.Info("I hate you") rude.Info("You're ugly and I hate you") rude.Error("I'm better than you") }
Output: [000000] I-polite.module : example_stdout_test.go:64 : You're very pretty [000000] I-polite.module : example_stdout_test.go:65 : I like you [000000] E-rude.module : example_stdout_test.go:70 : I'm better than you
Index ¶
- func DumpKnownFilters(w io.Writer)
- func Hostname() string
- func RegisterFilter(name string, nf NewFilter)
- func RegisterFormatter(name string, nf NewFormatter)
- type Config
- type Ctx
- type Data
- type Entry
- type Filter
- type FilterConfig
- type Formatter
- type HumanFormat
- type JSONFormat
- type Level
- type LevelFilter
- type Log
- func (l Log) Debug(args ...interface{})
- func (l Log) Debugf(format string, args ...interface{})
- func (l Log) Error(args ...interface{})
- func (l Log) Errorf(format string, args ...interface{})
- func (l Log) Info(args ...interface{})
- func (l Log) Infof(format string, args ...interface{})
- func (l Log) Panic(args ...interface{})
- func (l Log) Panicf(format string, args ...interface{})
- func (l Log) Warn(args ...interface{})
- func (l Log) Warnf(format string, args ...interface{})
- func (l Log) With(d Data) Logger
- func (l Log) WithKV(k string, v interface{}) Logger
- type LogFmtFormat
- type Logger
- type LogstashFormat
- type ModuleConfig
- type NewFilter
- type NewFormatter
- type OutputConfig
- type Stats
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DumpKnownFilters ¶
DumpKnownFilters writes all known filters and their names to the given Writer.
func Hostname ¶
func Hostname() string
Hostname provides a cached version of os.Hostname() (call that a ton of times is probably a bad idea).
func RegisterFilter ¶
RegisterFilter adds a Filter to the list of filters
func RegisterFormatter ¶
func RegisterFormatter(name string, nf NewFormatter)
RegisterFormatter adds a Formatter to the list of formats
Types ¶
type Config ¶
type Config struct { // File is the simplest way of configuring this logger. It sets up a // JSONFile writing to the given path, with a root logger that only accepts // Info and greater. File string // Identifies all of the places where log entries are written. They keys in // this map name the output. Outputs map[string]*OutputConfig // Identifies all modules that you want to configure. They keys in this map // identify the module to work on. // // If no modules are given, everything at level Info and above goes to the // terminal by default. Modules map[string]*ModuleConfig }
Config specifies basic config for logging.
The Config struct is meant to be embedded directly into some other struct that you're Unmarshaling your application's config into (typically, this is a struct that is being filled by json.Unmarshal, yaml.Unmarshal, etc on application start).
type Ctx ¶
type Ctx struct {
// contains filtered or unexported fields
}
Ctx provides access to the logging facilities
func NewFromFile ¶
NewFromFile creates a new Log, configured from the given file. The file type is determined by the extension.
func NewFromJSONReader ¶
NewFromJSONReader creates a new Log configured from JSON in the given reader.
func (*Ctx) Flush ¶
Flush can be used by servers that exit gracefully. It causes all Outputs to write anything pending. It blocks until done.
If it returns an error, nothing was flushed.
func (*Ctx) Reconfigure ¶
Reconfigure reconfigures the entire logging system from the ground up. All active loggers are affected immediately, and all changes are applied atomically. If reconfiguration fails, the previous configuration remains.
func (*Ctx) ReconfigureFromFile ¶
ReconfigureFromFile reconfigures the Log from the given file. The file type is determined by the extension.
func (*Ctx) ReconfigureFromJSONReader ¶
ReconfigureFromJSONReader reconfigures the Log from the JSON in the given reader.
type Entry ¶
type Entry struct { // When this Entry was originally logged Time time.Time // Which module this Entry belongs to Module string // Level of this Entry Level Level // Source file and line from where this was logged Src string // How much of the call stack to ignore when looking for a file:lineno Depth int `json:"-"` // Formatted text Msg string // Name of the host this originated on Host string // Data to include with the Entry Data Data // contains filtered or unexported fields }
Entry is one complete log entry
type Filter ¶
type Filter interface { // Checks whether or not this Entry should be accepted Accept(Entry) bool // Just in case you need to cleanup when no longer needed Exit() }
A Filter determines which log entries are allowed through
type FilterConfig ¶
type FilterConfig struct { // Which the filter to use. This value is case-insensitive. Which string // Filter arguments Args eio.Args }
FilterConfig is for setting up a Filter
type Formatter ¶
type Formatter interface { // Format a message. FormatEntry(Entry) ([]byte, error) // Get the MimeType of data produced by this Formatter MimeType() string }
Formatter formats messages
type HumanFormat ¶
type HumanFormat struct { Args struct { ShortTime bool // Format timestamp as "seconds since start" } }
HumanFormat formats log entries so that a human can quickly decipher them
func (HumanFormat) FormatEntry ¶
func (f HumanFormat) FormatEntry(e Entry) ([]byte, error)
FormatEntry implements Formatter.FormatEntry
func (HumanFormat) MimeType ¶
func (HumanFormat) MimeType() string
MimeType implements Formatter.MimeType
type JSONFormat ¶
type JSONFormat struct{}
JSONFormat formats messages as JSON
func (JSONFormat) FormatEntry ¶
func (JSONFormat) FormatEntry(e Entry) ([]byte, error)
FormatEntry implements Formatter.FormatEntry
func (JSONFormat) MimeType ¶
func (JSONFormat) MimeType() string
MimeType implements Formatter.MimeType
type Level ¶
type Level int8
Level is a typical log level
const ( // Debug is the finest level: it contains things that are only useful for // debugging Debug Level = iota // Info is for general information that might be useful during runtime Info // Warn tells about something that doesn't seem right Warn // Error is a recoverable runtime error Error // Panic causes the current goroutine to panic after logging the message Panic )
func (Level) MarshalJSON ¶
MarshalJSON implements JSON.Marshaler
func (*Level) UnmarshalJSON ¶
UnmarshalJSON implements JSON.Unmarshaler
type LevelFilter ¶
type LevelFilter struct { Args struct { // Don't log anything below this level Level Level } }
LevelFilter is the filter used by the required "Level" argument for both Modules and Outputs and is typically not used directly.
Example:
Filters: []FilterConfig{ FilterConfig{ Which: "Level", Args: eio.Args{ "level": clog.Info, }, }, }
func (LevelFilter) Accept ¶
func (lf LevelFilter) Accept(e Entry) bool
Accept implements Filter.Accept()
type LogFmtFormat ¶
type LogFmtFormat struct{}
LogFmtFormat formats messages in heroku's LogFmt
func (LogFmtFormat) FormatEntry ¶
func (LogFmtFormat) FormatEntry(e Entry) ([]byte, error)
FormatEntry implements Formatter.FormatEntry
func (LogFmtFormat) MimeType ¶
func (LogFmtFormat) MimeType() string
MimeType implements Formatter.MimeType
type Logger ¶
type Logger interface { // With merges the given data into any current data and returns a new // Logger with the resulting data. New values replace older ones with the // same key. With(d Data) Logger // Add the KV pair and get a new Logger. WithKV(k string, v interface{}) Logger // Debug entries: things that can be ignored in production Debug(args ...interface{}) Debugf(format string, args ...interface{}) // Info: stuff that is useful, even in production Info(args ...interface{}) Infof(format string, args ...interface{}) // Something might start going wrong soon Warn(args ...interface{}) Warnf(format string, args ...interface{}) // Something went wrong Error(args ...interface{}) Errorf(format string, args ...interface{}) // A horrible error happened. Panic(args ...interface{}) Panicf(format string, args ...interface{}) }
A Logger provides a simplified Log
type LogstashFormat ¶
type LogstashFormat struct{}
LogstashFormat formats messages as JSON, with special fields for logstash
func (LogstashFormat) FormatEntry ¶
func (LogstashFormat) FormatEntry(e Entry) ([]byte, error)
FormatEntry implements Formatter.FormatEntry
func (LogstashFormat) MimeType ¶
func (LogstashFormat) MimeType() string
MimeType implements Formatter.MimeType
type ModuleConfig ¶
type ModuleConfig struct { // The list of outputs to write to. These values come from the keys in the // Outputs map in Config. Outputs []string // Log level to use for this output. Use Debug to accept all. This is // actually an implicit (and required) Filter. Level Level // Which filters to apply to this module Filters []FilterConfig // By default, messages are propagated until the root logger. If you want // messages to stop here, set this to True. DontPropagate bool }
ModuleConfig specifies how a module to to be treated
type NewFormatter ¶
NewFormatter creates a new, configured Formatter.
type OutputConfig ¶
type OutputConfig struct { // Which Producer to use. This value is case-insensitive. // // The full list of Producers can be found at: // https://godoc.org/github.com/iheartradio/cog/cio/eio Prod string ProdArgs eio.Args // Args to pass to the Producer // Which Formatter to use for this output. Fmt string FmtArgs eio.Args // Args to pass to the Fmt // Log level to use for this output. Use Debug to accept all. This is // actually an implicit (and required) Filter. Level Level // Which filters to apply to this output. Filters []FilterConfig }
OutputConfig specifies how an output is to be built