183 lines
3.6 KiB
Go
Executable File
183 lines
3.6 KiB
Go
Executable File
package ripsawlogger
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
LOG_INFO = "info"
|
|
LOG_WARN = "warn"
|
|
LOG_ERROR = "error"
|
|
LOG_AUDIT = "audit"
|
|
LOG_ACCESS = "access"
|
|
)
|
|
|
|
var (
|
|
levels = []string{LOG_INFO, LOG_WARN, LOG_ERROR, LOG_AUDIT, LOG_ACCESS}
|
|
defaultLogger Logger = new(multiLogger) // a list of loggers to send each log entry to
|
|
defaults = true // whether to append default data to logs
|
|
hostname, _ = os.Hostname()
|
|
)
|
|
|
|
// A LogEntry represents a single line of data destined for a log file and is a simple
|
|
// JSON-like map[string]interface{}. May include any arbitrary data.
|
|
type LogEntry struct {
|
|
Time time.Time
|
|
Level string
|
|
Contents map[string]interface{}
|
|
}
|
|
|
|
// The Logger interface is implemented by logging targets, which direct the log data to
|
|
// an appopriate place.
|
|
type Logger interface {
|
|
Log(data LogEntry)
|
|
Flush()
|
|
}
|
|
|
|
type multiLogger []Logger
|
|
|
|
func (ml multiLogger) Log(data LogEntry) {
|
|
|
|
// wrap in default properties
|
|
if defaults == true {
|
|
data.Contents["_hostname"] = hostname
|
|
data.Contents["_language"] = "golang"
|
|
}
|
|
|
|
for _, logger := range ml {
|
|
logger.Log(data)
|
|
}
|
|
}
|
|
|
|
func (ml multiLogger) Flush() {
|
|
for _, logger := range ml {
|
|
logger.Flush()
|
|
}
|
|
}
|
|
|
|
type filteredLogger struct {
|
|
logger Logger
|
|
levels []string
|
|
}
|
|
|
|
func NewFilteredLogger(log Logger, levels ...string) filteredLogger {
|
|
return filteredLogger{
|
|
logger: log,
|
|
levels: levels,
|
|
}
|
|
}
|
|
|
|
func (l filteredLogger) Log(data LogEntry) {
|
|
for _, v := range l.levels {
|
|
if data.Level == v {
|
|
l.logger.Log(data)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (l filteredLogger) Flush() {
|
|
// nothing
|
|
}
|
|
|
|
func NewStandardLogger() Logger {
|
|
return multiLogger([]Logger{
|
|
NewFilteredLogger(NewWriterLogger(os.Stdout), LOG_INFO, LOG_WARN, LOG_ERROR),
|
|
NewFilteredLogger(NewWriterLogger(ioutil.Discard), LOG_AUDIT, LOG_ACCESS),
|
|
})
|
|
// return NewWriterLogger(map[string]io.Writer{
|
|
// LOG_INFO: os.Stdout,
|
|
// LOG_WARN: os.Stdout,
|
|
// LOG_ERROR: os.Stderr,
|
|
// LOG_AUDIT: ioutil.Discard,
|
|
// LOG_ACCESS: ioutil.Discard,
|
|
// })
|
|
}
|
|
|
|
func Configure(loggers []Logger) {
|
|
logger := multiLogger(loggers)
|
|
defaultLogger = &logger
|
|
}
|
|
|
|
func EnableDefaults(def bool) {
|
|
defaults = def
|
|
}
|
|
|
|
func Info(args ...interface{}) {
|
|
log(newLogEntry(LOG_INFO, args...))
|
|
}
|
|
|
|
func Warn(args ...interface{}) {
|
|
log(newLogEntry(LOG_WARN, args...))
|
|
}
|
|
|
|
func Error(args ...interface{}) {
|
|
log(newLogEntry(LOG_ERROR, args...))
|
|
}
|
|
|
|
func Audit(args ...interface{}) {
|
|
log(newLogEntry(LOG_AUDIT, args...))
|
|
}
|
|
|
|
func Flush() {
|
|
defaultLogger.Flush()
|
|
}
|
|
|
|
func getDefaultLogger() Logger {
|
|
return defaultLogger
|
|
}
|
|
|
|
func log(entry LogEntry) {
|
|
defaultLogger.Log(entry)
|
|
}
|
|
|
|
// formats generic interface{} arguments into a map[string]interface{} that
|
|
// can be cleanly converted to JSON and logged.
|
|
func newLogEntry(level string, args ...interface{}) LogEntry {
|
|
|
|
baseObject := make(map[string]interface{}, 0)
|
|
|
|
var logTime time.Time
|
|
|
|
if len(args) > 0 {
|
|
|
|
var message string
|
|
|
|
// if the first argument is a time.Time object, use it as the message time
|
|
if time, ok := args[0].(time.Time); ok {
|
|
logTime = time
|
|
args = args[1:]
|
|
}
|
|
|
|
// if the final argument is a map, use that
|
|
if base, ok := args[len(args)-1].(map[string]interface{}); ok {
|
|
baseObject = base
|
|
args = args[:len(args)-1]
|
|
}
|
|
|
|
// if we still have any, find a way to messagize them
|
|
if len(args) > 0 {
|
|
|
|
if str, ok := args[0].(string); ok && str != "" {
|
|
message = fmt.Sprintf(str, args[1:]...)
|
|
} else {
|
|
message = fmt.Sprint(args...)
|
|
}
|
|
|
|
}
|
|
|
|
if message != "" {
|
|
baseObject["message"] = message
|
|
}
|
|
|
|
}
|
|
|
|
return LogEntry{
|
|
Time: logTime,
|
|
Level: level,
|
|
Contents: baseObject,
|
|
}
|
|
}
|