Documentation ¶
Overview ¶
Package go-logger is a logging library based on node-bunyan, https://github.com/trentm/node-bunyan.
The output is compatible with the `bunyan` log reader application from that `node` package.
Usage ¶
You first start by creating a `Logger` object that will operate on a `Stream`.
package main import "github.com/gildas/go-logger" var Log = logger.Create("myapp")
Then, you can log messages to the same levels from `node-bunyan`:
Log.Tracef("This is a message at the trace level for %s", myObject) Log.Debugf("This is a message at the debug level for %s", myObject) Log.Infof("This is a message at the trace level for %s", myObject) Log.Warnf("This is a message at the warn level for %s", myObject) Log.Errorf("This is a message at the error level for %s", myObject, err) Log.Fatalf("This is a message at the fatal level for %s", myObject, err)
Note the `err` variable (must implement the error interface) used with the last two log calls. By just adding it to the list of arguments at Error or Fatal level while not mentioning it in the format string will tell the `Logger` to spit that error in a bunyan Record field (see https://github.com/trentm/node-bunyan#log-record-fields).
More generally, Record fields can be logged like this:
Log.Record("myObject", myObject).Infof("Another message about my object") Log.Recordf("myObject", "format %s %+v". myObject.ID(), myObject).Infof("This record uses a formatted value") log := Log.Record("dynamic", func() interface{} { return myObject.Callme() }) log.Infof("This is here") log.Infof("That is there")
In the last example, the code `myObject.Callme()` will be executed each time *log* is used to write a message. This is used, for example, to add a timestamp to the log's `Record`.
Here is a simple example how Record fields can be used with a type:
type Stuff struct { Field1 string Field2 int Logger *logger.Logger // So Stuff carries its own logger } func (s *Stuff) SetLogger(l *logger.Logger) { s.Logger = l.Topic("stuff").Scope("stuff") } func (s Stuff) DoSomething(other *OtherStuff) error { log := s.Logger.Scope("dosomething") log.Record("other", other).Infof("Need to do something") if err := someFunc(s, other); err != nil { log.Errorf("Something went wrong with other", err) return err } return nil }
The call to Record(key, value) creates a new Logger object. So, they are like Russian dolls when it comes down to actually writing the log message to the output stream. In other words, Record objects are collected from their parent's `Logger` back to the original `Logger.
For example:
var Log = logger.Create("test") var child = Log.Record("key1", "value1").Record("key2", "value2")
child will actually be something like Logger(Logger(Logger(Stream to stdout))). Though we added only 2 records.
Therefore, to optimize the number of Logger objects that are created, there are some convenience methods that can be used:
func (s stuff) DoSomethingElse(other *OtherStuff) { log := s.Logger.Child("new_topic", "new_scope", "id", other.ID(), "key1", "value1") log.Infof("I am logging this stuff") log.Records("key2", "value2", "key3", 12345).Warnf("Aouch that hurts!") }
The Child method will create one Logger that has a Record containing a topic, a scope, 2 keys (*id* and *key1*) with their values.
Stream objects ¶
A Stream is where the Logger actually writes its Record data.
When creating a Logger, you can specify the destination it will write to:
var Log = logger.Create("myapp", "file://path/to/myapp.log") var Log = logger.Create("myapp", "/path/to/myapp.log") var Log = logger.Create("myapp", "./localpath/to/myapp.log") var Log = logger.Create("myapp", "stackdriver") var Log = logger.Create("myapp", "gcp") var Log = logger.Create("myapp", "/path/to/myapp.log", "stderr") var Log = logger.Create("myapp", "nil")
The first three Logger objects will write to a file, the fourth to Google Stackdriver, the fifth to Google Cloud Platform (GCP), the sixth to a file and stderr, and the seventh to nowhere (i.e. logs do not get written at all).
By default, when creating a Logger with:
var Log = logger.Create("myapp")
The Logger will write to the standard output or the destination specified in the environment variable LOG_DESTINATION.
You can also create a Logger by passing it a Stream object (these are equivalent to the previous code):
var Log = logger.Create("myapp", &logger.FileStream{Path: "/path/to/myapp.log"}) var Log = logger.Create("myapp", &logger.StackDriverStream{}) var Log = logger.Create("myapp", &logger.NilStream{}) var Log = logger.Create("myapp", &logger.FileStream{Path: "/path/to/myapp.log"}, &logger.StderrStream{})
A few notes:
- logger.CreateWithStream can also be used to create with one or more streams. (Backward compatibility)
- logger.CreateWithDestination can also be used to create with one or more destinations. (Backward compatibility)
- the StackDriverStream needs a LogID parameter or the value of the environment variable GOOGLE_PROJECT_ID. (see Google's StackDriver documentation: https://godoc.org/cloud.google.com/go/logging#NewClient for the description of that parameter).
- NilStream is a Stream that does not write anything, all messages are lost.
- MultiStream is a Stream than can write to several streams.
- All Stream types, except NilStream and MultiStream can use a FilterLevel. When set, Record objects that have a Level below the FilterLevel are not written to the Stream. This allows to log only stuff above Warn for instance. The FilterLevel can be set via the environment variable LOG_LEVEL.
- StdoutStream and FileStream are buffered by default. Data is written from every LOG_FLUSHFREQUENCY (default 5 minutes) or when the Record's Level is at least ERROR.
- Streams convert the Record to write via a Converter. The converter is set to a default value per Stream.
You can also create a Logger with a combination of destinations and streams, AND you can even add some records right away:
var Log = logger.Create("myapp", &logger.FileStream{Path: "/path/to/myapp.log"}, "stackdriver", NewRecord().Set("key", "value"), )
StackDriver Stream ¶
If you plan to log to Google's StackDriver from a Google Cloud Kubernetes or a Google Cloud Instance, you do not need the StackDriver Stream and should use the Stdout Stream with the StackDriver Converter, since the standard output of your application will be captured automatically by Google to feed StackDriver:
var Log = logger.Create("myapp", "gcp") // "google" or "googlecloud" are valid aliases var Log = logger.Create("myapp", &logger.StdoutStream{Converter: &logger.StackDriverConverter{}})
To be able to use the StackDriver Stream from outside Google Cloud, you have some configuration to do first.
On your workstation, you need to get the key filename:
- Authenticate with Google Cloud gcloud auth login
- Create a Service Account (`logger-account` is just an example of a service account name) gcloud iam service-acccount create logger-account
- Associate the Service Account to the Project you want to use gcloud projects add-iam-policy-binding my-logging-project \ --member "serviceAccount:logger-account@my-logging-project.iam.gserviceaccount.com" \ --role "roles/logging.logWriter"
- Retrieve the key filename gcloud iam service-accounts keys create /path/to/key.json \ --iam-account logger-account@my-logging-project.iam.gserviceaccount.com
You can either set the GOOGLE_APPLICATION_CREDENTIAL and GOOGLE_PROJECT_ID environment variables with the path of the obtained key and Google Project ID or provide them to the StackDriver stream:
var Log = logger.Create("myapp", &logger.StackDriverStream{}) var Log = logger.Create("myapp", &logger.StackDriverStream{ Parent: "my-logging-project", KeyFilename: "/path/to/key.json", })
Writing your own Stream ¶
You can also write your own Stream by implementing the logger.Streamer interface and create the Logger like this:
var Log = logger.Create("myapp", &MyStream{})
Miscellaneous ¶
The following convenience methods can be used when creating a Logger from another one (received from arguments, for example):
var Log = logger.CreateIfNil(OtherLogger, "myapp") var Log = logger.Create("myapp", OtherLogger)
If OtherLogger is nil, the new Logger will write to the NilStream().
var Log = logger.Must(logger.FromContext(context))
Must() can be used to create a Logger from a method that returns (*Logger, error), if there is an error, Must will panic.
FromContext() can be used to retrieve a Logger from a Go context. (This is used in the next paragraph about HTTP Usage). log.ToContext will store the Logger to the given Go context.
Converters ¶
The Converter object is responsible for converting the Record, given to the Stream to write, to match other log viewers.
The default Converter is BunyanConverter so the bunyan log viewer can read the logs.
Here is a list of all the converters:
- BunyanConverter, the default converter (does nothing, actually), - PinoConverter produces logs that can be used by pino (http://getpino.io), - StackDriverConverter produces logs that are nicer with Google StackDriver log viewer,
Writing your own Converter ¶
You can also write your own Converter by implementing the logger.Converter interface:
type MyConverter struct { // ... } func (converter *MyConverter) Convert(record Record) Record { record["newvalue"] = true return record } var Log = logger.Create("myapp", &logger.StdoutStream{Converter: &MyConverter{}})
HTTP Usage ¶
It is possible to pass Logger objects to http.Handler object. When doing so, the Logger will automatically write the request identifier ("X-Request-Id" HTTP Header), remote host, user agent, when the request starts and when the request finishes along with its duration.
The request identifier is attached every time the log writes in a Record.
Here is an example:
package main import ( "net/http" "github.com/gildas/go-logger" "github.com/gorilla/mux" ) func MyHandler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Extracts the Logger from the request's context // Note: Use logger.Must only when you know there is a Logger as it will panic otherwise log := logger.Must(logger.FromContext(r.Context())) log.Infof("Now we are logging inside this http Handler") }) } func main() { log := logger.Create("myapp") router := mux.NewRouter() router.Methods("GET").Path("/").Handler(log.HttpHandler()(MyHandler())) }
Environment Variables ¶
The Logger can be configured completely by environment variables if needed. These are:
LOG_DESTINATION, default: StdoutStream
The Stream to write logs to. It can be a comma-separated list (for a MultiStream)
LOG_LEVEL, default: INFO
The level to filter by default. If the environment DEBUG is set the default level is DEBUG
LOG_CONVERTER, default: bunyan
The default Converter to use
LOG_FLUSHFREQUENCY, default: 5 minutes
The default Flush Frequency for the streams that will be buffered
GOOGLE_APPLICATION_CREDENTIALS
The path to the credential file for the StackDriverStream
GOOGLE_PROJECT_ID
The Google Cloud Project ID for the StackDriverStream
DEBUG, default: none
If set to "1", this will set the default level to filter to DEBUG
Index ¶
- Variables
- func GetFlushFrequencyFromEnvironment() time.Duration
- type BunyanConverter
- type Converter
- type FileStream
- type Level
- type Logger
- func Create(name string, parameters ...interface{}) *Logger
- func CreateIfNil(logger *Logger, name string) *Logger
- func CreateWithDestination(name string, destinations ...string) *Logger
- func CreateWithStream(name string, streams ...Streamer) *Logger
- func FromContext(context context.Context) (*Logger, error)
- func Must(log *Logger, err error) *Logger
- func (log *Logger) Child(topic, scope interface{}, params ...interface{}) *Logger
- func (log *Logger) Close()
- func (log *Logger) Debugf(msg string, args ...interface{})
- func (log *Logger) Errorf(msg string, args ...interface{})
- func (log *Logger) Fatalf(msg string, args ...interface{})
- func (log *Logger) Flush()
- func (log *Logger) GetRecord(key string) interface{}
- func (l *Logger) HttpHandler() func(http.Handler) http.Handler
- func (log *Logger) Infof(msg string, args ...interface{})
- func (log *Logger) Record(key string, value interface{}) *Logger
- func (log *Logger) Recordf(key, value string, args ...interface{}) *Logger
- func (log *Logger) Records(params ...interface{}) *Logger
- func (log *Logger) Scope(value interface{}) *Logger
- func (log *Logger) SetFilterLevel(level Level) Streamer
- func (log *Logger) ShouldWrite(level Level) bool
- func (log Logger) String() string
- func (l *Logger) ToContext(parent context.Context) context.Context
- func (log *Logger) Topic(value interface{}) *Logger
- func (log *Logger) Tracef(msg string, args ...interface{})
- func (log *Logger) Warnf(msg string, args ...interface{})
- func (log *Logger) Write(record Record) error
- type MultiStream
- type NilStream
- type PinoConverter
- type Record
- type StackDriverConverter
- type StackDriverStream
- func (stream *StackDriverStream) Close()
- func (stream *StackDriverStream) Flush()
- func (stream *StackDriverStream) SetFilterLevel(level Level) Streamer
- func (stream *StackDriverStream) ShouldWrite(level Level) bool
- func (stream *StackDriverStream) String() string
- func (stream *StackDriverStream) Write(record Record) (err error)
- type StderrStream
- type StdoutStream
- type Streamer
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var VERSION = "1.3.2" + commit
VERSION is the version of this application
Functions ¶
func GetFlushFrequencyFromEnvironment ¶
GetFlushFrequencyFromEnvironment fetches the flush frequency from the environment the frequency should be like https://golang.org/pkg/time/#ParseDuration or an ISO8601 duration.
If not set, the frequency will be 5 minutes
Types ¶
type BunyanConverter ¶ added in v1.3.0
type BunyanConverter struct { }
BunyanConverter is the default Converter
func (*BunyanConverter) Convert ¶ added in v1.3.0
func (converter *BunyanConverter) Convert(record Record) Record
Convert converts the Record into a Bunyan compatible Record
type Converter ¶ added in v1.3.0
Converter is used to convert a Record into another format
func GetConverterFromEnvironment ¶ added in v1.3.0
func GetConverterFromEnvironment() Converter
GetConverterFromEnvironment fetches the Converter from the environment
type FileStream ¶
type FileStream struct { *json.Encoder Path string Converter Converter FilterLevel Level Unbuffered bool // contains filtered or unexported fields }
FileStream is the Stream that writes to a file
Any record with a level < FilterLevel will be written
func (*FileStream) Close ¶ added in v1.3.0
func (stream *FileStream) Close()
Close closes the stream
func (*FileStream) Flush ¶
func (stream *FileStream) Flush()
Flush flushes the stream (makes sure records are actually written)
func (*FileStream) SetFilterLevel ¶ added in v1.3.0
func (stream *FileStream) SetFilterLevel(level Level) Streamer
SetFilterLevel sets the filter level
func (*FileStream) ShouldWrite ¶
func (stream *FileStream) ShouldWrite(level Level) bool
ShouldWrite tells if the given level should be written to this stream
func (*FileStream) Write ¶
func (stream *FileStream) Write(record Record) (err error)
Write writes the given Record
implements logger.Stream
type Level ¶
type Level byte
Level represents the log Level The higher the value the more chances to write in the Stream
func GetLevelFromEnvironment ¶
func GetLevelFromEnvironment() Level
GetLevelFromEnvironment retrieves the level from the environment LOG_LEVEL
func GetLevelFromRecord ¶
GetLevelFromRecord retrieves the level from the given Record
func (Level) ShouldWrite ¶
ShouldWrite tells if the current level is writeable when compared to the given filter level
type Logger ¶
type Logger struct {
// contains filtered or unexported fields
}
Logger is a Logger that creates Bunyan's compatible logs (see: https://github.com/trentm/node-bunyan)
func CreateIfNil ¶
CreateIfNil creates a new Logger if the given Logger is nil, otherwise return the said Logger
func CreateWithDestination ¶
CreateWithDestination creates a new Logger streaming to the given destination(s)
func CreateWithStream ¶
CreateWithStream creates a new Logger streaming to the given stream or list of streams
func FromContext ¶
FromContext retrieves the Logger stored in the context
func (*Logger) Errorf ¶
Errorf traces a message at the ERROR Level If the last argument is an error, a Record is added and the error string is added to the message
func (*Logger) Fatalf ¶
Fatalf traces a message at the FATAL Level If the last argument is an error, a Record is added and the error string is added to the message
func (*Logger) Flush ¶
func (log *Logger) Flush()
Flush flushes the stream (makes sure records are actually written)
func (*Logger) HttpHandler ¶
HttpHandler function will wrap an http handler with extra logging information
func (*Logger) Records ¶
Records adds key, value pairs as Record objects E.g.: log.Records("key1", value1, "key2", value2)
The key should be castable to a string If the last value is missing, its key is ignored
func (*Logger) SetFilterLevel ¶ added in v1.3.0
SetFilterLevel sets the filter level of the streamer
func (*Logger) ShouldWrite ¶
ShouldWrite tells if the given level should be written to this stream
type MultiStream ¶
type MultiStream struct {
// contains filtered or unexported fields
}
MultiStream is the Stream that writes to several streams
func (*MultiStream) Close ¶ added in v1.3.0
func (stream *MultiStream) Close()
Close closes the stream
func (*MultiStream) Flush ¶
func (stream *MultiStream) Flush()
Flush flushes the stream (makes sure records are actually written)
func (*MultiStream) SetFilterLevel ¶ added in v1.3.0
func (stream *MultiStream) SetFilterLevel(level Level) Streamer
SetFilterLevel sets the filter level of all streams
func (*MultiStream) ShouldWrite ¶
func (stream *MultiStream) ShouldWrite(level Level) bool
ShouldWrite tells if the given level should be written to this stream
func (*MultiStream) Write ¶
func (stream *MultiStream) Write(record Record) error
Write writes the given Record
type NilStream ¶
type NilStream struct { }
NilStream is the Stream that writes nowhere
Example ¶
stream := &logger.NilStream{} if err := stream.Write(logger.NewRecord().Set("bello", "banana").Set("だれ", "Me")); err != nil { os.Stdout.WriteString(err.Error() + "\n") } if stream.ShouldWrite(logger.ALWAYS) { os.Stdout.WriteString("This should not be seen\n") } stream.Flush()
Output:
func (*NilStream) Flush ¶
func (stream *NilStream) Flush()
Flush flushes the stream (makes sure records are actually written)
func (*NilStream) SetFilterLevel ¶ added in v1.3.0
SetFilterLevel sets the filter level of all streams
func (*NilStream) ShouldWrite ¶
ShouldWrite tells if the given level should be written to this stream
type PinoConverter ¶ added in v1.3.0
type PinoConverter struct { }
PinoConverter is used to convert Records to be read by pino
func (*PinoConverter) Convert ¶ added in v1.3.0
func (converter *PinoConverter) Convert(record Record) Record
Convert converts the Record into a Pino compatible Record
type Record ¶
type Record map[string]interface{}
Record is the map that contains all records of a log entry
If the value at a key is a func() interface the func will be called when the record is marshaled
func (Record) MarshalJSON ¶
MarshalJSON marshals this into JSON
func (Record) Merge ¶
Merge merges a source Record into this Record
values already set in this record cannot be overriden
func (*Record) UnmarshalJSON ¶
UnmarshalJSON unmarshals JSON into this
type StackDriverConverter ¶ added in v1.3.0
type StackDriverConverter struct { }
StackDriverConverter is used to convert a Record for StackDriver
func (*StackDriverConverter) Convert ¶ added in v1.3.0
func (converter *StackDriverConverter) Convert(record Record) Record
Convert converts the Record into a StackDriver compatible Record
type StackDriverStream ¶
type StackDriverStream struct { LogID string Parent string KeyFilename string Key interface{} Converter Converter FilterLevel Level // contains filtered or unexported fields }
GCPStream is the Stream that writes to the standard output
func (*StackDriverStream) Close ¶ added in v1.3.0
func (stream *StackDriverStream) Close()
Close closes the stream
func (*StackDriverStream) Flush ¶
func (stream *StackDriverStream) Flush()
Flush flushes the stream (makes sure records are actually written)
func (*StackDriverStream) SetFilterLevel ¶ added in v1.3.0
func (stream *StackDriverStream) SetFilterLevel(level Level) Streamer
SetFilterLevel sets the filter level
func (*StackDriverStream) ShouldWrite ¶
func (stream *StackDriverStream) ShouldWrite(level Level) bool
ShouldWrite tells if the given level should be written to this stream
func (*StackDriverStream) String ¶
func (stream *StackDriverStream) String() string
String gets a string version
func (*StackDriverStream) Write ¶
func (stream *StackDriverStream) Write(record Record) (err error)
Write writes the given Record
type StderrStream ¶
type StderrStream struct { *json.Encoder Converter Converter FilterLevel Level // contains filtered or unexported fields }
StderrStream is the Stream that writes to the standard output
Example ¶
output := CaptureStderr(func() { stream := &logger.StderrStream{} if err := stream.Write(logger.NewRecord().Set("bello", "banana").Set("だれ", "Me")); err != nil { os.Stderr.WriteString(err.Error() + "\n") } if stream.ShouldWrite(logger.TRACE) { os.Stderr.WriteString("This should not be seen, stream Filter: " + stream.FilterLevel.String() + "\n") } stream.Flush() }) fmt.Println(output)
Output: {"bello":"banana","だれ":"Me"}
func (*StderrStream) Close ¶ added in v1.3.0
func (stream *StderrStream) Close()
Close closes the stream
func (*StderrStream) Flush ¶
func (stream *StderrStream) Flush()
Flush flushes the stream (makes sure records are actually written)
func (*StderrStream) SetFilterLevel ¶ added in v1.3.0
func (stream *StderrStream) SetFilterLevel(level Level) Streamer
SetFilterLevel sets the filter level
func (*StderrStream) ShouldWrite ¶
func (stream *StderrStream) ShouldWrite(level Level) bool
ShouldWrite tells if the given level should be written to this stream
func (*StderrStream) String ¶
func (stream *StderrStream) String() string
String gets a string version
func (*StderrStream) Write ¶
func (stream *StderrStream) Write(record Record) error
Write writes the given Record
type StdoutStream ¶
type StdoutStream struct { *json.Encoder Converter Converter FilterLevel Level Unbuffered bool // contains filtered or unexported fields }
StdoutStream is the Stream that writes to the standard output
Example ¶
os.Setenv("LOG_FLUSHFREQUENCY", "10ms") defer os.Unsetenv("LOG_FLUSHFREQUENCY") stream := &logger.StdoutStream{} if err := stream.Write(logger.NewRecord().Set("bello", "banana").Set("だれ", "Me")); err != nil { os.Stdout.WriteString(err.Error() + "\n") } if stream.ShouldWrite(logger.TRACE) { os.Stdout.WriteString("This should not be seen, stream Filter: " + stream.FilterLevel.String() + "\n") } if err := stream.Write(logger.NewRecord().Set("bello", "banana").Set("level", logger.ERROR)); err != nil { os.Stdout.WriteString(err.Error() + "\n") } stream.Flush() time.Sleep(11 * time.Millisecond) stream.Close()
Output: {"bello":"banana","だれ":"Me"} {"bello":"banana","level":50}
func (*StdoutStream) Close ¶ added in v1.3.0
func (stream *StdoutStream) Close()
Close closes the stream
func (*StdoutStream) Flush ¶
func (stream *StdoutStream) Flush()
Flush flushes the stream (makes sure records are actually written)
func (*StdoutStream) SetFilterLevel ¶ added in v1.3.0
func (stream *StdoutStream) SetFilterLevel(level Level) Streamer
SetFilterLevel sets the filter level
func (*StdoutStream) ShouldWrite ¶
func (stream *StdoutStream) ShouldWrite(level Level) bool
ShouldWrite tells if the given level should be written to this stream
func (*StdoutStream) String ¶
func (stream *StdoutStream) String() string
String gets a string version
func (*StdoutStream) Write ¶
func (stream *StdoutStream) Write(record Record) error
Write writes the given Record
type Streamer ¶
type Streamer interface { Write(record Record) error ShouldWrite(level Level) bool SetFilterLevel(level Level) Streamer Flush() Close() }
Streamer is the interface a Logger writes to
func CreateMultiStream ¶ added in v1.1.1
func CreateStreamWithDestination ¶ added in v1.1.0
CreateStreamWithDestination creates a new Streamer from a list of strings
"stdout" will create a StdoutStream
"stderr" will create a StderrStream
"nil", "null" will create a NilStream
"stackdriver" will create a StackDriverStream
"gcp", "googlecloud", "google" will create a StdoutStream, unbuffered, with the StackDriverConverter
"file:///path/to/file" or "path/to/file", "/path/to/file" will create a FileStream on the given location
If more than one string is given, a MultiStream of all Streams from strings is created.
If the environment variable DEBUG is set to 1, all Streams are created unbuffered.
If the list is empty, the environment variable LOG_DESTINATION is used.
Source Files ¶
- converter.go
- converter_bunyan.go
- converter_pino.go
- converter_stackdriver.go
- doc.go
- gettid-linux.go
- level.go
- logger-context.go
- logger-http.go
- logger.go
- record.go
- stream-file.go
- stream-logger.go
- stream-multistream.go
- stream-nil.go
- stream-stackdriver.go
- stream-stderr.go
- stream-stdout.go
- stream.go
- version.go