vanilla.go (5301B)
1 package logging 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "os" 8 "path" 9 "runtime" 10 ) 11 12 var ( 13 // LogWriter is used as io.Writer the Vanilla Logger implementation. 14 LogWriter io.Writer = os.Stderr 15 ) 16 17 // Vanilla is a basic single-line structured output logger for terminal output. 18 type Vanilla struct { 19 domain string 20 levelFilter int 21 ctxkey []string 22 } 23 24 // NewVanilla creates a new Vanilla logger. 25 func NewVanilla() Vanilla { 26 return Vanilla{ 27 domain: "main", 28 levelFilter: LogLevel, 29 } 30 } 31 32 // WithDomain sets the logging domain. It is prepended to the caller file/line information. 33 func (v Vanilla) WithDomain(domain string) Vanilla { 34 v.domain = domain 35 return v 36 } 37 38 // WithLevel overrides the globally set loglevel for the logger instance. 39 func (v Vanilla) WithLevel(level int) Vanilla { 40 v.levelFilter = level 41 return v 42 } 43 44 func (v Vanilla) WithContextKey(k string) Vanilla { 45 v.ctxkey = append(v.ctxkey, k) 46 return v 47 } 48 49 // Printf logs to the global writer. 50 func (v Vanilla) Printf(level int, msg string, args ...any) { 51 v.Writef(LogWriter, level, msg, args...) 52 } 53 54 // compile log line from inputs and send to given writer. 55 func (v Vanilla) writef(ctx context.Context, w io.Writer, file string, line int, level int, msg string, args ...any) { 56 var argsStr string 57 if level > v.levelFilter { 58 return 59 } 60 if ctx == nil { 61 argsStr = v.argsToString(nil, args) 62 } else { 63 argsStr = v.argsToString(ctx, args) 64 } 65 66 if len(msg) > 0 { 67 fmt.Fprintf(w, "[%s] %s:%s:%v %s\t%s\n", AsString(level), v.domain, file, line, msg, argsStr) 68 } else { 69 fmt.Fprintf(w, "[%s] %s:%s:%v %s\n", AsString(level), v.domain, file, line, argsStr) 70 } 71 } 72 73 // Writef logs to the given writer. 74 func (v Vanilla) Writef(w io.Writer, level int, msg string, args ...any) { 75 file, line := getCaller(2) 76 v.writef(nil, w, file, line, level, msg, args) 77 } 78 79 // WriteCtxf logs with context to the given writer. 80 func (v Vanilla) WriteCtxf(ctx context.Context, w io.Writer, level int, msg string, args ...any) { 81 file, line := getCaller(2) 82 v.writef(ctx, w, file, line, level, msg, args...) 83 } 84 85 // get caller information and pass on to writef 86 func (v Vanilla) printf(level int, msg string, args ...any) { 87 file, line := getCaller(3) 88 v.writef(nil, LogWriter, file, line, level, msg, args...) 89 } 90 91 // get caller information and pass on to writef 92 func (v Vanilla) printCtxf(ctx context.Context, level int, msg string, args ...any) { 93 file, line := getCaller(3) 94 v.writef(ctx, LogWriter, file, line, level, msg, args...) 95 } 96 97 // PrintCtxf logs with context to the global writer. 98 func (v Vanilla) PrintCtxf(ctx context.Context, level int, msg string, args ...any) { 99 v.printf(level, msg, args...) 100 } 101 102 // Tracef logs a line with level TRACE to the global writer. 103 func (v Vanilla) Tracef(msg string, args ...any) { 104 v.printf(LVL_TRACE, msg, args...) 105 } 106 107 // Debugf logs a line with level DEBUG to the global writer. 108 func (v Vanilla) Debugf(msg string, args ...any) { 109 v.printf(LVL_DEBUG, msg, args...) 110 } 111 112 // Infof logs a line with level INFO to the global writer. 113 func (v Vanilla) Infof(msg string, args ...any) { 114 v.printf(LVL_INFO, msg, args...) 115 } 116 117 // Warnf logs a line with level WARN to the global writer. 118 func (v Vanilla) Warnf(msg string, args ...any) { 119 v.printf(LVL_WARN, msg, args...) 120 } 121 122 // Errorf logs a line with level ERROR to the global writer. 123 func (v Vanilla) Errorf(msg string, args ...any) { 124 v.printf(LVL_ERROR, msg, args...) 125 } 126 127 // TraceCtxf logs a line with context with level TRACE to the global writer. 128 func (v Vanilla) TraceCtxf(ctx context.Context, msg string, args ...any) { 129 v.printCtxf(ctx, LVL_TRACE, msg, args...) 130 } 131 132 // DebugCtxf logs a line with context with level DEBUG to the global writer. 133 func (v Vanilla) DebugCtxf(ctx context.Context, msg string, args ...any) { 134 v.printCtxf(ctx, LVL_DEBUG, msg, args...) 135 } 136 137 // InfoCtxf logs a line with context with level INFO to the global writer. 138 func (v Vanilla) InfoCtxf(ctx context.Context, msg string, args ...any) { 139 v.printCtxf(ctx, LVL_INFO, msg, args...) 140 } 141 142 // WarnCtxf logs a line with context with level WARN to the global writer. 143 func (v Vanilla) WarnCtxf(ctx context.Context, msg string, args ...any) { 144 v.printCtxf(ctx, LVL_WARN, msg, args...) 145 } 146 147 // ErrorCtxf logs a line with context with level ERROR to the global writer. 148 func (v Vanilla) ErrorCtxf(ctx context.Context, msg string, args ...any) { 149 v.printCtxf(ctx, LVL_ERROR, msg, args...) 150 } 151 152 // return file basename and line for caller information. 153 func getCaller(depth int) (string, int) { 154 var file string 155 var line int 156 _, file, line, _ = runtime.Caller(depth) 157 baseFile := path.Base(file) 158 return baseFile, line 159 } 160 161 // string representation of the given structured log args. 162 func (v Vanilla) argsToString(ctx context.Context, args []any) string { 163 var s string 164 165 if ctx != nil { 166 for _, k := range v.ctxkey { 167 v := ctx.Value(k) 168 if v != nil { 169 v, ok := v.(string) 170 if ok { 171 args = append(args, "x-"+k, v) 172 } 173 } 174 } 175 } 176 c := len(args) 177 var i int 178 for i = 0; i < c; i += 2 { 179 if len(s) > 0 { 180 s += ", " 181 } 182 183 if i+1 < c { 184 var argByte []byte 185 var ok bool 186 argByte, ok = args[i+1].([]byte) 187 if ok { 188 s += fmt.Sprintf("%s=%x", args[i], argByte) 189 } else { 190 s += fmt.Sprintf("%s=%v", args[i], args[i+1]) 191 } 192 } else { 193 s += fmt.Sprintf("%s=??", args[i]) 194 } 195 } 196 return s 197 }