logging is now compatible with klog.
See https://git.kyleisom.net/lib/libklogger
This commit is contained in:
		
							parent
							
								
									419f23d655
								
							
						
					
					
						commit
						d1452f54c0
					
				| 
						 | 
					@ -0,0 +1,14 @@
 | 
				
			||||||
 | 
					package logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Console is a Logger that writes to the console. It must be
 | 
				
			||||||
 | 
					// constructed with a call to NewConsole.
 | 
				
			||||||
 | 
					type Console struct {
 | 
				
			||||||
 | 
						*LogWriter
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewConsole returns a new console logger.
 | 
				
			||||||
 | 
					func NewConsole() *Console {
 | 
				
			||||||
 | 
						return &Console{LogWriter: NewLogWriter(os.Stdout, os.Stderr)}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,15 @@
 | 
				
			||||||
 | 
					// Package logging implements attribute-based logging. Log entries
 | 
				
			||||||
 | 
					// consist of timestamps, an actor and event string, and a mapping of
 | 
				
			||||||
 | 
					// string key-value attribute pairs. For example,
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//   log.Error("serialiser", "failed to open file",
 | 
				
			||||||
 | 
					//             map[string]string{
 | 
				
			||||||
 | 
					//                     "error": err.Error(),
 | 
				
			||||||
 | 
					//                     "path": "data.bin",
 | 
				
			||||||
 | 
					//             })
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This produces the output message
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//   [2016-04-01T15:04:30-0700] [ERROR] [actor:serialiser event:failed to open file] error=is a directory path=data.bin
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					package logging
 | 
				
			||||||
| 
						 | 
					@ -1,75 +1,43 @@
 | 
				
			||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"os"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/kisom/goutils/logging"
 | 
						"github.com/kisom/goutils/logging"
 | 
				
			||||||
	"github.com/kisom/testio"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var log = logging.Init()
 | 
					var log = logging.NewConsole()
 | 
				
			||||||
var olog, _ = logging.New("subsystem #42", logging.LevelNotice)
 | 
					var olog = logging.NewConsole()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
	exampleNewWriters()
 | 
						log.Info("example", "Hello, world.", nil)
 | 
				
			||||||
	log.Notice("Hello, world.")
 | 
						log.Warn("example", "this program is about to end", nil)
 | 
				
			||||||
	log.Warning("this program is about to end")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log.SetLevel(logging.LevelDebug)
 | 
						log.Critical("example", "screaming into the void", nil)
 | 
				
			||||||
	log.Debug("hello world")
 | 
						olog.Critical("other", "can anyone hear me?", nil)
 | 
				
			||||||
	log.SetLevel(logging.LevelNotice)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	olog.Print("now online")
 | 
						log.Warn("example", "but not for long", nil)
 | 
				
			||||||
	logging.Suppress("olog")
 | 
					 | 
				
			||||||
	olog.Print("extraneous information")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	logging.Enable("olog")
 | 
						log.Info("example", "fare thee well", nil)
 | 
				
			||||||
	olog.Print("relevant now")
 | 
						olog.Info("other", "all good journeys must come to an end",
 | 
				
			||||||
 | 
							map[string]string{"when": time.Now().String()})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	logging.SuppressAll()
 | 
						log.Info("example", "filelog test", nil)
 | 
				
			||||||
	log.Alert("screaming into the void")
 | 
					 | 
				
			||||||
	olog.Critical("can anyone hear me?")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	log.Enable()
 | 
					 | 
				
			||||||
	log.Notice("i'm baaack")
 | 
					 | 
				
			||||||
	log.Suppress()
 | 
					 | 
				
			||||||
	log.Warning("but not for long")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	logging.EnableAll()
 | 
					 | 
				
			||||||
	log.Notice("fare thee well")
 | 
					 | 
				
			||||||
	olog.Print("all good journeys must come to an end")
 | 
					 | 
				
			||||||
	exampleNewFromFile()
 | 
						exampleNewFromFile()
 | 
				
			||||||
}
 | 
						os.Remove("example.log")
 | 
				
			||||||
 | 
						os.Remove("example.err")
 | 
				
			||||||
func exampleNewWriters() {
 | 
					 | 
				
			||||||
	o := testio.NewBufCloser(nil)
 | 
					 | 
				
			||||||
	e := testio.NewBufCloser(nil)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlog, _ := logging.NewFromWriters("writers", logging.DefaultLevel, o, e)
 | 
					 | 
				
			||||||
	wlog.Notice("hello, world")
 | 
					 | 
				
			||||||
	wlog.Notice("some more things happening")
 | 
					 | 
				
			||||||
	wlog.Warning("something suspicious has happened")
 | 
					 | 
				
			||||||
	wlog.Alert("pick up that can, Citizen!")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	fmt.Println("--- BEGIN OUT ---")
 | 
					 | 
				
			||||||
	fmt.Printf("%s", o.Bytes())
 | 
					 | 
				
			||||||
	fmt.Println("--- END OUT ---")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	fmt.Println("--- BEGIN ERR ---")
 | 
					 | 
				
			||||||
	fmt.Printf("%s", e.Bytes())
 | 
					 | 
				
			||||||
	fmt.Println("--- END ERR ---")
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func exampleNewFromFile() {
 | 
					func exampleNewFromFile() {
 | 
				
			||||||
	flog, err := logging.NewFromFile("file logger", logging.LevelNotice,
 | 
						flog, err := logging.NewSplitFile("example.log", "example.err", true)
 | 
				
			||||||
		"example.log", "example.err", true)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatalf("failed to open logger: %v", err)
 | 
							log.Fatal("filelog", "failed to open logger",
 | 
				
			||||||
 | 
								map[string]string{"error": err.Error()})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer flog.Close()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	flog.Notice("hello, world")
 | 
						flog.Info("filelog", "hello, world", nil)
 | 
				
			||||||
	flog.Notice("some more things happening")
 | 
						flog.Info("filelog", "some more things happening", nil)
 | 
				
			||||||
	flog.Warning("something suspicious has happened")
 | 
						flog.Warn("filelog", "something suspicious has happened", nil)
 | 
				
			||||||
	flog.Alert("pick up that can, Citizen!")
 | 
						flog.Critical("filelog", "pick up that can, Citizen!", nil)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,44 +1,37 @@
 | 
				
			||||||
package logging_test
 | 
					package logging_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "github.com/kisom/goutils/logging"
 | 
					import (
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var log = logging.Init()
 | 
						"github.com/kisom/goutils/logging"
 | 
				
			||||||
var olog, _ = logging.New("subsystem #42", logging.LevelNotice)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var log = logging.NewConsole()
 | 
				
			||||||
 | 
					var olog = logging.NewConsole()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Example() {
 | 
					func Example() {
 | 
				
			||||||
	log.Notice("Hello, world.")
 | 
						log.Info("example", "Hello, world.", nil)
 | 
				
			||||||
	log.Warning("this program is about to end")
 | 
						log.Warn("example", "this program is about to end", nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	olog.Print("now online")
 | 
						log.Critical("example", "screaming into the void", nil)
 | 
				
			||||||
	logging.Suppress("olog")
 | 
						olog.Critical("other", "can anyone hear me?", nil)
 | 
				
			||||||
	olog.Print("extraneous information")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	logging.Enable("olog")
 | 
						log.Warn("example", "but not for long", nil)
 | 
				
			||||||
	olog.Print("relevant now")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	logging.SuppressAll()
 | 
						log.Info("example", "fare thee well", nil)
 | 
				
			||||||
	log.Alert("screaming into the void")
 | 
						olog.Info("example", "all good journeys must come to an end",
 | 
				
			||||||
	olog.Critical("can anyone hear me?")
 | 
							map[string]string{"when": time.Now().String()})
 | 
				
			||||||
 | 
					 | 
				
			||||||
	log.Enable()
 | 
					 | 
				
			||||||
	log.Notice("i'm baaack")
 | 
					 | 
				
			||||||
	log.Suppress()
 | 
					 | 
				
			||||||
	log.Warning("but not for long")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	logging.EnableAll()
 | 
					 | 
				
			||||||
	log.Notice("fare thee well")
 | 
					 | 
				
			||||||
	olog.Print("all good journeys must come to an end")
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ExampleNewFromFile() {
 | 
					func ExampleNewFromFile() {
 | 
				
			||||||
	log, err := logging.NewFromFile("file logger", logging.LevelNotice,
 | 
						flog, err := logging.NewSplitFile("example.log", "example.err", true)
 | 
				
			||||||
		"example.log", "example.err", true)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatalf("failed to open logger: %v", err)
 | 
							log.Fatal("filelog", "failed to open logger",
 | 
				
			||||||
 | 
								map[string]string{"error": err.Error()})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log.Notice("hello, world")
 | 
						flog.Info("filelog", "hello, world", nil)
 | 
				
			||||||
	log.Notice("some more things happening")
 | 
						flog.Info("filelog", "some more things happening", nil)
 | 
				
			||||||
	log.Warning("something suspicious has happened")
 | 
						flog.Warn("filelog", "something suspicious has happened", nil)
 | 
				
			||||||
	log.Alert("pick up that can, Citizen!")
 | 
						flog.Critical("filelog", "pick up that can, Citizen!", nil)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,73 @@
 | 
				
			||||||
 | 
					package logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// File writes its logs to file.
 | 
				
			||||||
 | 
					type File struct {
 | 
				
			||||||
 | 
						fo, fe *os.File
 | 
				
			||||||
 | 
						*LogWriter
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (fl *File) Close() {
 | 
				
			||||||
 | 
						fl.fo.Close()
 | 
				
			||||||
 | 
						if fl.fe != nil {
 | 
				
			||||||
 | 
							fl.fe.Close()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewFile creates a new Logger that writes all logs to the file
 | 
				
			||||||
 | 
					// specified by path. If overwrite is specified, the log file will be
 | 
				
			||||||
 | 
					// truncated before writing. Otherwise, the log file will be appended
 | 
				
			||||||
 | 
					// to.
 | 
				
			||||||
 | 
					func NewFile(path string, overwrite bool) (*File, error) {
 | 
				
			||||||
 | 
						fl := new(File)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if overwrite {
 | 
				
			||||||
 | 
							fl.fo, err = os.Create(path)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							fl.fo, err = os.OpenFile(path, os.O_WRONLY|os.O_APPEND, 0644)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fl.LogWriter = NewLogWriter(fl.fo, fl.fo)
 | 
				
			||||||
 | 
						return fl, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewSplitFile creates a new Logger that writes debug and information
 | 
				
			||||||
 | 
					// messages to the output file, and warning and higher messages to the
 | 
				
			||||||
 | 
					// error file. If overwrite is specified, the log files will be
 | 
				
			||||||
 | 
					// truncated before writing.
 | 
				
			||||||
 | 
					func NewSplitFile(outpath, errpath string, overwrite bool) (*File, error) {
 | 
				
			||||||
 | 
						fl := new(File)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if overwrite {
 | 
				
			||||||
 | 
							fl.fo, err = os.Create(outpath)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							fl.fo, err = os.OpenFile(outpath, os.O_WRONLY|os.O_APPEND, 0644)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if overwrite {
 | 
				
			||||||
 | 
							fl.fe, err = os.Create(errpath)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							fl.fe, err = os.OpenFile(errpath, os.O_WRONLY|os.O_APPEND, 0644)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fl.Close()
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fl.LogWriter = NewLogWriter(fl.fo, fl.fe)
 | 
				
			||||||
 | 
						return fl, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,5 @@
 | 
				
			||||||
package logging
 | 
					package logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
	"runtime"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// A Level represents a logging level.
 | 
					// A Level represents a logging level.
 | 
				
			||||||
type Level uint8
 | 
					type Level uint8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,15 +7,11 @@ type Level uint8
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	// LevelDebug are debug output useful during program testing
 | 
						// LevelDebug are debug output useful during program testing
 | 
				
			||||||
	// and debugging.
 | 
						// and debugging.
 | 
				
			||||||
	LevelDebug = iota
 | 
						LevelDebug = 1 << iota
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// LevelInfo is used for informational messages.
 | 
						// LevelInfo is used for informational messages.
 | 
				
			||||||
	LevelInfo
 | 
						LevelInfo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// LevelNotice is for messages that are normal but
 | 
					 | 
				
			||||||
	// significant.
 | 
					 | 
				
			||||||
	LevelNotice
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// LevelWarning is for messages that are warning conditions:
 | 
						// LevelWarning is for messages that are warning conditions:
 | 
				
			||||||
	// they're not indicative of a failure, but of a situation
 | 
						// they're not indicative of a failure, but of a situation
 | 
				
			||||||
	// that may lead to a failure later.
 | 
						// that may lead to a failure later.
 | 
				
			||||||
| 
						 | 
					@ -35,15 +24,13 @@ const (
 | 
				
			||||||
	// LevelCritical are messages for critical conditions.
 | 
						// LevelCritical are messages for critical conditions.
 | 
				
			||||||
	LevelCritical
 | 
						LevelCritical
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// LevelAlert are for messages indicating that action
 | 
					 | 
				
			||||||
	// must be taken immediately.
 | 
					 | 
				
			||||||
	LevelAlert
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// LevelFatal messages are akin to syslog's LOG_EMERG: the
 | 
						// LevelFatal messages are akin to syslog's LOG_EMERG: the
 | 
				
			||||||
	// system is unusable and cannot continue execution.
 | 
						// system is unusable and cannot continue execution.
 | 
				
			||||||
	LevelFatal
 | 
						LevelFatal
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const DefaultLevel = LevelInfo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Cheap integer to fixed-width decimal ASCII.  Give a negative width
 | 
					// Cheap integer to fixed-width decimal ASCII.  Give a negative width
 | 
				
			||||||
// to avoid zero-padding. (From log/log.go in the standard library).
 | 
					// to avoid zero-padding. (From log/log.go in the standard library).
 | 
				
			||||||
func itoa(i int, wid int) string {
 | 
					func itoa(i int, wid int) string {
 | 
				
			||||||
| 
						 | 
					@ -70,168 +57,13 @@ func writeToOut(level Level) bool {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var levelPrefix = [...]string{
 | 
					var levelPrefix = [...]string{
 | 
				
			||||||
	LevelDebug:    "[DEBUG] ",
 | 
						LevelDebug:    "DEBUG",
 | 
				
			||||||
	LevelInfo:     "[INFO] ",
 | 
						LevelInfo:     "INFO",
 | 
				
			||||||
	LevelNotice:   "[NOTICE] ",
 | 
						LevelWarning:  "WARNING",
 | 
				
			||||||
	LevelWarning:  "[WARNING] ",
 | 
						LevelError:    "ERROR",
 | 
				
			||||||
	LevelError:    "[ERROR] ",
 | 
						LevelCritical: "CRITICAL",
 | 
				
			||||||
	LevelCritical: "[CRITICAL] ",
 | 
						LevelFatal:    "FATAL",
 | 
				
			||||||
	LevelAlert:    "[ALERT] ",
 | 
					 | 
				
			||||||
	LevelFatal:    "[FATAL] ",
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DateFormat contains the default date format string used by the logger.
 | 
					// DateFormat contains the default date format string used by the logger.
 | 
				
			||||||
var DateFormat = "2006-01-02T15:03:04-0700"
 | 
					const DateFormat = "2006-01-02T15:03:04-0700"
 | 
				
			||||||
 | 
					 | 
				
			||||||
func (l *Logger) outputf(level Level, format string, v []interface{}) {
 | 
					 | 
				
			||||||
	if !l.Enabled() {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if level >= l.level {
 | 
					 | 
				
			||||||
		domain := l.domain
 | 
					 | 
				
			||||||
		if level == LevelDebug {
 | 
					 | 
				
			||||||
			_, file, line, ok := runtime.Caller(2)
 | 
					 | 
				
			||||||
			if ok {
 | 
					 | 
				
			||||||
				domain += " " + file + ":" + itoa(line, -1)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		format = fmt.Sprintf("%s %s: %s%s\n",
 | 
					 | 
				
			||||||
			time.Now().Format(DateFormat),
 | 
					 | 
				
			||||||
			domain, levelPrefix[level], format)
 | 
					 | 
				
			||||||
		if writeToOut(level) {
 | 
					 | 
				
			||||||
			fmt.Fprintf(l.out, format, v...)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			fmt.Fprintf(l.err, format, v...)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (l *Logger) output(level Level, v []interface{}) {
 | 
					 | 
				
			||||||
	if !l.Enabled() {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if level >= l.level {
 | 
					 | 
				
			||||||
		domain := l.domain
 | 
					 | 
				
			||||||
		if level == LevelDebug {
 | 
					 | 
				
			||||||
			_, file, line, ok := runtime.Caller(2)
 | 
					 | 
				
			||||||
			if ok {
 | 
					 | 
				
			||||||
				domain += " " + file + ":" + itoa(line, -1)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		format := fmt.Sprintf("%s %s: %s",
 | 
					 | 
				
			||||||
			time.Now().Format(DateFormat),
 | 
					 | 
				
			||||||
			domain, levelPrefix[level])
 | 
					 | 
				
			||||||
		if writeToOut(level) {
 | 
					 | 
				
			||||||
			fmt.Fprintf(l.out, format)
 | 
					 | 
				
			||||||
			fmt.Fprintln(l.out, v...)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			fmt.Fprintf(l.err, format)
 | 
					 | 
				
			||||||
			fmt.Fprintln(l.err, v...)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Fatalf logs a formatted message at the "fatal" level and then exits. The
 | 
					 | 
				
			||||||
// arguments are handled in the same manner as fmt.Printf.
 | 
					 | 
				
			||||||
func (l *Logger) Fatalf(format string, v ...interface{}) {
 | 
					 | 
				
			||||||
	l.outputf(LevelFatal, format, v)
 | 
					 | 
				
			||||||
	os.Exit(1)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Fatal logs its arguments at the "fatal" level and then exits.
 | 
					 | 
				
			||||||
func (l *Logger) Fatal(v ...interface{}) {
 | 
					 | 
				
			||||||
	l.output(LevelFatal, v)
 | 
					 | 
				
			||||||
	os.Exit(1)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Alertf logs a formatted message at the "alert" level. The
 | 
					 | 
				
			||||||
// arguments are handled in the same manner as fmt.Printf.
 | 
					 | 
				
			||||||
func (l *Logger) Alertf(format string, v ...interface{}) {
 | 
					 | 
				
			||||||
	l.outputf(LevelAlert, format, v)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Alert logs its arguments at the "alert" level.
 | 
					 | 
				
			||||||
func (l *Logger) Alert(v ...interface{}) {
 | 
					 | 
				
			||||||
	l.output(LevelAlert, v)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Criticalf logs a formatted message at the "critical" level. The
 | 
					 | 
				
			||||||
// arguments are handled in the same manner as fmt.Printf.
 | 
					 | 
				
			||||||
func (l *Logger) Criticalf(format string, v ...interface{}) {
 | 
					 | 
				
			||||||
	l.outputf(LevelCritical, format, v)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Critical logs its arguments at the "critical" level.
 | 
					 | 
				
			||||||
func (l *Logger) Critical(v ...interface{}) {
 | 
					 | 
				
			||||||
	l.output(LevelCritical, v)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Errorf logs a formatted message at the "error" level. The arguments
 | 
					 | 
				
			||||||
// are handled in the same manner as fmt.Printf.
 | 
					 | 
				
			||||||
func (l *Logger) Errorf(format string, v ...interface{}) {
 | 
					 | 
				
			||||||
	l.outputf(LevelError, format, v)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Error logs its arguments at the "error" level.
 | 
					 | 
				
			||||||
func (l *Logger) Error(v ...interface{}) {
 | 
					 | 
				
			||||||
	l.output(LevelError, v)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Warningf logs a formatted message at the "warning" level. The
 | 
					 | 
				
			||||||
// arguments are handled in the same manner as fmt.Printf.
 | 
					 | 
				
			||||||
func (l *Logger) Warningf(format string, v ...interface{}) {
 | 
					 | 
				
			||||||
	l.outputf(LevelWarning, format, v)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Warning logs its arguments at the "warning" level.
 | 
					 | 
				
			||||||
func (l *Logger) Warning(v ...interface{}) {
 | 
					 | 
				
			||||||
	l.output(LevelWarning, v)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Noticef logs a formatted message at the "notice" level. The arguments
 | 
					 | 
				
			||||||
// are handled in the same manner as fmt.Printf.
 | 
					 | 
				
			||||||
func (l *Logger) Noticef(format string, v ...interface{}) {
 | 
					 | 
				
			||||||
	l.outputf(LevelNotice, format, v)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Notice logs its arguments at the "notice" level.
 | 
					 | 
				
			||||||
func (l *Logger) Notice(v ...interface{}) {
 | 
					 | 
				
			||||||
	l.output(LevelNotice, v)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Infof logs a formatted message at the "info" level. The arguments
 | 
					 | 
				
			||||||
// are handled in the same manner as fmt.Printf.
 | 
					 | 
				
			||||||
func (l *Logger) Infof(format string, v ...interface{}) {
 | 
					 | 
				
			||||||
	l.outputf(LevelInfo, format, v)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Info logs its arguments at the "info" level.
 | 
					 | 
				
			||||||
func (l *Logger) Info(v ...interface{}) {
 | 
					 | 
				
			||||||
	l.output(LevelInfo, v)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Debugf logs a formatted message at the "debug" level. The arguments
 | 
					 | 
				
			||||||
// are handled in the same manner as fmt.Printf. Note that debug
 | 
					 | 
				
			||||||
// logging will print the current
 | 
					 | 
				
			||||||
func (l *Logger) Debugf(format string, v ...interface{}) {
 | 
					 | 
				
			||||||
	l.outputf(LevelDebug, format, v)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Debug logs its arguments at the "debug" level.
 | 
					 | 
				
			||||||
func (l *Logger) Debug(v ...interface{}) {
 | 
					 | 
				
			||||||
	l.output(LevelDebug, v)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Printf prints a formatted message at the default level.
 | 
					 | 
				
			||||||
func (l *Logger) Printf(format string, v ...interface{}) {
 | 
					 | 
				
			||||||
	l.outputf(DefaultLevel, format, v)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Print prints its arguments at the default level.
 | 
					 | 
				
			||||||
func (l *Logger) Print(v ...interface{}) {
 | 
					 | 
				
			||||||
	l.output(DefaultLevel, v)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										457
									
								
								logging/log.go
								
								
								
								
							
							
						
						
									
										457
									
								
								logging/log.go
								
								
								
								
							| 
						 | 
					@ -1,274 +1,279 @@
 | 
				
			||||||
// Package logging provides domain-based logging in the same style as
 | 
					 | 
				
			||||||
// sylog. Domains are some name for which logging can be selectively
 | 
					 | 
				
			||||||
// enabled or disabled. Logging also differentiates between normal
 | 
					 | 
				
			||||||
// messages (which are sent to standard output) and errors, which are
 | 
					 | 
				
			||||||
// sent to standard error; debug messages will also include the file
 | 
					 | 
				
			||||||
// and line number.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Domains are intended for identifying logging subystems. A domain
 | 
					 | 
				
			||||||
// can be suppressed with Suppress, and re-enabled with Enable. There
 | 
					 | 
				
			||||||
// are prefixed versions of these as well.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Packages (e.g. those meant to be imported by programs) using the
 | 
					 | 
				
			||||||
// loggers here should observe a few etiquette guides:
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// 1. A package should never suppress or enable other loggers except
 | 
					 | 
				
			||||||
// via an exported function that should be called by the end user.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// 2. A package should never call the global `SetLevel`; this is
 | 
					 | 
				
			||||||
// reserved for the end user.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// 3. Packages should use consistent, sane domains: preferably,
 | 
					 | 
				
			||||||
// related packages should use an unsurprising common prefix in their
 | 
					 | 
				
			||||||
// domains.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// This package was adapted from the CFSSL logging code.
 | 
					 | 
				
			||||||
package logging
 | 
					package logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
						"time"
 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"sync"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/kisom/goutils/mwc"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var logConfig = struct {
 | 
					// Logger provides a standardised logging interface.
 | 
				
			||||||
	registered map[string]*Logger
 | 
					type Logger interface {
 | 
				
			||||||
	lock       *sync.Mutex
 | 
						// SetLevel sets the minimum log level.
 | 
				
			||||||
}{
 | 
						SetLevel(Level)
 | 
				
			||||||
	registered: map[string]*Logger{},
 | 
					
 | 
				
			||||||
	lock:       new(sync.Mutex),
 | 
						// Good returns true if the Logger is healthy.
 | 
				
			||||||
 | 
						Good() bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Status returns an error corresponding to the logger's state;
 | 
				
			||||||
 | 
						// if it's healthy (e.g. Good() returns true), Error will
 | 
				
			||||||
 | 
						// return nil.
 | 
				
			||||||
 | 
						Status() error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Close gives the Logger the opportunity to perform any cleanup.
 | 
				
			||||||
 | 
						Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Log messages consist of four components:
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// 1. The **level** attaches a notion of priority to the log message.
 | 
				
			||||||
 | 
						//    Several log levels are available:
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						//    + FATAL (32): the system is in an unsuable state, and cannot
 | 
				
			||||||
 | 
						//      continue to run. Most of the logging for this will cause the
 | 
				
			||||||
 | 
						//      program to exit with an error code.
 | 
				
			||||||
 | 
						//    + CRITICAL (16): critical conditions. The error, if uncorrected, is
 | 
				
			||||||
 | 
						//      likely to cause a fatal condition shortly.  An example is running
 | 
				
			||||||
 | 
						//      out of disk space. This is something that the ops team should get
 | 
				
			||||||
 | 
						//      paged for.
 | 
				
			||||||
 | 
						//    + ERROR (8): error conditions. A single error doesn't require an
 | 
				
			||||||
 | 
						//      ops team to be paged, but repeated errors should often trigger a
 | 
				
			||||||
 | 
						//      page based on threshold triggers. An example is a network
 | 
				
			||||||
 | 
						//      failure: it might be a transient failure (these do happen), but
 | 
				
			||||||
 | 
						//      most of the time it's self-correcting.
 | 
				
			||||||
 | 
						//    + WARNING (4): warning conditions. An example of this is a bad
 | 
				
			||||||
 | 
						//      request sent to a server. This isn't an error on the part of the
 | 
				
			||||||
 | 
						//      program, but it may be indicative of other things. Like errors,
 | 
				
			||||||
 | 
						//      the ops team shouldn't be paged for errors, but a page might be
 | 
				
			||||||
 | 
						//      triggered if a certain threshold of warnings is reached (which is
 | 
				
			||||||
 | 
						//      typically much higher than errors). For example, repeated
 | 
				
			||||||
 | 
						//      warnings might be a sign that the system is under attack.
 | 
				
			||||||
 | 
						//    + INFO (2): informational message. This is a normal log message
 | 
				
			||||||
 | 
						//      that is used to deliver information, such as recording
 | 
				
			||||||
 | 
						//      requests. Ops teams are never paged for informational
 | 
				
			||||||
 | 
						//      messages. This is the default log level.
 | 
				
			||||||
 | 
						//    + DEBUG (1): debug-level message. These are only used during
 | 
				
			||||||
 | 
						//      development or if a deployed system repeatedly sees abnormal
 | 
				
			||||||
 | 
						//      errors.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						//    The numeric values indicate the priority of a given level.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// 2. The **actor** is used to specify which component is generating
 | 
				
			||||||
 | 
						//    the log message. This could be the program name, or it could be
 | 
				
			||||||
 | 
						//    a specific component inside the system.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// 3. The **event** is a short message indicating what happened. This is
 | 
				
			||||||
 | 
						//    most like the traditional log message.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// 4. The **attributes** are an optional set of key-value string pairs that
 | 
				
			||||||
 | 
						//    provide additional information.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// Additionally, each log message has an associated timestamp. For the
 | 
				
			||||||
 | 
						// text-based logs, this is "%FT%T%z"; for the binary logs, this is a
 | 
				
			||||||
 | 
						// 64-bit Unix timestamp. An example text-based timestamp might look like ::
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						//   [2016-03-27T20:59:27-0700] [INFO] [actor:server event:request received] client=192.168.2.5 request-size=839
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// Note that this is organised in a manner that facilitates parsing::
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						//   /\[(\d{4}-\d{3}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4})\] \[(\w+\)]\) \[actor:(.+?) event:(.+?)\]/
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// will cover the header:
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// + ``$1`` contains the timestamp
 | 
				
			||||||
 | 
						// + ``$2`` contains the level
 | 
				
			||||||
 | 
						// + ``$3`` contains the actor
 | 
				
			||||||
 | 
						// + ``$4`` contains the event
 | 
				
			||||||
 | 
						Debug(actor, event string, attrs map[string]string)
 | 
				
			||||||
 | 
						Info(actor, event string, attrs map[string]string)
 | 
				
			||||||
 | 
						Warn(actor, event string, attrs map[string]string)
 | 
				
			||||||
 | 
						Error(actor, event string, attrs map[string]string)
 | 
				
			||||||
 | 
						Critical(actor, event string, attrs map[string]string)
 | 
				
			||||||
 | 
						Fatal(actor, event string, attrs map[string]string)
 | 
				
			||||||
 | 
						FatalCode(exitcode int, actor, event string, attrs map[string]string)
 | 
				
			||||||
 | 
						FatalNoDie(actor, event string, attrs map[string]string)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetLevel sets the logging level for all loggers.
 | 
					// A LogWriter is a Logger that operates on an io.Writer.
 | 
				
			||||||
func SetLevel(level Level) {
 | 
					type LogWriter struct {
 | 
				
			||||||
	logConfig.lock.Lock()
 | 
						wo, we io.Writer
 | 
				
			||||||
	defer logConfig.lock.Unlock()
 | 
						lvl    Level
 | 
				
			||||||
 | 
						state  error
 | 
				
			||||||
 | 
						snl    bool // suppress newline
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, l := range logConfig.registered {
 | 
					// NewLogWriter takes an output writer (wo) and an error writer (we),
 | 
				
			||||||
		l.SetLevel(level)
 | 
					// and produces a new Logger. If the error writer is nil, error logs
 | 
				
			||||||
 | 
					// will be multiplexed onto the output writer.
 | 
				
			||||||
 | 
					func NewLogWriter(wo, we io.Writer) *LogWriter {
 | 
				
			||||||
 | 
						if we == nil {
 | 
				
			||||||
 | 
							we = wo
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &LogWriter{
 | 
				
			||||||
 | 
							wo:    wo,
 | 
				
			||||||
 | 
							we:    we,
 | 
				
			||||||
 | 
							lvl:   DefaultLevel,
 | 
				
			||||||
 | 
							state: nil,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DefaultLevel defaults to the notice level of logging.
 | 
					func (lw *LogWriter) output(w io.Writer, lvl Level, actor, event string, attrs map[string]string) {
 | 
				
			||||||
const DefaultLevel = LevelNotice
 | 
						t := time.Now().Format(DateFormat)
 | 
				
			||||||
 | 
						fmt.Fprintf(w, "[%s] [%s] [actor:%s event:%s]", t, levelPrefix[lvl], actor, event)
 | 
				
			||||||
// Init returns a new default logger. The domain is set to the
 | 
						for k, v := range attrs {
 | 
				
			||||||
// program's name, and the default logging level is used.
 | 
							fmt.Fprintf(w, " %s=%s", k, v)
 | 
				
			||||||
func Init() *Logger {
 | 
					 | 
				
			||||||
	l, _ := New(filepath.Base(os.Args[0]), DefaultLevel)
 | 
					 | 
				
			||||||
	return l
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// A Logger writes logs on behalf of a particular domain at a certain
 | 
					 | 
				
			||||||
// level.
 | 
					 | 
				
			||||||
type Logger struct {
 | 
					 | 
				
			||||||
	enabled bool
 | 
					 | 
				
			||||||
	lock    *sync.Mutex
 | 
					 | 
				
			||||||
	domain  string
 | 
					 | 
				
			||||||
	level   Level
 | 
					 | 
				
			||||||
	out     io.WriteCloser
 | 
					 | 
				
			||||||
	err     io.WriteCloser
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Close closes the log's writers and suppresses the logger.
 | 
					 | 
				
			||||||
func (l *Logger) Close() error {
 | 
					 | 
				
			||||||
	Suppress(l.domain)
 | 
					 | 
				
			||||||
	err := l.out.Close()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return l.err.Close()
 | 
						if !lw.snl {
 | 
				
			||||||
}
 | 
							fmt.Fprintf(w, "\n")
 | 
				
			||||||
 | 
					 | 
				
			||||||
// Suppress ignores logs from a specific domain.
 | 
					 | 
				
			||||||
func Suppress(domain string) {
 | 
					 | 
				
			||||||
	logConfig.lock.Lock()
 | 
					 | 
				
			||||||
	defer logConfig.lock.Unlock()
 | 
					 | 
				
			||||||
	l, ok := logConfig.registered[domain]
 | 
					 | 
				
			||||||
	if ok {
 | 
					 | 
				
			||||||
		l.Suppress()
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SuppressPrefix suppress logs whose domain is prefixed with the
 | 
					// Debug emits a debug-level message. These are only used during
 | 
				
			||||||
// prefix.
 | 
					// development or if a deployed system repeatedly sees abnormal
 | 
				
			||||||
func SuppressPrefix(prefix string) {
 | 
					// errors.
 | 
				
			||||||
	logConfig.lock.Lock()
 | 
					//
 | 
				
			||||||
	defer logConfig.lock.Unlock()
 | 
					// Actor specifies the component emitting the message; event indicates
 | 
				
			||||||
	for domain, l := range logConfig.registered {
 | 
					// the event that caused the log message to be emitted. attrs is a map
 | 
				
			||||||
		if strings.HasPrefix(domain, prefix) {
 | 
					// of key-value string pairs that can be used to provide additional
 | 
				
			||||||
			l.Suppress()
 | 
					// information.
 | 
				
			||||||
		}
 | 
					func (lw *LogWriter) Debug(actor, event string, attrs map[string]string) {
 | 
				
			||||||
 | 
						if lw.lvl > LevelDebug {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						lw.output(lw.wo, LevelDebug, actor, event, attrs)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SuppressAll suppresses all logging output.
 | 
					// Info emits an informational message. This is a normal log message
 | 
				
			||||||
func SuppressAll() {
 | 
					// that is used to deliver information, such as recording
 | 
				
			||||||
	logConfig.lock.Lock()
 | 
					// requests. Ops teams are never paged for informational
 | 
				
			||||||
	defer logConfig.lock.Unlock()
 | 
					// messages. This is the default log level.
 | 
				
			||||||
	for _, l := range logConfig.registered {
 | 
					//
 | 
				
			||||||
		l.Suppress()
 | 
					// Actor specifies the component emitting the message; event indicates
 | 
				
			||||||
 | 
					// the event that caused the log message to be emitted. attrs is a map
 | 
				
			||||||
 | 
					// of key-value string pairs that can be used to provide additional
 | 
				
			||||||
 | 
					// information.
 | 
				
			||||||
 | 
					func (lw *LogWriter) Info(actor, event string, attrs map[string]string) {
 | 
				
			||||||
 | 
						if lw.lvl > LevelInfo {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						lw.output(lw.wo, LevelInfo, actor, event, attrs)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Enable enables logs from a specific domain.
 | 
					// Warn emits a warning message. An example of this is a bad request
 | 
				
			||||||
func Enable(domain string) {
 | 
					// sent to a server. This isn't an error on the part of the program,
 | 
				
			||||||
	logConfig.lock.Lock()
 | 
					// but it may be indicative of other things. Like errors, the ops team
 | 
				
			||||||
	defer logConfig.lock.Unlock()
 | 
					// shouldn't be paged for errors, but a page might be triggered if a
 | 
				
			||||||
	l, ok := logConfig.registered[domain]
 | 
					// certain threshold of warnings is reached (which is typically much
 | 
				
			||||||
	if ok {
 | 
					// higher than errors). For example, repeated warnings might be a sign
 | 
				
			||||||
		l.Enable()
 | 
					// that the system is under attack.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Actor specifies the component emitting the message; event indicates
 | 
				
			||||||
 | 
					// the event that caused the log message to be emitted. attrs is a map
 | 
				
			||||||
 | 
					// of key-value string pairs that can be used to provide additional
 | 
				
			||||||
 | 
					// information.
 | 
				
			||||||
 | 
					func (lw *LogWriter) Warn(actor, event string, attrs map[string]string) {
 | 
				
			||||||
 | 
						if lw.lvl > LevelWarning {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						lw.output(lw.we, LevelWarning, actor, event, attrs)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// EnablePrefix enables logs whose domain is prefixed with prefix.
 | 
					// Error emits an error message. A single error doesn't require an ops
 | 
				
			||||||
func EnablePrefix(prefix string) {
 | 
					// team to be paged, but repeated errors should often trigger a page
 | 
				
			||||||
	logConfig.lock.Lock()
 | 
					// based on threshold triggers. An example is a network failure: it
 | 
				
			||||||
	defer logConfig.lock.Unlock()
 | 
					// might be a transient failure (these do happen), but most of the
 | 
				
			||||||
	for domain, l := range logConfig.registered {
 | 
					// time it's self-correcting.
 | 
				
			||||||
		if strings.HasPrefix(domain, prefix) {
 | 
					//
 | 
				
			||||||
			l.Enable()
 | 
					// Actor specifies the component emitting the message; event indicates
 | 
				
			||||||
		}
 | 
					// the event that caused the log message to be emitted. attrs is a map
 | 
				
			||||||
 | 
					// of key-value string pairs that can be used to provide additional
 | 
				
			||||||
 | 
					// information.
 | 
				
			||||||
 | 
					func (lw *LogWriter) Error(actor, event string, attrs map[string]string) {
 | 
				
			||||||
 | 
						if lw.lvl > LevelError {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						lw.output(lw.we, LevelError, actor, event, attrs)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// EnableAll enables all domains.
 | 
					// Critical emits a message indicating a critical condition. The
 | 
				
			||||||
func EnableAll() {
 | 
					// error, if uncorrected, is likely to cause a fatal condition
 | 
				
			||||||
	logConfig.lock.Lock()
 | 
					// shortly.  An example is running out of disk space. This is
 | 
				
			||||||
	defer logConfig.lock.Unlock()
 | 
					// something that the ops team should get paged for.
 | 
				
			||||||
	for _, l := range logConfig.registered {
 | 
					//
 | 
				
			||||||
		l.Enable()
 | 
					// Actor specifies the component emitting the message; event indicates
 | 
				
			||||||
 | 
					// the event that caused the log message to be emitted. attrs is a map
 | 
				
			||||||
 | 
					// of key-value string pairs that can be used to provide additional
 | 
				
			||||||
 | 
					// information.
 | 
				
			||||||
 | 
					func (lw *LogWriter) Critical(actor, event string, attrs map[string]string) {
 | 
				
			||||||
 | 
						if lw.lvl > LevelCritical {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						lw.output(lw.we, LevelCritical, actor, event, attrs)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// New returns a new logger that writes to standard output for Notice
 | 
					// Fatal emits a message indicating that the system is in an unsuable
 | 
				
			||||||
// and below and standard error for levels above Notice. If a logger
 | 
					// state, and cannot continue to run. The program will exit with exit
 | 
				
			||||||
// with the same domain exists, the logger will set its level to level
 | 
					// code 1.
 | 
				
			||||||
// and return the logger; in this case, the registered return value
 | 
					//
 | 
				
			||||||
// will be true.
 | 
					// Actor specifies the component emitting the message; event indicates
 | 
				
			||||||
func New(domain string, level Level) (l *Logger, registered bool) {
 | 
					// the event that caused the log message to be emitted. attrs is a map
 | 
				
			||||||
	logConfig.lock.Lock()
 | 
					// of key-value string pairs that can be used to provide additional
 | 
				
			||||||
	defer logConfig.lock.Unlock()
 | 
					// information.
 | 
				
			||||||
 | 
					func (lw *LogWriter) Fatal(actor, event string, attrs map[string]string) {
 | 
				
			||||||
	l = logConfig.registered[domain]
 | 
						if lw.lvl > LevelFatal {
 | 
				
			||||||
	if l != nil {
 | 
							return
 | 
				
			||||||
		l.SetLevel(level)
 | 
					 | 
				
			||||||
		return l, true
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						lw.output(lw.we, LevelFatal, actor, event, attrs)
 | 
				
			||||||
	l = &Logger{
 | 
						os.Exit(1)
 | 
				
			||||||
		domain: domain,
 | 
					 | 
				
			||||||
		level:  level,
 | 
					 | 
				
			||||||
		out:    os.Stdout,
 | 
					 | 
				
			||||||
		err:    os.Stderr,
 | 
					 | 
				
			||||||
		lock:   new(sync.Mutex),
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	l.Enable()
 | 
					 | 
				
			||||||
	logConfig.registered[domain] = l
 | 
					 | 
				
			||||||
	return l, false
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewFromWriters returns a new logger that writes to the w io.WriteCloser
 | 
					// Fatal emits a message indicating that the system is in an unsuable
 | 
				
			||||||
// for Notice and below and to the e io.WriteCloser for levels above
 | 
					// state, and cannot continue to run. The program will exit with the
 | 
				
			||||||
// Notice. If e is nil, w will be used. If a logger with the same
 | 
					// exit code speicfied in the exitcode argument.
 | 
				
			||||||
// domain exists, the logger will set its level to level and return
 | 
					//
 | 
				
			||||||
// the logger; in this case, the registered return value will be true.
 | 
					// Actor specifies the component emitting the message; event indicates
 | 
				
			||||||
func NewFromWriters(domain string, level Level, w, e io.WriteCloser) (l *Logger, registered bool) {
 | 
					// the event that caused the log message to be emitted. attrs is a map
 | 
				
			||||||
	logConfig.lock.Lock()
 | 
					// of key-value string pairs that can be used to provide additional
 | 
				
			||||||
	defer logConfig.lock.Unlock()
 | 
					// information.
 | 
				
			||||||
 | 
					func (lw *LogWriter) FatalCode(exitcode int, actor, event string, attrs map[string]string) {
 | 
				
			||||||
	l = logConfig.registered[domain]
 | 
						if lw.lvl > LevelFatal {
 | 
				
			||||||
	if l != nil {
 | 
							return
 | 
				
			||||||
		l.SetLevel(level)
 | 
					 | 
				
			||||||
		return l, true
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						lw.output(lw.we, LevelFatal, actor, event, attrs)
 | 
				
			||||||
	if w == nil {
 | 
						os.Exit(exitcode)
 | 
				
			||||||
		w = os.Stdout
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if e == nil {
 | 
					 | 
				
			||||||
		e = w
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	l = &Logger{
 | 
					 | 
				
			||||||
		domain: domain,
 | 
					 | 
				
			||||||
		level:  level,
 | 
					 | 
				
			||||||
		out:    w,
 | 
					 | 
				
			||||||
		err:    e,
 | 
					 | 
				
			||||||
		lock:   new(sync.Mutex),
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	l.Enable()
 | 
					 | 
				
			||||||
	logConfig.registered[domain] = l
 | 
					 | 
				
			||||||
	return l, false
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewFromFile returns a new logger that opens the files for writing. If
 | 
					// Fatal emits a message indicating that the system is in an unsuable
 | 
				
			||||||
// multiplex is true, output will be multiplexed to standard output
 | 
					// state, and cannot continue to run. The program will not exit; it is
 | 
				
			||||||
// and standard error as well.
 | 
					// assumed that the caller has some final clean up to perform.
 | 
				
			||||||
func NewFromFile(domain string, level Level, outFile, errFile string, multiplex bool, flags int) (*Logger, error) {
 | 
					//
 | 
				
			||||||
	l := &Logger{
 | 
					// Actor specifies the component emitting the message; event indicates
 | 
				
			||||||
		domain: domain,
 | 
					// the event that caused the log message to be emitted. attrs is a map
 | 
				
			||||||
		level:  level,
 | 
					// of key-value string pairs that can be used to provide additional
 | 
				
			||||||
		lock:   new(sync.Mutex),
 | 
					// information.
 | 
				
			||||||
 | 
					func (lw *LogWriter) FatalNoDie(actor, event string, attrs map[string]string) {
 | 
				
			||||||
 | 
						if lw.lvl > LevelFatal {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						lw.output(lw.we, LevelFatal, actor, event, attrs)
 | 
				
			||||||
	outf, err := os.OpenFile(outFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY|flags, 0644)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	errf, err := os.OpenFile(errFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY|flags, 0644)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if multiplex {
 | 
					 | 
				
			||||||
		l.out = mwc.MultiWriteCloser(outf, os.Stdout)
 | 
					 | 
				
			||||||
		l.err = mwc.MultiWriteCloser(errf, os.Stderr)
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		l.out = outf
 | 
					 | 
				
			||||||
		l.err = errf
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Enable(domain)
 | 
					 | 
				
			||||||
	return l, nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Enable allows output from the logger.
 | 
					// Good returns true if the logger is healthy.
 | 
				
			||||||
func (l *Logger) Enable() {
 | 
					func (lw *LogWriter) Good() bool {
 | 
				
			||||||
	l.lock.Lock()
 | 
						return lw.state == nil
 | 
				
			||||||
	defer l.lock.Unlock()
 | 
					 | 
				
			||||||
	l.enabled = true
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Enabled returns true if the logger is enabled.
 | 
					// Status returns an error value from the logger if it isn't healthy,
 | 
				
			||||||
func (l *Logger) Enabled() bool {
 | 
					// or nil if the logger is healthy.
 | 
				
			||||||
	return l.enabled
 | 
					func (lw *LogWriter) Status() error {
 | 
				
			||||||
 | 
						return lw.state
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Suppress ignores output from the logger.
 | 
					// SetLevel changes the log level.
 | 
				
			||||||
func (l *Logger) Suppress() {
 | 
					func (lw *LogWriter) SetLevel(l Level) {
 | 
				
			||||||
	l.lock.Lock()
 | 
						lw.lvl = l
 | 
				
			||||||
	defer l.lock.Unlock()
 | 
					 | 
				
			||||||
	l.enabled = false
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Domain returns the domain of the logger.
 | 
					// Close is a no-op that satisfies the Logger interface.
 | 
				
			||||||
func (l *Logger) Domain() string {
 | 
					func (lw *LogWriter) Close() {}
 | 
				
			||||||
	return l.domain
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// SetLevel changes the level of the logger.
 | 
					 | 
				
			||||||
func (l *Logger) SetLevel(level Level) {
 | 
					 | 
				
			||||||
	l.lock.Lock()
 | 
					 | 
				
			||||||
	defer l.lock.Unlock()
 | 
					 | 
				
			||||||
	l.level = level
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,55 @@
 | 
				
			||||||
 | 
					package logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// A list of implementations that should be tested.
 | 
				
			||||||
 | 
					var implementations []Logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						lw := NewLogWriter(&bytes.Buffer{}, nil)
 | 
				
			||||||
 | 
						cw := NewConsole()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						implementations = append(implementations, lw)
 | 
				
			||||||
 | 
						implementations = append(implementations, cw)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestFileSetup(t *testing.T) {
 | 
				
			||||||
 | 
						fw1, err := NewFile("fw1.log", true)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("failed to create new file logger: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fw2, err := NewSplitFile("fw2.log", "fw2.err", true)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("failed to create new split file logger: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						implementations = append(implementations, fw1)
 | 
				
			||||||
 | 
						implementations = append(implementations, fw2)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestImplementations(t *testing.T) {
 | 
				
			||||||
 | 
						for _, l := range implementations {
 | 
				
			||||||
 | 
							l.Info("TestImplementations", "Info message",
 | 
				
			||||||
 | 
								map[string]string{"type": fmt.Sprintf("%T", l)})
 | 
				
			||||||
 | 
							l.Warn("TestImplementations", "Warning message",
 | 
				
			||||||
 | 
								map[string]string{"type": fmt.Sprintf("%T", l)})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestCloseLoggers(t *testing.T) {
 | 
				
			||||||
 | 
						for _, l := range implementations {
 | 
				
			||||||
 | 
							l.Close()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDestroyLogFiles(t *testing.T) {
 | 
				
			||||||
 | 
						os.Remove("fw1.log")
 | 
				
			||||||
 | 
						os.Remove("fw2.log")
 | 
				
			||||||
 | 
						os.Remove("fw2.err")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue