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-constantstype Severity intconst (
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 linefunc(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 := truefor 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 loggerstype 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,
}
// simplefuncNewLogger() Logger { return NewLoggerWithOptions(DefaultOptions) }
funcNewLoggerWithOptions(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))
iflen(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) }
// JSONfuncNewJSONLogger() Logger { return NewJSONLoggerWithOptions(DefaultOptions) }
funcNewJSONLoggerWithOptions(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
}
iflen(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) }
The raw file follows...
// 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) }