go-vise

Constrained Size Output Virtual Machine
Info | Log | Files | Refs | README | LICENSE

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 }