Files
plane/monitor/lib/logger/logger.go
Henit Chobisa a93467b95b 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

228 lines
4.8 KiB
Go

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)
}