mirror of https://github.com/h44z/wg-portal.git
				
				
				
			
		
			
				
	
	
		
			169 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			169 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Go
		
	
	
	
| package internal
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"log/slog"
 | |
| 	"os"
 | |
| 	"runtime"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"sync"
 | |
| )
 | |
| 
 | |
| // SetupLogging initializes the global logger with the given level and format
 | |
| func SetupLogging(level string, pretty, json bool) {
 | |
| 	var logLevel = new(slog.LevelVar)
 | |
| 
 | |
| 	switch strings.ToLower(level) {
 | |
| 	case "trace", "debug":
 | |
| 		logLevel.Set(slog.LevelDebug)
 | |
| 	case "info", "information":
 | |
| 		logLevel.Set(slog.LevelInfo)
 | |
| 	case "warn", "warning":
 | |
| 		logLevel.Set(slog.LevelWarn)
 | |
| 	case "error":
 | |
| 		logLevel.Set(slog.LevelError)
 | |
| 	default:
 | |
| 		logLevel.Set(slog.LevelInfo)
 | |
| 	}
 | |
| 
 | |
| 	opts := &slog.HandlerOptions{
 | |
| 		Level: logLevel,
 | |
| 	}
 | |
| 
 | |
| 	// send everything to stderr as suggested in https://www.gnu.org/software/libc/manual/html_node/Standard-Streams.html
 | |
| 	output := os.Stderr
 | |
| 
 | |
| 	var handler slog.Handler
 | |
| 	switch {
 | |
| 	case json:
 | |
| 		handler = slog.NewJSONHandler(output, opts)
 | |
| 	case pretty:
 | |
| 		handler = NewPrettyHandler(output, opts)
 | |
| 	default:
 | |
| 		handler = slog.NewTextHandler(output, opts)
 | |
| 	}
 | |
| 
 | |
| 	logger := slog.New(handler)
 | |
| 
 | |
| 	slog.SetDefault(logger)
 | |
| }
 | |
| 
 | |
| // PrettyHandler is a slog.Handler that formats log records in a human-readable way.
 | |
| // It mimics the behavior of the slog.Default() handler.
 | |
| type PrettyHandler struct {
 | |
| 	opts       slog.HandlerOptions
 | |
| 	prefix     string // preformatted group names followed by a dot
 | |
| 	preformat  string // preformatted Attrs, with an initial space
 | |
| 	timeFormat string
 | |
| 
 | |
| 	mu sync.Mutex
 | |
| 	w  io.Writer
 | |
| }
 | |
| 
 | |
| // NewPrettyHandler creates a new PrettyHandler.
 | |
| func NewPrettyHandler(w io.Writer, opts *slog.HandlerOptions) *PrettyHandler {
 | |
| 	h := &PrettyHandler{w: w}
 | |
| 	if opts != nil {
 | |
| 		h.opts = *opts
 | |
| 	}
 | |
| 	if h.opts.ReplaceAttr == nil {
 | |
| 		h.opts.ReplaceAttr = func(_ []string, a slog.Attr) slog.Attr { return a }
 | |
| 	}
 | |
| 
 | |
| 	h.timeFormat = "2006/01/02 15:04:05"
 | |
| 
 | |
| 	return h
 | |
| }
 | |
| 
 | |
| // Enabled reports whether the handler handles records at the given level.
 | |
| func (h *PrettyHandler) Enabled(_ context.Context, level slog.Level) bool {
 | |
| 	minLevel := slog.LevelInfo
 | |
| 	if h.opts.Level != nil {
 | |
| 		minLevel = h.opts.Level.Level()
 | |
| 	}
 | |
| 	return level >= minLevel
 | |
| }
 | |
| 
 | |
| // WithGroup returns a new Handler with the given group appended to the handler's
 | |
| func (h *PrettyHandler) WithGroup(name string) slog.Handler {
 | |
| 	return &PrettyHandler{
 | |
| 		w:         h.w,
 | |
| 		opts:      h.opts,
 | |
| 		preformat: h.preformat,
 | |
| 		prefix:    h.prefix + name + ".",
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // WithAttrs returns a new Handler whose attributes consist of the handler's
 | |
| func (h *PrettyHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
 | |
| 	var buf []byte
 | |
| 	for _, a := range attrs {
 | |
| 		buf = h.appendAttr(buf, h.prefix, a)
 | |
| 	}
 | |
| 	return &PrettyHandler{
 | |
| 		w:         h.w,
 | |
| 		opts:      h.opts,
 | |
| 		prefix:    h.prefix,
 | |
| 		preformat: h.preformat + string(buf),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Handle formats its argument Record as a single line of text ending in a newline.
 | |
| func (h *PrettyHandler) Handle(_ context.Context, r slog.Record) error {
 | |
| 	var buf []byte
 | |
| 	if !r.Time.IsZero() {
 | |
| 		buf = r.Time.AppendFormat(buf, h.timeFormat)
 | |
| 		buf = append(buf, ' ')
 | |
| 	}
 | |
| 
 | |
| 	// Make sure that each level has the same length.
 | |
| 	// The shortest level is "INFO", thus we add a space to the end of the level string
 | |
| 	levText := (r.Level.String() + " ")[0:5]
 | |
| 
 | |
| 	buf = append(buf, levText...)
 | |
| 	buf = append(buf, ' ')
 | |
| 	if h.opts.AddSource && r.PC != 0 {
 | |
| 		fs := runtime.CallersFrames([]uintptr{r.PC})
 | |
| 		f, _ := fs.Next()
 | |
| 		buf = append(buf, f.File...)
 | |
| 		buf = append(buf, ':')
 | |
| 		buf = strconv.AppendInt(buf, int64(f.Line), 10)
 | |
| 		buf = append(buf, ' ')
 | |
| 	}
 | |
| 	buf = append(buf, r.Message...)
 | |
| 	buf = append(buf, h.preformat...)
 | |
| 	r.Attrs(func(a slog.Attr) bool {
 | |
| 		buf = h.appendAttr(buf, h.prefix, a)
 | |
| 		return true
 | |
| 	})
 | |
| 	buf = append(buf, '\n')
 | |
| 	h.mu.Lock()
 | |
| 	defer h.mu.Unlock()
 | |
| 	_, err := h.w.Write(buf)
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| func (h *PrettyHandler) appendAttr(buf []byte, prefix string, a slog.Attr) []byte {
 | |
| 	if a.Equal(slog.Attr{}) {
 | |
| 		return buf
 | |
| 	}
 | |
| 	if a.Value.Kind() != slog.KindGroup {
 | |
| 		buf = append(buf, ' ')
 | |
| 		buf = append(buf, prefix...)
 | |
| 		buf = append(buf, a.Key...)
 | |
| 		buf = append(buf, '=')
 | |
| 		return fmt.Appendf(buf, "%v", a.Value.Any())
 | |
| 	}
 | |
| 	// Group
 | |
| 	if a.Key != "" {
 | |
| 		prefix += a.Key + "."
 | |
| 	}
 | |
| 	for _, a := range a.Value.Group() {
 | |
| 		buf = h.appendAttr(buf, prefix, a)
 | |
| 	}
 | |
| 	return buf
 | |
| }
 |