Files
plane/monitor/lib/logger/logger.go

228 lines
4.8 KiB
Go
Raw Permalink Normal View History

feat: Added Prime Monitor Go Service for monitoring, health check and feature flagging (#411) * feat: added build meta data to monitor * feat: added versioning to prime-monitor * feat: added logger package for prime-monitor * feat: implemented logging in cmd * feat: implemented moduled monorepo architecture * feat: added cron package for healthcheck * feat: added constants and modules for healthcheck * feat: added environment service getter method * feat: added environment service parser for parsing the recieved keys * feat: added environment service provider method * feat: added retry based executer for the test methods * feat: added http health check method and placeholders for redis & pg * feat: added tests for healthcheck methods * feat: added PerformHealthCheck Controller Method for execution of healthcheck controller * feat: added healthcheck test file for testing the healthcheck * feat: added healthcheck to work file * feat: added readme for healthcheck package * feat: created healthcheck register for prime scheduler * feat: added cron command for running cron subpart of monitor * feat: added prime schedule handler for cron * feat: added start command for starting up prime scheduler * feat: added build utility files for monitor * fix: goreleaser builds * feat: removed Redis method from healthcheck methods * feat: added docker file for building prime-monitor * feat: added flag for adding healthcheck duration for cron start * chore: removed redis as a healthcheck method * chore: modified actions to build monitor on branch build and docker images * feat: added monitor service in docker compose caddy * feat: added description for HealthCheckJob * fix: status code issue for reachable and non reachable * feat: added api package for connecting with prime api * feat: modified cmd package to report healthcheck status to prime * feat: added api types inside prime-monitor * feat: added message field for meta while status reporting * fix: modified constants for environment variables * feat: added monitor readme * fix: build-branch monitor content shift to build-branch-ee * chore: added build args on release * feat: remove version meta data from cli * fix: docker file changed to fix the build to default * fix: removed build flags from github branch build * fix: moved cron start to root level start cmd * fix: passed the recieved machine signature to api headers * feat: optimised docker build for monitor
2024-06-20 12:14:46 +05:30
package logger
import (
"bytes"
"context"
"encoding/json"
"fmt"
"strconv"
"strings"
"sync"
"time"
"log/slog"
)
const (
timeFormat = "[15:04:05.000]"
reset = "\033[0m"
black = 30
red = 31
green = 32
yellow = 33
blue = 34
magenta = 35
cyan = 36
lightGray = 37
darkGray = 90
lightRed = 91
lightGreen = 92
lightYellow = 93
lightBlue = 94
lightMagenta = 95
lightCyan = 96
white = 97
)
func colorize(colorCode int, v string) string {
return fmt.Sprintf("\033[%sm%s%s", strconv.Itoa(colorCode), v, reset)
}
type Handler struct {
h slog.Handler
r func([]string, slog.Attr) slog.Attr
b *bytes.Buffer
m *sync.Mutex
}
func (h *Handler) Enabled(ctx context.Context, level slog.Level) bool {
return h.h.Enabled(ctx, level)
}
func (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler {
return &Handler{h: h.h.WithAttrs(attrs), b: h.b, r: h.r, m: h.m}
}
func (h *Handler) WithGroup(name string) slog.Handler {
return &Handler{h: h.h.WithGroup(name), b: h.b, r: h.r, m: h.m}
}
func (h *Handler) computeAttrs(
ctx context.Context,
r slog.Record,
) (map[string]any, error) {
h.m.Lock()
defer func() {
h.b.Reset()
h.m.Unlock()
}()
if err := h.h.Handle(ctx, r); err != nil {
return nil, fmt.Errorf("error when calling inner handler's Handle: %w", err)
}
var attrs map[string]any
err := json.Unmarshal(h.b.Bytes(), &attrs)
if err != nil {
return nil, fmt.Errorf("error when unmarshaling inner handler's Handle result: %w", err)
}
return attrs, nil
}
func (h *Handler) Handle(ctx context.Context, r slog.Record) error {
var level string
levelAttr := slog.Attr{
Key: slog.LevelKey,
Value: slog.AnyValue(r.Level),
}
if h.r != nil {
levelAttr = h.r([]string{}, levelAttr)
}
if !levelAttr.Equal(slog.Attr{}) {
level = levelAttr.Value.String() + ":"
if r.Level <= slog.LevelDebug {
level = colorize(lightGray, level)
} else if r.Level <= slog.LevelInfo {
level = colorize(cyan, level)
} else if r.Level < slog.LevelWarn {
level = colorize(lightBlue, level)
} else if r.Level < slog.LevelError {
level = colorize(lightYellow, level)
} else if r.Level <= slog.LevelError+1 {
level = colorize(lightRed, level)
} else if r.Level > slog.LevelError+1 {
level = colorize(lightMagenta, level)
}
}
var timestamp string
timeAttr := slog.Attr{
Key: slog.TimeKey,
Value: slog.StringValue(r.Time.Format(timeFormat)),
}
if h.r != nil {
timeAttr = h.r([]string{}, timeAttr)
}
if !timeAttr.Equal(slog.Attr{}) {
timestamp = colorize(lightGray, timeAttr.Value.String())
}
var msg string
msgAttr := slog.Attr{
Key: slog.MessageKey,
Value: slog.StringValue(r.Message),
}
if h.r != nil {
msgAttr = h.r([]string{}, msgAttr)
}
if !msgAttr.Equal(slog.Attr{}) {
msg = colorize(white, msgAttr.Value.String())
}
attrs, err := h.computeAttrs(ctx, r)
if err != nil {
return err
}
bytes, err := json.MarshalIndent(attrs, "", " ")
if err != nil {
return fmt.Errorf("error when marshaling attrs: %w", err)
}
out := strings.Builder{}
if len(timestamp) > 0 {
out.WriteString(timestamp)
out.WriteString(" ")
}
if len(level) > 0 {
out.WriteString(level)
out.WriteString(" ")
}
if len(msg) > 0 {
out.WriteString(msg)
out.WriteString(" ")
}
if len(bytes) > 0 {
out.WriteString(colorize(darkGray, string(bytes)))
}
fmt.Println(out.String())
return nil
}
func suppressDefaults(
next func([]string, slog.Attr) slog.Attr,
) func([]string, slog.Attr) slog.Attr {
return func(groups []string, a slog.Attr) slog.Attr {
if a.Key == slog.TimeKey ||
a.Key == slog.LevelKey ||
a.Key == slog.MessageKey {
return slog.Attr{}
}
if next == nil {
return a
}
return next(groups, a)
}
}
func NewHandler(opts *slog.HandlerOptions) *Handler {
if opts == nil {
opts = &slog.HandlerOptions{}
}
b := &bytes.Buffer{}
return &Handler{
b: b,
h: slog.NewJSONHandler(b, &slog.HandlerOptions{
Level: opts.Level,
AddSource: opts.AddSource,
ReplaceAttr: suppressDefaults(opts.ReplaceAttr),
}),
r: opts.ReplaceAttr,
m: &sync.Mutex{},
}
}
func (h *Handler) GetSlogHandler() slog.Handler {
return h.h
}
// New methods for simpler logging API
func (h *Handler) log(ctx context.Context, level slog.Level, msg string) {
r := slog.Record{
Time: time.Now(),
Level: level,
Message: msg,
}
h.Handle(ctx, r)
}
func (h *Handler) Info(ctx context.Context, msg string) {
h.log(ctx, slog.LevelInfo, msg)
}
func (h *Handler) Error(ctx context.Context, msg string, attrs ...slog.Attr) {
h.log(ctx, slog.LevelError, msg)
}
func (h *Handler) Warning(ctx context.Context, msg string, attrs ...slog.Attr) {
h.log(ctx, slog.LevelWarn, msg)
}
func (h *Handler) Debug(ctx context.Context, msg string, attrs ...slog.Attr) {
h.log(ctx, slog.LevelDebug, msg)
}