package ripsawlogger import ( "encoding/json" "errors" "fmt" "time" ) var ( // A simple syntax that displays the timestamp and the message field. Can // be useful when the exact format of the log entry is not known, or when // entries of various formats are recorded. MessageFormat = HumanSyntax{ String: "%s: %s", Tokens: []string{ "_timestamp", "message", }, } // This represents Common Log Format, similar to how HTTP and nginx record // access logs. Defined here for convenience. CommonLogFormat = HumanSyntax{ String: "%s %s %s [%s] \"%s %s HTTP/%s\" %d %d", Tokens: []string{ "host", "rfc931", "username", "_timestamp", "method", "url", "httpVersion", "statusCode", "bytes", }, } ) type HumanLoggerLevel struct { Color string Syntax HumanSyntax } type HumanSyntax struct { String string Tokens []string } func (syntax HumanSyntax) Format(entry LogEntry) string { // if we have no syntax string, just try to pull a message if syntax.String == "" { if msg, ok := entry.Contents["message"]; ok { stringified, _ := json.Marshal(msg) return string(stringified) } // just format the JSON I guess stringified, _ := json.Marshal(entry.Contents) return string(stringified) } // gather tokens values := make([]interface{}, 0, len(syntax.Tokens)) for _, token := range syntax.Tokens { var resolvedToken interface{} = "-" if value, err := syntax.extract(token, entry); err == nil { resolvedToken = value } values = append(values, resolvedToken) } return fmt.Sprintf(syntax.String, values...) } func (syntax HumanSyntax) extract(path string, entry LogEntry) (interface{}, error) { switch path { case "_timestamp": if entry.Time.IsZero() { return time.Now().Format(time.RFC3339Nano), nil } else { return entry.Time.Format(time.RFC3339Nano), nil } case "_level": return entry.Level, nil default: if value, ok := entry.Contents[path]; ok { return value, nil } } return "", errors.New("Token not found") } type humanLogger map[string]HumanLoggerLevel // Creates a logger that formats log data in a human-readable way, and can be configured // on a per-log-level basis. func NewHumanLogger(cfg map[string]HumanLoggerLevel) Logger { return humanLogger(cfg) } func (sl humanLogger) Log(entry LogEntry) { if cfg, ok := sl[entry.Level]; ok { fmt.Printf("[%s] %s\n", entry.Level, cfg.Syntax.Format(entry)) } } func (sl humanLogger) Flush() { // ¯\_(ツ)_/¯ }