package log import ( "errors" "log/slog" "net/http" "os" "strings" "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" slogmulti "github.com/samber/slog-multi" "github.com/tigorlazuardi/bluemage/go/config" "github.com/tigorlazuardi/bluemage/go/pkg/telemetry" "gopkg.in/natefinch/lumberjack.v2" ) func NewHandler(cfg *config.Config) (slog.Handler, func() error) { var handlers []slog.Handler cleanup := func() error { return nil } if cfg.Bool("log.enable") { log, sync := createStandardLogger(cfg) cleanup = sync handlers = append(handlers, log) } if cfg.Bool("log.file.enable") { log, clean := createFileLogger(cfg) cl := cleanup cleanup = func() error { return errors.Join(cl(), clean()) } handlers = append(handlers, log) } if cfg.Bool("telemetry.openobserve.enable") && cfg.Bool("telemetry.openobserve.log.enable") { log, clean := createO2Logger(cfg) cl := cleanup handlers = append(handlers, log) cleanup = func() error { err := cl() clean() return err } } if len(handlers) == 0 { return NullHandler{}, cleanup } return slogmulti.Fanout(handlers...), cleanup } func createFileLogger(cfg *config.Config) (slog.Handler, func() error) { output := &lumberjack.Logger{ Filename: cfg.String("log.file.path"), MaxSize: 15, MaxAge: 30, MaxBackups: 10, LocalTime: true, } var lvl slog.Level _ = lvl.UnmarshalText(cfg.Bytes("log.level")) opts := &slog.HandlerOptions{ AddSource: cfg.Bool("log.source"), Level: lvl, } return slog.NewJSONHandler(Lock(AddSync(output)), opts), output.Close } func createStandardLogger(cfg *config.Config) (slog.Handler, func() error) { var output WriteSyncer var cleanup func() error if strings.ToLower(cfg.String("log.output")) == "stdout" { output = Lock(AddSync(colorable.NewColorableStdout())) cleanup = output.Sync } else { output = Lock(AddSync(colorable.NewColorableStderr())) cleanup = output.Sync } var lvl slog.Level _ = lvl.UnmarshalText(cfg.Bytes("log.level")) opts := &slog.HandlerOptions{ AddSource: cfg.Bool("log.source"), Level: lvl, } if isatty.IsTerminal(os.Stdout.Fd()) { return NewPrettyHandler(output, opts), cleanup } else { return slog.NewJSONHandler(output, opts), cleanup } } func createO2Logger(cfg *config.Config) (slog.Handler, func()) { var lvl slog.Level _ = lvl.UnmarshalText(cfg.Bytes("telemetry.openobserve.log.level")) opts := &slog.HandlerOptions{ AddSource: cfg.Bool("telemetry.openobserve.log.source"), Level: lvl, } handler := telemetry.NewOpenObserveHandler(telemetry.OpenObserveHandlerOptions{ HandlerOptions: opts, BufferSize: cfg.Int("telemetry.openobserve.log.buffer.size"), BufferTimeout: cfg.Duration("telemetry.openobserve.log.buffer.timeout"), Concurrency: cfg.Int("telemetry.openobserve.log.concurrency"), Endpoint: cfg.String("telemetry.openobserve.log.endpoint"), HTTPClient: http.DefaultClient, Username: cfg.String("telemetry.openobserve.log.username"), Password: cfg.String("telemetry.openobserve.log.password"), }) return handler, handler.Flush }