bnkamalesh/goapp

View on GitHub
internal/pkg/logger/logger.go

Summary

Maintainability
A
0 mins
Test Coverage
// Package logger is used for logging. The default one pushes structured (JSON) logs.
// This is a barebones logger which I use and have not required any other logging libraries till date.
// It depends on your hosting environment and other complex requirements with logging.
package logger

import (
    "context"
    "encoding/json"
    "fmt"
    "os"
    "runtime"
    "time"
)

func init() {
    _ = Logger(defaultLogger)
}

const (
    // LogTypeInfo is for logging type 'info'
    LogTypeInfo = "info"
    // LogTypeWarn is for logging type 'warn'
    LogTypeWarn = "warn"
    // LogTypeError is for logging type 'error'
    LogTypeError = "error"
    // LogTypeFatal is for logging type 'fatal'
    LogTypeFatal = "fatal"
)

// Logger interface defines all the logging methods to be implemented
type Logger interface {
    Info(ctx context.Context, payload ...any)
    Warn(ctx context.Context, payload ...any)
    Error(ctx context.Context, payload ...any)
    Fatal(ctx context.Context, payload ...any)
}

// LogHandler implements Logger
type LogHandler struct {
    Skipstack  int
    appName    string
    appVersion string
    params     map[string]string
}

func (lh *LogHandler) defaultPayload(severity string) map[string]any {
    _, file, line, _ := runtime.Caller(lh.Skipstack)
    payload := map[string]any{
        "app":        lh.appName,
        "appVersion": lh.appVersion,
        "severity":   severity,
        "at":         fmt.Sprintf("%s:%d", file, line),
        "timestamp":  time.Now(),
    }
    for key, value := range lh.params {
        payload[key] = value
    }
    return payload
}

func (lh *LogHandler) serialize(severity string, data ...any) (string, error) {
    payload := lh.defaultPayload(severity)
    for idx, value := range data {
        payload[fmt.Sprintf("%d", idx)] = fmt.Sprintf("%+v", value)
    }

    b, err := json.Marshal(payload)
    if err != nil {
        return "", err
    }

    return string(b), nil
}

func (lh *LogHandler) log(severity string, payload ...any) {
    out, err := lh.serialize(severity, payload...)
    if err != nil {
        fmt.Printf("%+v\n", err)
        return
    }

    switch severity {
    case LogTypeFatal:
        {
            fmt.Println(out)
            os.Exit(1)
        }
    }
    fmt.Println(out)
}

// Info is for logging items with severity 'info'
func (lh *LogHandler) Info(ctx context.Context, payload ...any) {
    lh.log(LogTypeInfo, payload...)
}

// Warn is for logging items with severity 'Warn'
func (lh *LogHandler) Warn(ctx context.Context, payload ...any) {
    lh.log(LogTypeWarn, payload...)
}

// Error is for logging items with severity 'Error'
func (lh *LogHandler) Error(ctx context.Context, payload ...any) {
    lh.log(LogTypeError, payload...)
}

// Fatal is for logging items with severity 'Fatal'
func (lh *LogHandler) Fatal(ctx context.Context, payload ...any) {
    lh.log(LogTypeFatal, payload...)
}

// New returns a new instance of LogHandler
func New(
    appname string,
    appversion string,
    skipStack uint8,
    params map[string]string,
) *LogHandler {
    if skipStack <= 1 {
        skipStack = 4
    }

    return &LogHandler{
        Skipstack:  int(skipStack),
        appName:    appname,
        appVersion: appversion,
        params:     params,
    }
}