Content inside fengaldar
(The raw file follows this syntax highlighted file.)
// Fengaldar provides a logger, or, 'one who cuts trees'.
//
// The result of NewLogger().Println("Some message") looks like:
// INFO 2020-09-03T18:53:04-06:00 Some message
//
// The result of NewJSONLogger().Println("Some message") looks like:
// {"timestamp":"2020-09-03T18:53:04-06:00", "severity":"INFO", "message":"Some message"}
//
// The result of NewJSONLogger().Notice("Some message", LogContext{"extra":"info"}) looks like:
// {"timestamp": "2020-09-03T18:53:04-06:00", "severity": "INFO", "message": "Some message", "additionalContext": "{\"extra\":\"info\"}"}
//
// Of course you'll likely store the result of NewLogger() in a var so your code might look more like this:
// import ( fen "git.ondollo.com/fengaldar" )
// ...
// l := fen.NewLogger()
// ...
// l.Log("Something happened")
// ...
// l.Notice("Something interesting happened")
//
// You might also want to tweak the defaults with your own. That might look something like this:
// import ( fen "git.ondollo.com/fengaldar" )
// ...
// minSeverityToLog := fen.Warning
// defaultLogSeverity := fen.Notice
// customOptions := fen.Options{minSeverityToLog, defaultLogSeverity, time.RFC822}
// l := fen.NewLoggerWithOptions(customOptions)
// ...
// l.Log("Something happened") // This will be ignored due to having set 'minSeverityToLog' to a higher Severity than the default (which was provided in defaultLogSeverity)
// ...
// l.Notice("Something interesting happened") // This will also be ignored due to having set 'minSeverityToLog' to a higher Severity than the default (which was provided in defaultLogSeverity)
// ...
// l.Critical("Something caused a brief outage") // This will NOT be ignored
//
// You might also want to add some context to the log entry, and that might look something like this:
// import ( fen "git.ondollo.com/fengaldar" )
// ...
// l := fen.NewLogger()
// ...
// l.Log("Something happened", fen.LogContext{"somethingWasSetTo": "someValue"})
package fengaldar
import (
"fmt"
"os"
"strconv"
"strings"
"time"
)
// Severity represents the severity of the related log entry.
// Naming, ordering, and guidlines are based on severity consts found at https://pkg.go.dev/cloud.google.com/go/logging#pkg-constants
type Severity int
const (
Debug Severity = iota // Debug - debug or trace information.
Info // Info - routine information, such as ongoing status or performance.
Notice // Notice - normal but significant events, such as start up, shut down, or configuration.
Warning // Warning - events that might cause problems.
Error // Error - events that are likely to cause problems.
Critical // Critical - events that cause more severe problems or brief outages.
Alert // Alert - a person must take an action immediately.
Emergency // Emergency - one or more systems are unusable.
Panic // Panic - one or more systems are unusable and this package will throw a panic.
Fatal // Fatal - one or more systems are unusable and this package will call os.Exit(1).
)
var sevStrings = []string{
"DEBUG",
"INFO",
"NOTICE",
"WARNING",
"ERROR",
"CRITICAL",
"ALERT",
"EMERGENCY",
"PANIC",
"FATAL",
}
func (s Severity) String() string {
return sevStrings[s]
}
// LogContext provides a way to express additional context on a log entry.
type LogContext map[string]string
// LogContext.String will produce a unicode box-drawing prefix-wrapped output where each key in the LogContext is on it's own line
func (lc LogContext) String() string {
var s strings.Builder
s.WriteString(" \u250f\n")
for k, v := range lc {
s.WriteString(fmt.Sprintf(" \u2503 %q: %q\n", k, v))
}
s.WriteString(" \u2517")
return s.String()
}
// LogContext.JSON will produce a JSON object, where each key and value remain
// keys and values and are converted with fmt.Sprintf("%q")
func (lc LogContext) JSON() string {
var s strings.Builder
s.WriteString("{")
first := true
for k, v := range lc {
if !first {
s.WriteString(",")
}
s.WriteString(fmt.Sprintf("%q:%q", k, v))
first = false
}
s.WriteString("}")
return s.String()
}
// FOR ALL DECLARATIONS OF `...LogContext` ONLY THE FIRST LogContext WILL BE USED.
// This allows the LogContext to be optional.
//
// You might notice that Printf is not provided.
//
// In cases where you would use Printf you should explore constructing a LogContext to
// convey the additional data, or use fmt.Sprintf.
type Logger interface {
Time() string // Time is the result of applying the time.Layout specified in Options.TF in a call to time.Now().Format
Sev() Severity // Sev is the default severity used in Println and Log
Println(...interface{}) // For NewLogger, each parameter will be on it's own line. For NewJSONLogger, they're combined with a strings.Join("\n").
Log(string, ...LogContext) // FOR ALL DECLARATIONS OF `...LogContext` ONLY THE FIRST LogContext WILL BE USED
Debug(string, ...LogContext) // Debug - debug or trace information.
Info(string, ...LogContext) // Info - routine information, such as ongoing status or performance.
Notice(string, ...LogContext) // Notice - normal but significant events, such as start up, shut down, or configuration.
Warning(string, ...LogContext) // Warning - events that might cause problems.
Error(string, ...LogContext) // Error - events that are likely to cause problems.
Critical(string, ...LogContext) // Critical - events that cause more severe problems or brief outages.
Alert(string, ...LogContext) // Alert - a person must take an action immediately.
Emergency(string, ...LogContext) // Emergency - one or more systems are unusable.
Panic(string, ...LogContext) // Panic - one or more systems are unusable and this package will throw a panic.
Fatal(string, ...LogContext) // Fatal - one or more systems are unusable and this package will call os.Exit(1).
}
// Options allow you to specify different defaults for loggers
type Options struct {
Min Severity // The minimum severity to actually produce output. Any log entry with an associated severity *below* this will be ignored.
Sev Severity
// TF is the TimeFormat that will be used when producing log entries.
// This follows the time.Layout format seen here: https://pkg.go.dev/time#pkg-constants
TF string
}
var DefaultOptions = Options{
Min: Debug,
Sev: Info,
TF: time.RFC3339,
}
// simple
func NewLogger() Logger { return NewLoggerWithOptions(DefaultOptions) }
func NewLoggerWithOptions(o Options) Logger {
return &simpleLogger{
o.Min,
o.Sev,
o.TF,
strconv.Itoa(len(time.Now().Format(o.TF))),
}
}
type simpleLogger struct {
MS Severity
DS Severity
TF string
TS string
}
func (sl simpleLogger) Time() string { return time.Now().Format(sl.TF) }
func (sl simpleLogger) Sev() Severity { return sl.DS }
func (sl simpleLogger) line(sev Severity, s interface{}) string {
return fmt.Sprintf("%10.10s %"+sl.TS+"."+sl.TS+"s %s\n", sev, sl.Time(), s)
}
func (sl simpleLogger) Println(ss ...interface{}) {
if sl.Sev() < sl.MS {
return
}
for _, s := range ss {
fmt.Printf(sl.line(sl.Sev(), s))
}
}
func (sl simpleLogger) internal(sev Severity, s string, lc ...LogContext) {
if sev < sl.MS {
return
}
fmt.Printf(sl.line(sev, s))
if len(lc) > 0 {
fmt.Println(lc[0])
}
}
func (sl simpleLogger) Log(s string, lc ...LogContext) { sl.internal(sl.Sev(), s, lc...) }
func (sl simpleLogger) Debug(s string, lc ...LogContext) { sl.internal(Debug, s, lc...) }
func (sl simpleLogger) Info(s string, lc ...LogContext) { sl.internal(Info, s, lc...) }
func (sl simpleLogger) Notice(s string, lc ...LogContext) { sl.internal(Notice, s, lc...) }
func (sl simpleLogger) Warning(s string, lc ...LogContext) { sl.internal(Warning, s, lc...) }
func (sl simpleLogger) Error(s string, lc ...LogContext) { sl.internal(Error, s, lc...) }
func (sl simpleLogger) Critical(s string, lc ...LogContext) { sl.internal(Critical, s, lc...) }
func (sl simpleLogger) Alert(s string, lc ...LogContext) { sl.internal(Alert, s, lc...) }
func (sl simpleLogger) Emergency(s string, lc ...LogContext) { sl.internal(Emergency, s, lc...) }
func (sl simpleLogger) Panic(s string, lc ...LogContext) { sl.internal(Panic, s, lc...); panic(s) }
func (sl simpleLogger) Fatal(s string, lc ...LogContext) { sl.internal(Fatal, s, lc...); os.Exit(1) }
// JSON
func NewJSONLogger() Logger { return NewJSONLoggerWithOptions(DefaultOptions) }
func NewJSONLoggerWithOptions(o Options) Logger { return &jsonLogger{o.Min, o.Sev, o.TF} }
type jsonLogger struct {
MS Severity
DS Severity
TF string
}
func (jl jsonLogger) Time() string { return time.Now().Format(jl.TF) }
func (jl jsonLogger) Sev() Severity { return jl.DS }
func (jl jsonLogger) line(sev Severity, s interface{}) string {
return fmt.Sprintf(`"timestamp":%q, "severity":%q, "message":%q`, jl.Time(), sev, s)
}
func (jl jsonLogger) lineWithContext(s interface{}, lc LogContext) string {
return fmt.Sprintf(`%s, "additionalContext":%q`, jl.line(jl.Sev(), s), lc.JSON())
}
func (jl jsonLogger) Println(ss ...interface{}) {
if jl.Sev() < jl.MS {
return
}
var jss strings.Builder
for i, s := range ss {
if i > 0 {
jss.WriteString("\n")
}
jss.WriteString(s.(string))
}
fmt.Printf("{%s}\n", jl.line(jl.Sev(), jss.String()))
}
func (jl jsonLogger) internal(sev Severity, s string, lc ...LogContext) {
if sev < jl.MS {
return
}
if len(lc) == 0 {
fmt.Printf("{%s}\n", jl.line(sev, s))
} else {
fmt.Printf("{%s}\n", jl.lineWithContext(s, lc[0]))
}
}
func (jl jsonLogger) Log(s string, lc ...LogContext) { jl.internal(jl.Sev(), s, lc...) }
func (jl jsonLogger) Debug(s string, lc ...LogContext) { jl.internal(Debug, s, lc...) }
func (jl jsonLogger) Info(s string, lc ...LogContext) { jl.internal(Info, s, lc...) }
func (jl jsonLogger) Notice(s string, lc ...LogContext) { jl.internal(Notice, s, lc...) }
func (jl jsonLogger) Warning(s string, lc ...LogContext) { jl.internal(Warning, s, lc...) }
func (jl jsonLogger) Error(s string, lc ...LogContext) { jl.internal(Error, s, lc...) }
func (jl jsonLogger) Critical(s string, lc ...LogContext) { jl.internal(Critical, s, lc...) }
func (jl jsonLogger) Alert(s string, lc ...LogContext) { jl.internal(Alert, s, lc...) }
func (jl jsonLogger) Emergency(s string, lc ...LogContext) { jl.internal(Emergency, s, lc...) }
func (jl jsonLogger) Panic(s string, lc ...LogContext) { jl.internal(Panic, s, lc...); panic(s) }
func (jl jsonLogger) Fatal(s string, lc ...LogContext) { jl.internal(Fatal, s, lc...); os.Exit(1) }
// Fengaldar provides a logger, or, 'one who cuts trees'. // // The result of NewLogger().Println("Some message") looks like: // INFO 2020-09-03T18:53:04-06:00 Some message // // The result of NewJSONLogger().Println("Some message") looks like: // {"timestamp":"2020-09-03T18:53:04-06:00", "severity":"INFO", "message":"Some message"} // // The result of NewJSONLogger().Notice("Some message", LogContext{"extra":"info"}) looks like: // {"timestamp": "2020-09-03T18:53:04-06:00", "severity": "INFO", "message": "Some message", "additionalContext": "{\"extra\":\"info\"}"} // // Of course you'll likely store the result of NewLogger() in a var so your code might look more like this: // import ( fen "git.ondollo.com/fengaldar" ) // ... // l := fen.NewLogger() // ... // l.Log("Something happened") // ... // l.Notice("Something interesting happened") // // You might also want to tweak the defaults with your own. That might look something like this: // import ( fen "git.ondollo.com/fengaldar" ) // ... // minSeverityToLog := fen.Warning // defaultLogSeverity := fen.Notice // customOptions := fen.Options{minSeverityToLog, defaultLogSeverity, time.RFC822} // l := fen.NewLoggerWithOptions(customOptions) // ... // l.Log("Something happened") // This will be ignored due to having set 'minSeverityToLog' to a higher Severity than the default (which was provided in defaultLogSeverity) // ... // l.Notice("Something interesting happened") // This will also be ignored due to having set 'minSeverityToLog' to a higher Severity than the default (which was provided in defaultLogSeverity) // ... // l.Critical("Something caused a brief outage") // This will NOT be ignored // // You might also want to add some context to the log entry, and that might look something like this: // import ( fen "git.ondollo.com/fengaldar" ) // ... // l := fen.NewLogger() // ... // l.Log("Something happened", fen.LogContext{"somethingWasSetTo": "someValue"}) package fengaldar import ( "fmt" "os" "strconv" "strings" "time" ) // Severity represents the severity of the related log entry. // Naming, ordering, and guidlines are based on severity consts found at https://pkg.go.dev/cloud.google.com/go/logging#pkg-constants type Severity int const ( Debug Severity = iota // Debug - debug or trace information. Info // Info - routine information, such as ongoing status or performance. Notice // Notice - normal but significant events, such as start up, shut down, or configuration. Warning // Warning - events that might cause problems. Error // Error - events that are likely to cause problems. Critical // Critical - events that cause more severe problems or brief outages. Alert // Alert - a person must take an action immediately. Emergency // Emergency - one or more systems are unusable. Panic // Panic - one or more systems are unusable and this package will throw a panic. Fatal // Fatal - one or more systems are unusable and this package will call os.Exit(1). ) var sevStrings = []string{ "DEBUG", "INFO", "NOTICE", "WARNING", "ERROR", "CRITICAL", "ALERT", "EMERGENCY", "PANIC", "FATAL", } func (s Severity) String() string { return sevStrings[s] } // LogContext provides a way to express additional context on a log entry. type LogContext map[string]string // LogContext.String will produce a unicode box-drawing prefix-wrapped output where each key in the LogContext is on it's own line func (lc LogContext) String() string { var s strings.Builder s.WriteString(" \u250f\n") for k, v := range lc { s.WriteString(fmt.Sprintf(" \u2503 %q: %q\n", k, v)) } s.WriteString(" \u2517") return s.String() } // LogContext.JSON will produce a JSON object, where each key and value remain // keys and values and are converted with fmt.Sprintf("%q") func (lc LogContext) JSON() string { var s strings.Builder s.WriteString("{") first := true for k, v := range lc { if !first { s.WriteString(",") } s.WriteString(fmt.Sprintf("%q:%q", k, v)) first = false } s.WriteString("}") return s.String() } // FOR ALL DECLARATIONS OF `...LogContext` ONLY THE FIRST LogContext WILL BE USED. // This allows the LogContext to be optional. // // You might notice that Printf is not provided. // // In cases where you would use Printf you should explore constructing a LogContext to // convey the additional data, or use fmt.Sprintf. type Logger interface { Time() string // Time is the result of applying the time.Layout specified in Options.TF in a call to time.Now().Format Sev() Severity // Sev is the default severity used in Println and Log Println(...interface{}) // For NewLogger, each parameter will be on it's own line. For NewJSONLogger, they're combined with a strings.Join("\n"). Log(string, ...LogContext) // FOR ALL DECLARATIONS OF `...LogContext` ONLY THE FIRST LogContext WILL BE USED Debug(string, ...LogContext) // Debug - debug or trace information. Info(string, ...LogContext) // Info - routine information, such as ongoing status or performance. Notice(string, ...LogContext) // Notice - normal but significant events, such as start up, shut down, or configuration. Warning(string, ...LogContext) // Warning - events that might cause problems. Error(string, ...LogContext) // Error - events that are likely to cause problems. Critical(string, ...LogContext) // Critical - events that cause more severe problems or brief outages. Alert(string, ...LogContext) // Alert - a person must take an action immediately. Emergency(string, ...LogContext) // Emergency - one or more systems are unusable. Panic(string, ...LogContext) // Panic - one or more systems are unusable and this package will throw a panic. Fatal(string, ...LogContext) // Fatal - one or more systems are unusable and this package will call os.Exit(1). } // Options allow you to specify different defaults for loggers type Options struct { Min Severity // The minimum severity to actually produce output. Any log entry with an associated severity *below* this will be ignored. Sev Severity // TF is the TimeFormat that will be used when producing log entries. // This follows the time.Layout format seen here: https://pkg.go.dev/time#pkg-constants TF string } var DefaultOptions = Options{ Min: Debug, Sev: Info, TF: time.RFC3339, } // simple func NewLogger() Logger { return NewLoggerWithOptions(DefaultOptions) } func NewLoggerWithOptions(o Options) Logger { return &simpleLogger{ o.Min, o.Sev, o.TF, strconv.Itoa(len(time.Now().Format(o.TF))), } } type simpleLogger struct { MS Severity DS Severity TF string TS string } func (sl simpleLogger) Time() string { return time.Now().Format(sl.TF) } func (sl simpleLogger) Sev() Severity { return sl.DS } func (sl simpleLogger) line(sev Severity, s interface{}) string { return fmt.Sprintf("%10.10s %"+sl.TS+"."+sl.TS+"s %s\n", sev, sl.Time(), s) } func (sl simpleLogger) Println(ss ...interface{}) { if sl.Sev() < sl.MS { return } for _, s := range ss { fmt.Printf(sl.line(sl.Sev(), s)) } } func (sl simpleLogger) internal(sev Severity, s string, lc ...LogContext) { if sev < sl.MS { return } fmt.Printf(sl.line(sev, s)) if len(lc) > 0 { fmt.Println(lc[0]) } } func (sl simpleLogger) Log(s string, lc ...LogContext) { sl.internal(sl.Sev(), s, lc...) } func (sl simpleLogger) Debug(s string, lc ...LogContext) { sl.internal(Debug, s, lc...) } func (sl simpleLogger) Info(s string, lc ...LogContext) { sl.internal(Info, s, lc...) } func (sl simpleLogger) Notice(s string, lc ...LogContext) { sl.internal(Notice, s, lc...) } func (sl simpleLogger) Warning(s string, lc ...LogContext) { sl.internal(Warning, s, lc...) } func (sl simpleLogger) Error(s string, lc ...LogContext) { sl.internal(Error, s, lc...) } func (sl simpleLogger) Critical(s string, lc ...LogContext) { sl.internal(Critical, s, lc...) } func (sl simpleLogger) Alert(s string, lc ...LogContext) { sl.internal(Alert, s, lc...) } func (sl simpleLogger) Emergency(s string, lc ...LogContext) { sl.internal(Emergency, s, lc...) } func (sl simpleLogger) Panic(s string, lc ...LogContext) { sl.internal(Panic, s, lc...); panic(s) } func (sl simpleLogger) Fatal(s string, lc ...LogContext) { sl.internal(Fatal, s, lc...); os.Exit(1) } // JSON func NewJSONLogger() Logger { return NewJSONLoggerWithOptions(DefaultOptions) } func NewJSONLoggerWithOptions(o Options) Logger { return &jsonLogger{o.Min, o.Sev, o.TF} } type jsonLogger struct { MS Severity DS Severity TF string } func (jl jsonLogger) Time() string { return time.Now().Format(jl.TF) } func (jl jsonLogger) Sev() Severity { return jl.DS } func (jl jsonLogger) line(sev Severity, s interface{}) string { return fmt.Sprintf(`"timestamp":%q, "severity":%q, "message":%q`, jl.Time(), sev, s) } func (jl jsonLogger) lineWithContext(s interface{}, lc LogContext) string { return fmt.Sprintf(`%s, "additionalContext":%q`, jl.line(jl.Sev(), s), lc.JSON()) } func (jl jsonLogger) Println(ss ...interface{}) { if jl.Sev() < jl.MS { return } var jss strings.Builder for i, s := range ss { if i > 0 { jss.WriteString("\n") } jss.WriteString(s.(string)) } fmt.Printf("{%s}\n", jl.line(jl.Sev(), jss.String())) } func (jl jsonLogger) internal(sev Severity, s string, lc ...LogContext) { if sev < jl.MS { return } if len(lc) == 0 { fmt.Printf("{%s}\n", jl.line(sev, s)) } else { fmt.Printf("{%s}\n", jl.lineWithContext(s, lc[0])) } } func (jl jsonLogger) Log(s string, lc ...LogContext) { jl.internal(jl.Sev(), s, lc...) } func (jl jsonLogger) Debug(s string, lc ...LogContext) { jl.internal(Debug, s, lc...) } func (jl jsonLogger) Info(s string, lc ...LogContext) { jl.internal(Info, s, lc...) } func (jl jsonLogger) Notice(s string, lc ...LogContext) { jl.internal(Notice, s, lc...) } func (jl jsonLogger) Warning(s string, lc ...LogContext) { jl.internal(Warning, s, lc...) } func (jl jsonLogger) Error(s string, lc ...LogContext) { jl.internal(Error, s, lc...) } func (jl jsonLogger) Critical(s string, lc ...LogContext) { jl.internal(Critical, s, lc...) } func (jl jsonLogger) Alert(s string, lc ...LogContext) { jl.internal(Alert, s, lc...) } func (jl jsonLogger) Emergency(s string, lc ...LogContext) { jl.internal(Emergency, s, lc...) } func (jl jsonLogger) Panic(s string, lc ...LogContext) { jl.internal(Panic, s, lc...); panic(s) } func (jl jsonLogger) Fatal(s string, lc ...LogContext) { jl.internal(Fatal, s, lc...); os.Exit(1) }