diff --git a/etc/default/webhookd.env b/etc/default/webhookd.env index 56a08f0..6ef862c 100644 --- a/etc/default/webhookd.env +++ b/etc/default/webhookd.env @@ -5,9 +5,6 @@ # Hook execution logs location, default is OS temporary directory #WHD_HOOK_LOG_DIR="/tmp" -# Output hook execution logs to server logs, default is false -#WHD_HOOK_LOG_OUTPUT=false - # Maximum hook execution time in second, default is 10 #WHD_HOOK_TIMEOUT=10 @@ -15,9 +12,18 @@ # Example: `localhost:8080` or `:8080` for all interfaces #WHD_LISTEN_ADDR=":8080" -# Log level (debug, info, warn, error), default is "info" +# Log level (debug, info, warn or error), default is "info" #WHD_LOG_LEVEL=info +# Log format (text or json), default is "text" +#WHD_LOG_FORMAT=text + +# Log HTTP request, default is false +#WHD_LOG_HTTP_REQUEST=false + +# Log hook execution output, default is false +#WHD_LOG_HOOK_OUTPUT=false + # Number of workers to start, default is 2 #WHD_NB_WORKERS=2 diff --git a/main.go b/main.go index 209ea1c..3d7bf94 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,8 @@ package main import ( "context" "flag" + "log" + "log/slog" "net/http" "os" "os/signal" @@ -31,31 +33,29 @@ func main() { os.Exit(0) } - if conf.HookLogOutput { - logger.Init(conf.LogLevel, "out") - } else { - logger.Init(conf.LogLevel) - } - if conf.HookLogDir == "" { conf.HookLogDir = os.TempDir() } if err := conf.Validate(); err != nil { - logger.Error.Fatal("invalid configuration:", err) + log.Fatal("invalid configuration:", err) } - logger.Debug.Println("starting webhookd server...") + logger.Configure(conf.LogFormat, conf.LogLevel) + logger.HookOutputEnabled = conf.LogHookOutput + logger.RequestOutputEnabled = conf.LogHTTPRequest + + slog.Debug("starting webhookd server...") srv := server.NewServer(conf) // Configure notification if err := notification.Init(conf.NotificationURI); err != nil { - logger.Error.Fatalf("unable to create notification channel: %v\n", err) + slog.Error("unable to create notification channel", "err", err) } // Start the dispatcher. - logger.Debug.Printf("starting the dispatcher with %d workers...\n", conf.NbWorkers) + slog.Debug("starting the dispatcher...", "workers", conf.NbWorkers) worker.StartDispatcher(conf.NbWorkers) done := make(chan bool) @@ -64,24 +64,25 @@ func main() { go func() { <-quit - logger.Debug.Println("server is shutting down...") + slog.Debug("server is shutting down...") api.Shutdown() ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { - logger.Error.Fatalf("could not gracefully shutdown the server: %v\n", err) + slog.Error("could not gracefully shutdown the server", "err", err) } close(done) }() - logger.Info.Println("server is ready to handle requests at", conf.ListenAddr) api.Start() + slog.Info("server started", "addr", conf.ListenAddr) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { - logger.Error.Fatalf("could not listen on %s : %v\n", conf.ListenAddr, err) + slog.Error("unable to start the server", "addr", conf.ListenAddr, "err", err) + os.Exit(1) } <-done - logger.Debug.Println("server stopped") + slog.Debug("server stopped") } diff --git a/pkg/api/index.go b/pkg/api/index.go index e3c3e78..f3f4ae1 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -3,7 +3,7 @@ package api import ( "fmt" "io" - "io/ioutil" + "log/slog" "mime" "net/http" "path" @@ -13,7 +13,6 @@ import ( "github.com/ncarlier/webhookd/pkg/config" "github.com/ncarlier/webhookd/pkg/hook" - "github.com/ncarlier/webhookd/pkg/logger" "github.com/ncarlier/webhookd/pkg/worker" ) @@ -64,13 +63,13 @@ func triggerWebhook(w http.ResponseWriter, r *http.Request) { } _, err := hook.ResolveScript(scriptDir, hookName) if err != nil { - logger.Error.Println(err.Error()) + slog.Error("hooke not found", "err", err.Error()) http.Error(w, "hook not found", http.StatusNotFound) return } if err = r.ParseForm(); err != nil { - logger.Error.Printf("error reading from-data: %v", err) + slog.Error("error reading from-data", "err", err) http.Error(w, "unable to parse request form", http.StatusBadRequest) return } @@ -81,9 +80,9 @@ func triggerWebhook(w http.ResponseWriter, r *http.Request) { if ct != "" { mediatype, _, _ := mime.ParseMediaType(ct) if strings.HasPrefix(mediatype, "text/") || mediatype == "application/json" { - body, err = ioutil.ReadAll(r.Body) + body, err = io.ReadAll(r.Body) if err != nil { - logger.Error.Printf("error reading body: %v", err) + slog.Error("error reading body", "err", err) http.Error(w, "unable to read request body", http.StatusBadRequest) return } @@ -107,7 +106,7 @@ func triggerWebhook(w http.ResponseWriter, r *http.Request) { OutputDir: outputDir, }) if err != nil { - logger.Error.Printf("error creating hook job: %v", err) + slog.Error("error creating hook job", "err", err) http.Error(w, "unable to create hook job", http.StatusInternalServerError) return } @@ -151,7 +150,7 @@ func getWebhookLog(w http.ResponseWriter, r *http.Request) { hookName := path.Dir(strings.TrimPrefix(r.URL.Path, "/")) _, err := hook.ResolveScript(scriptDir, hookName) if err != nil { - logger.Error.Println(err.Error()) + slog.Error(err.Error()) http.Error(w, err.Error(), http.StatusNotFound) return } @@ -159,7 +158,7 @@ func getWebhookLog(w http.ResponseWriter, r *http.Request) { // Retrieve log file logFile, err := hook.Logs(id, hookName, outputDir) if err != nil { - logger.Error.Println(err.Error()) + slog.Error(err.Error()) http.Error(w, err.Error(), http.StatusInternalServerError) return } diff --git a/pkg/api/routes.go b/pkg/api/routes.go index 7068dc1..70c7192 100644 --- a/pkg/api/routes.go +++ b/pkg/api/routes.go @@ -1,9 +1,10 @@ package api import ( + "log/slog" + "github.com/ncarlier/webhookd/pkg/auth" "github.com/ncarlier/webhookd/pkg/config" - "github.com/ncarlier/webhookd/pkg/logger" "github.com/ncarlier/webhookd/pkg/middleware" "github.com/ncarlier/webhookd/pkg/truststore" ) @@ -24,7 +25,7 @@ func buildMiddlewares(conf *config.Config) middleware.Middlewares { // Load trust store... ts, err := truststore.New(conf.TrustStoreFile) if err != nil { - logger.Warning.Printf("unable to load trust store (\"%s\"): %s\n", conf.TrustStoreFile, err) + slog.Warn("unable to load trust store", "filename", conf.TrustStoreFile, "err", err) } if ts != nil { middlewares = middlewares.UseAfter(middleware.Signature(ts)) @@ -33,7 +34,7 @@ func buildMiddlewares(conf *config.Config) middleware.Middlewares { // Load authenticator... authenticator, err := auth.NewHtpasswdFromFile(conf.PasswdFile) if err != nil { - logger.Debug.Printf("unable to load htpasswd file (\"%s\"): %s\n", conf.PasswdFile, err) + slog.Debug("unable to load htpasswd file", "filename", conf.PasswdFile, "err", err) } if authenticator != nil { middlewares = middlewares.UseAfter(middleware.AuthN(authenticator)) diff --git a/pkg/config/config.go b/pkg/config/config.go index 5321328..e08887f 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -15,10 +15,12 @@ type Config struct { NbWorkers int `flag:"nb-workers" desc:"Number of workers to start" default:"2"` HookTimeout int `flag:"hook-timeout" desc:"Maximum hook execution time in second" default:"10"` HookLogDir string `flag:"hook-log-dir" desc:"Hook execution logs location" default:""` - HookLogOutput bool `flag:"hook-log-output" desc:"Output hook execution logs to server logs" default:"false"` ScriptDir string `flag:"scripts" desc:"Scripts location" default:"scripts"` PasswdFile string `flag:"passwd-file" desc:"Password file for basic HTTP authentication" default:".htpasswd"` LogLevel string `flag:"log-level" desc:"Log level (debug, info, warn, error)" default:"info"` + LogFormat string `flag:"log-format" desc:"Log format (json, text)" default:"text"` + LogHookOutput bool `flag:"log-hook-output" desc:"Log hook execution output" default:"false"` + LogHTTPRequest bool `flag:"log-http-request" desc:"Log HTTP request" default:"false"` StaticDir string `flag:"static-dir" desc:"Static file directory to serve on /static path" default:""` StaticPath string `flag:"static-path" desc:"Path to serve static file directory" default:"/static"` NotificationURI string `flag:"notification-uri" desc:"Notification URI"` diff --git a/pkg/hook/job.go b/pkg/hook/job.go index 6e22ad9..ebd96b0 100644 --- a/pkg/hook/job.go +++ b/pkg/hook/job.go @@ -5,6 +5,7 @@ import ( "bytes" "fmt" "io" + "log/slog" "os" "os/exec" "path" @@ -31,6 +32,7 @@ type Job struct { args []string MessageChan chan []byte timeout int + start time.Time status Status logFilename string err error @@ -83,14 +85,27 @@ func (job *Job) Meta() []string { func (job *Job) Terminate(err error) error { job.mutex.Lock() defer job.mutex.Unlock() + job.status = Success if err != nil { job.status = Error job.err = err - logger.Info.Printf("hook %s#%d done [ERROR]\n", job.Name(), job.ID()) + slog.Error( + "hook executed", + "hook", job.Name(), + "id", job.ID(), + "status", "error", + "err", err, + "took", time.Since(job.start).Microseconds(), + ) return err } - job.status = Success - logger.Info.Printf("hook %s#%d done [SUCCESS]\n", job.Name(), job.ID()) + slog.Info( + "hook executed", + "hook", job.Name(), + "id", job.ID(), + "status", "success", + "took", time.Since(job.start).Microseconds(), + ) return nil } @@ -160,9 +175,8 @@ func (job *Job) Run() error { return fmt.Errorf("unable to run job: status=%s", job.StatusLabel()) } job.status = Running - logger.Info.Printf("hook %s#%d started...\n", job.name, job.id) - logger.Debug.Printf("hook %s#%d script: %s\n", job.name, job.id, job.script) - logger.Debug.Printf("hook %s#%d parameter: %v\n", job.name, job.id, job.args) + job.start = time.Now() + slog.Info("executing hook...", "hook", job.name, "id", job.id) binary, err := exec.LookPath(job.script) if err != nil { @@ -184,7 +198,7 @@ func (job *Job) Run() error { return job.Terminate(err) } defer logFile.Close() - logger.Debug.Printf("hook %s#%d output file: %s\n", job.name, job.id, logFile.Name()) + slog.Debug("hook details", "hook", job.name, "id", job.id, "script", job.script, "args", job.args, "output", logFile.Name()) wLogFile := bufio.NewWriter(logFile) defer wLogFile.Flush() @@ -219,26 +233,32 @@ func (job *Job) Run() error { if !job.IsTerminated() { job.MessageChan <- []byte(line) } else { - logger.Error.Printf("hook %s#%d is over ; unable to write more data into the channel: %s\n", job.name, job.id, line) + slog.Error("hook execution done ; unable to write more data into the channel", "hook", job.name, "id", job.id, "line", line) break } // write to stdout if configured - logger.Output.Println(line) + logger.LogIf( + logger.HookOutputEnabled, + slog.LevelInfo+1, + line, + "hook", job.name, + "id", job.id, + ) // writing to outfile if _, err := wLogFile.WriteString(line + "\n"); err != nil { - logger.Error.Println("error while writing into the log file:", logFile.Name(), err) + slog.Error("error while writing into the log file", "filename", logFile.Name(), "err", err) break } } if err := scanner.Err(); err != nil { - logger.Error.Printf("hook %s#%d is unable to read script stdout: %v\n", job.name, job.id, err) + slog.Error("hook is unable to read script stdout", "hook", job.name, "id", job.id, "err", err) } wg.Done() }(cmdReader) // Start timeout timer timer := time.AfterFunc(time.Duration(job.timeout)*time.Second, func() { - logger.Warning.Printf("hook %s#%d has timed out (%ds): killing process #%d ...\n", job.name, job.id, job.timeout, cmd.Process.Pid) + slog.Warn("hook has timed out: killing process...", "hook", job.name, "id", job.id, "timeout", job.timeout, "pid", cmd.Process.Pid) syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) }) diff --git a/pkg/hook/test/job_test.go b/pkg/hook/test/job_test.go index d390a90..6b459df 100644 --- a/pkg/hook/test/job_test.go +++ b/pkg/hook/test/job_test.go @@ -1,13 +1,13 @@ package test import ( + "log/slog" "os" "strconv" "testing" "github.com/ncarlier/webhookd/pkg/assert" "github.com/ncarlier/webhookd/pkg/hook" - "github.com/ncarlier/webhookd/pkg/logger" ) func printJobMessages(job *hook.Job) { @@ -17,13 +17,12 @@ func printJobMessages(job *hook.Job) { if !open { break } - logger.Info.Println(string(msg)) + slog.Info(string(msg)) } }() } func TestHookJob(t *testing.T) { - logger.Init("debug", "out") req := &hook.Request{ Name: "test_simple", Method: "GET", @@ -54,7 +53,6 @@ func TestHookJob(t *testing.T) { } func TestWorkRunnerWithError(t *testing.T) { - logger.Init("debug") req := &hook.Request{ Name: "test_error", Method: "POST", @@ -75,7 +73,6 @@ func TestWorkRunnerWithError(t *testing.T) { } func TestWorkRunnerWithTimeout(t *testing.T) { - logger.Init("debug") req := &hook.Request{ Name: "test_timeout", Method: "POST", diff --git a/pkg/logger/color.go b/pkg/logger/color.go deleted file mode 100644 index 0e23199..0000000 --- a/pkg/logger/color.go +++ /dev/null @@ -1,50 +0,0 @@ -package logger - -import ( - "os" - - "github.com/mattn/go-isatty" -) - -var ( - nocolor = "\033[0m" - red = "\033[0;31m" - green = "\033[0;32m" - orange = "\033[0;33m" - blue = "\033[0;34m" - purple = "\033[0;35m" - cyan = "\033[0;36m" - gray = "\033[0;37m" -) - -func colorize(text string, color string) string { - if isatty.IsTerminal(os.Stdout.Fd()) { - return color + text + nocolor - } - return text -} - -// Gray ANSI color applied to a string -func Gray(text string) string { - return colorize(text, gray) -} - -// Green ANSI color applied to a string -func Green(text string) string { - return colorize(text, green) -} - -// Orange ANSI color applied to a string -func Orange(text string) string { - return colorize(text, orange) -} - -// Red ANSI color applied to a string -func Red(text string) string { - return colorize(text, red) -} - -// Purple ANSI color applied to a string -func Purple(text string) string { - return colorize(text, purple) -} diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go new file mode 100644 index 0000000..02dd37a --- /dev/null +++ b/pkg/logger/logger.go @@ -0,0 +1,46 @@ +package logger + +import ( + "context" + "log/slog" + "os" +) + +var ( + HookOutputEnabled = false + RequestOutputEnabled = false +) + +// Configure logger +func Configure(format, level string) { + logLevel := slog.LevelDebug + switch level { + case "info": + logLevel = slog.LevelInfo + case "warn": + logLevel = slog.LevelWarn + case "error": + logLevel = slog.LevelError + } + + opts := slog.HandlerOptions{ + Level: logLevel, + AddSource: logLevel == slog.LevelDebug, + } + + var logger *slog.Logger + if format == "json" { + logger = slog.New(slog.NewJSONHandler(os.Stdout, &opts)) + } else { + logger = slog.New(slog.NewTextHandler(os.Stdout, &opts)) + } + + slog.SetDefault(logger) +} + +// LogIf writ log on condition +func LogIf(condition bool, level slog.Level, msg string, args ...any) { + if condition { + slog.Log(context.Background(), level, msg, args...) + } +} diff --git a/pkg/logger/main.go b/pkg/logger/main.go deleted file mode 100644 index 542ffd0..0000000 --- a/pkg/logger/main.go +++ /dev/null @@ -1,66 +0,0 @@ -package logger - -import ( - "io" - "io/ioutil" - "log" - "os" -) - -var ( - // Debug level - Debug *log.Logger - // Info level - Info *log.Logger - // Warning level - Warning *log.Logger - // Error level - Error *log.Logger - // Output special level used for script output - Output *log.Logger -) - -// Init logger level -func Init(level string, with ...string) { - var debugHandle, infoHandle, warnHandle, errorHandle, outputHandle io.Writer - debugHandle = os.Stdout - infoHandle = os.Stdout - warnHandle = os.Stderr - errorHandle = os.Stderr - outputHandle = ioutil.Discard - switch level { - case "info": - debugHandle = ioutil.Discard - case "warn": - debugHandle = ioutil.Discard - infoHandle = ioutil.Discard - case "error": - debugHandle = ioutil.Discard - infoHandle = ioutil.Discard - warnHandle = ioutil.Discard - } - - if contains(with, "out") { - outputHandle = os.Stdout - } - - commonFlags := log.LstdFlags | log.Lmicroseconds - if level == "debug" { - commonFlags = log.LstdFlags | log.Lmicroseconds | log.Lshortfile - } - - Debug = log.New(debugHandle, Gray("DBG "), commonFlags) - Info = log.New(infoHandle, Green("INF "), commonFlags) - Warning = log.New(warnHandle, Orange("WRN "), commonFlags) - Error = log.New(errorHandle, Red("ERR "), commonFlags) - Output = log.New(outputHandle, Purple("OUT "), commonFlags) -} - -func contains(s []string, e string) bool { - for _, a := range s { - if a == e { - return true - } - } - return false -} diff --git a/pkg/middleware/logger.go b/pkg/middleware/logger.go index 9958e7d..7bddea9 100644 --- a/pkg/middleware/logger.go +++ b/pkg/middleware/logger.go @@ -2,6 +2,7 @@ package middleware import ( "fmt" + "log/slog" "net/http" "strings" "time" @@ -25,16 +26,18 @@ func Logger(next http.Handler) http.Handler { if !ok { requestID = "0" } - logger.Info.Printf( - "%s - - [%s] %q %d %d %q %q %q", - getRequestIP(r), - start.Format("02/Jan/2006:15:04:05 -0700"), + logger.LogIf( + logger.RequestOutputEnabled, + slog.LevelInfo+1, fmt.Sprintf("%s %s %s", r.Method, r.URL, r.Proto), - o.status, - o.written, - r.Referer(), - r.UserAgent(), - fmt.Sprintf("REQID=%s", requestID), + "ip", getRequestIP(r), + "time", start.Format("02/Jan/2006:15:04:05 -0700"), + "duration", time.Since(start).Milliseconds(), + "status", o.status, + "bytes", o.written, + "referer", r.Referer(), + "ua", r.UserAgent(), + "reqid", requestID, ) }() next.ServeHTTP(o, r) diff --git a/pkg/middleware/signature/test/ed5519-signature_test.go b/pkg/middleware/signature/test/ed5519-signature_test.go index 39dc367..6d79203 100644 --- a/pkg/middleware/signature/test/ed5519-signature_test.go +++ b/pkg/middleware/signature/test/ed5519-signature_test.go @@ -12,14 +12,11 @@ import ( "time" "github.com/ncarlier/webhookd/pkg/assert" - "github.com/ncarlier/webhookd/pkg/logger" "github.com/ncarlier/webhookd/pkg/middleware/signature" "github.com/ncarlier/webhookd/pkg/truststore" ) func TestEd5519Signature(t *testing.T) { - logger.Init("warn") - pubkey, privkey, err := ed25519.GenerateKey(rand.Reader) assert.Nil(t, err, "") diff --git a/pkg/middleware/signature/test/http-signature_test.go b/pkg/middleware/signature/test/http-signature_test.go index 2e3de30..f0fe9a6 100644 --- a/pkg/middleware/signature/test/http-signature_test.go +++ b/pkg/middleware/signature/test/http-signature_test.go @@ -10,7 +10,6 @@ import ( "github.com/go-fed/httpsig" "github.com/ncarlier/webhookd/pkg/assert" - "github.com/ncarlier/webhookd/pkg/logger" "github.com/ncarlier/webhookd/pkg/middleware/signature" "github.com/ncarlier/webhookd/pkg/truststore" ) @@ -25,8 +24,6 @@ func assertSigner(t *testing.T) httpsig.Signer { } func TestHTTPSignature(t *testing.T) { - logger.Init("warn") - privkey, err := rsa.GenerateKey(rand.Reader, 2048) assert.Nil(t, err, "") pubkey := &privkey.PublicKey diff --git a/pkg/notification/http/http_notifier.go b/pkg/notification/http/http_notifier.go index 6b91b5f..a46bdab 100644 --- a/pkg/notification/http/http_notifier.go +++ b/pkg/notification/http/http_notifier.go @@ -3,13 +3,13 @@ package http import ( "bytes" "encoding/json" + "log/slog" "net/http" "net/url" "strconv" "strings" "github.com/ncarlier/webhookd/pkg/helper" - "github.com/ncarlier/webhookd/pkg/logger" "github.com/ncarlier/webhookd/pkg/notification" ) @@ -27,7 +27,7 @@ type httpNotifier struct { } func newHTTPNotifier(uri *url.URL) (notification.Notifier, error) { - logger.Info.Println("using HTTP notification system: ", uri.String()) + slog.Info("using HTTP notification system ", "üri", uri.Opaque) return &httpNotifier{ URL: uri, PrefixFilter: helper.GetValueOrAlt(uri.Query(), "prefix", "notify:"), @@ -65,7 +65,7 @@ func (n *httpNotifier) Notify(result notification.HookResult) error { return err } resp.Body.Close() - logger.Info.Printf("job %s#%d notification sent to %s\n", result.Name(), result.ID(), n.URL.String()) + slog.Info("notification sent", "hook", result.Name(), "id", result.ID(), "to", n.URL.Opaque) return nil } diff --git a/pkg/notification/notifier.go b/pkg/notification/notifier.go index 65b4459..b9c7010 100644 --- a/pkg/notification/notifier.go +++ b/pkg/notification/notifier.go @@ -1,7 +1,7 @@ package notification import ( - "github.com/ncarlier/webhookd/pkg/logger" + "log/slog" ) // Notifier is able to send a notification. @@ -17,7 +17,7 @@ func Notify(result HookResult) { return } if err := notifier.Notify(result); err != nil { - logger.Error.Printf("unable to send notification for webhook %s#%d: %v\n", result.Name(), result.ID(), err) + slog.Error("unable to send notification", "webhook", result.Name(), "id", result.ID(), "err", err) } } diff --git a/pkg/notification/smtp/smtp_notifier.go b/pkg/notification/smtp/smtp_notifier.go index a6ccfe1..f9af828 100644 --- a/pkg/notification/smtp/smtp_notifier.go +++ b/pkg/notification/smtp/smtp_notifier.go @@ -3,6 +3,7 @@ package smtp import ( "crypto/tls" "fmt" + "log/slog" "net" "net/smtp" "net/url" @@ -11,7 +12,6 @@ import ( "time" "github.com/ncarlier/webhookd/pkg/helper" - "github.com/ncarlier/webhookd/pkg/logger" "github.com/ncarlier/webhookd/pkg/notification" ) @@ -28,7 +28,7 @@ type smtpNotifier struct { } func newSMTPNotifier(uri *url.URL) (notification.Notifier, error) { - logger.Info.Println("using SMTP notification system:", uri.Opaque) + slog.Info("using SMTP notification system", "uri", uri.Opaque) q := uri.Query() return &smtpNotifier{ Host: helper.GetValueOrAlt(q, "smtp", "localhost:25"), @@ -128,7 +128,7 @@ func (n *smtpNotifier) Notify(result notification.HookResult) error { return err } - logger.Info.Printf("job %s#%d notification sent to %s\n", result.Name(), result.ID(), n.To) + slog.Info("notification sent", "hook", result.Name(), "id", result.ID(), "to", n.To) // Send the QUIT command and close the connection. return client.Quit() diff --git a/pkg/server/server.go b/pkg/server/server.go index 1f21c5b..838a78d 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -2,6 +2,7 @@ package server import ( "context" + "log/slog" "net/http" "os" "os/user" @@ -9,7 +10,6 @@ import ( "github.com/ncarlier/webhookd/pkg/api" "github.com/ncarlier/webhookd/pkg/config" - "github.com/ncarlier/webhookd/pkg/logger" "golang.org/x/crypto/acme/autocert" ) @@ -48,12 +48,13 @@ func (s *Server) Shutdown(ctx context.Context) error { // NewServer create new HTTP(s) server func NewServer(cfg *config.Config) *Server { + logger := slog.NewLogLogger(slog.Default().Handler(), slog.LevelError) server := &Server{ tls: cfg.TLS, self: &http.Server{ Addr: cfg.ListenAddr, Handler: api.NewRouter(cfg), - ErrorLog: logger.Error, + ErrorLog: logger, }, } if server.tls { diff --git a/pkg/truststore/p12_truststore.go b/pkg/truststore/p12_truststore.go index 514afa6..633c7aa 100644 --- a/pkg/truststore/p12_truststore.go +++ b/pkg/truststore/p12_truststore.go @@ -2,14 +2,14 @@ package truststore import ( "crypto" - "io/ioutil" + "log/slog" + "os" - "github.com/ncarlier/webhookd/pkg/logger" "golang.org/x/crypto/pkcs12" ) func newP12TrustStore(filename string) (TrustStore, error) { - data, err := ioutil.ReadFile(filename) + data, err := os.ReadFile(filename) if err != nil { return nil, err } @@ -25,7 +25,7 @@ func newP12TrustStore(filename string) (TrustStore, error) { keyID := string(cert.Subject.CommonName) result.Keys[keyID] = cert.PublicKey - logger.Debug.Printf("certificate \"%s\" loaded into the trustore", keyID) + slog.Debug("certificate loaded into the trustore", "id", keyID) return result, nil } diff --git a/pkg/truststore/pem_truststore.go b/pkg/truststore/pem_truststore.go index 9efbe0c..60d1bb9 100644 --- a/pkg/truststore/pem_truststore.go +++ b/pkg/truststore/pem_truststore.go @@ -5,13 +5,12 @@ import ( "crypto/x509" "encoding/pem" "fmt" - "io/ioutil" - - "github.com/ncarlier/webhookd/pkg/logger" + "log/slog" + "os" ) func newPEMTrustStore(filename string) (TrustStore, error) { - raw, err := ioutil.ReadFile(filename) + raw, err := os.ReadFile(filename) if err != nil { return nil, err } @@ -37,7 +36,7 @@ func newPEMTrustStore(filename string) (TrustStore, error) { } result.Keys[keyID] = key - logger.Debug.Printf("public key \"%s\" loaded into the trustore", keyID) + slog.Debug("public key loaded into the trustore", "id", keyID) case "CERTIFICATE": cert, err := x509.ParseCertificate(block.Bytes) if err != nil { @@ -45,7 +44,7 @@ func newPEMTrustStore(filename string) (TrustStore, error) { } keyID := string(cert.Subject.CommonName) result.Keys[keyID] = cert.PublicKey - logger.Debug.Printf("certificate \"%s\" loaded into the trustore", keyID) + slog.Debug("certificate loaded into the trustore", "id", keyID) } raw = rest } diff --git a/pkg/truststore/test/p12_truststore_test.go b/pkg/truststore/test/p12_truststore_test.go index 256c0f4..c199474 100644 --- a/pkg/truststore/test/p12_truststore_test.go +++ b/pkg/truststore/test/p12_truststore_test.go @@ -5,13 +5,11 @@ import ( "testing" "github.com/ncarlier/webhookd/pkg/assert" - "github.com/ncarlier/webhookd/pkg/logger" "github.com/ncarlier/webhookd/pkg/truststore" ) func TestTrustStoreWithP12(t *testing.T) { t.Skip() - logger.Init("warn") ts, err := truststore.New("test.p12") assert.Nil(t, err, "") diff --git a/pkg/truststore/test/pem_truststore_test.go b/pkg/truststore/test/pem_truststore_test.go index 2add102..6fd4fb5 100644 --- a/pkg/truststore/test/pem_truststore_test.go +++ b/pkg/truststore/test/pem_truststore_test.go @@ -5,13 +5,10 @@ import ( "testing" "github.com/ncarlier/webhookd/pkg/assert" - "github.com/ncarlier/webhookd/pkg/logger" "github.com/ncarlier/webhookd/pkg/truststore" ) func TestTrustStoreWithNoKeyID(t *testing.T) { - logger.Init("warn") - ts, err := truststore.New("test-key-01.pem") assert.Nil(t, err, "") assert.NotNil(t, ts, "") @@ -24,8 +21,6 @@ func TestTrustStoreWithNoKeyID(t *testing.T) { } func TestTrustStoreWithKeyID(t *testing.T) { - logger.Init("warn") - ts, err := truststore.New("test-key-02.pem") assert.Nil(t, err, "") assert.NotNil(t, ts, "") @@ -36,8 +31,6 @@ func TestTrustStoreWithKeyID(t *testing.T) { } func TestTrustStoreWithCertificate(t *testing.T) { - logger.Init("warn") - ts, err := truststore.New("test-cert.pem") assert.Nil(t, err, "") assert.NotNil(t, ts, "") @@ -48,8 +41,6 @@ func TestTrustStoreWithCertificate(t *testing.T) { } func TestTrustStoreWithMultipleEntries(t *testing.T) { - logger.Init("warn") - ts, err := truststore.New("test-multi.pem") assert.Nil(t, err, "") assert.NotNil(t, ts, "") diff --git a/pkg/truststore/truststore.go b/pkg/truststore/truststore.go index d75bd33..1112700 100644 --- a/pkg/truststore/truststore.go +++ b/pkg/truststore/truststore.go @@ -3,9 +3,8 @@ package truststore import ( "crypto" "fmt" + "log/slog" "path/filepath" - - "github.com/ncarlier/webhookd/pkg/logger" ) // TrustStore is a generic interface to retrieve a public key @@ -31,7 +30,7 @@ func New(filename string) (store TrustStore, err error) { return nil, nil } - logger.Debug.Printf("loading trust store: %s", filename) + slog.Debug("loading trust store...", "filname", filename) switch filepath.Ext(filename) { case ".pem": store, err = newPEMTrustStore(filename) diff --git a/pkg/worker/dispatcher.go b/pkg/worker/dispatcher.go index aed343e..f1137d3 100644 --- a/pkg/worker/dispatcher.go +++ b/pkg/worker/dispatcher.go @@ -1,7 +1,7 @@ package worker import ( - "github.com/ncarlier/webhookd/pkg/logger" + "log/slog" ) // WorkerQueue is the global queue of Workers @@ -17,7 +17,7 @@ func StartDispatcher(nworkers int) { // Now, create all of our workers. for i := 0; i < nworkers; i++ { - logger.Debug.Printf("starting worker #%d ...\n", i+1) + slog.Debug("starting worker...", "worker", i+1) worker := NewWorker(i+1, WorkerQueue) worker.Start() } @@ -29,7 +29,7 @@ func StartDispatcher(nworkers int) { go func() { worker := <-WorkerQueue - logger.Debug.Printf("dispatching hook request: %s#%d", work.Name(), work.ID()) + slog.Debug("dispatching hook request", "hook", work.Name(), "id", work.ID()) worker <- work }() } diff --git a/pkg/worker/worker.go b/pkg/worker/worker.go index d7de14e..e902d2a 100644 --- a/pkg/worker/worker.go +++ b/pkg/worker/worker.go @@ -2,10 +2,10 @@ package worker import ( "fmt" + "log/slog" "github.com/ncarlier/webhookd/pkg/metric" - "github.com/ncarlier/webhookd/pkg/logger" "github.com/ncarlier/webhookd/pkg/notification" ) @@ -41,7 +41,7 @@ func (w Worker) Start() { select { case work := <-w.Work: // Receive a work request. - logger.Debug.Printf("worker #%d received hook request: %s#%d\n", w.ID, work.Name(), work.ID()) + slog.Debug("hook execution request received", "worker", w.ID, "hook", work.Name(), "id", work.ID()) metric.Requests.Add(1) err := work.Run() if err != nil { @@ -53,7 +53,7 @@ func (w Worker) Start() { work.Close() case <-w.QuitChan: - logger.Debug.Printf("stopping worker #%d...\n", w.ID) + slog.Debug("stopping worker...", "worker", w.ID) return } } diff --git a/tooling/httpsig/main.go b/tooling/httpsig/main.go index bc254d9..9e2bf77 100644 --- a/tooling/httpsig/main.go +++ b/tooling/httpsig/main.go @@ -8,11 +8,11 @@ import ( "flag" "fmt" "io" - "io/ioutil" "log" "net/http" "net/http/httputil" "net/url" + "os" "strings" "time" @@ -45,7 +45,7 @@ func main() { log.Fatal("invalid target URL") } - keyBytes, err := ioutil.ReadFile(conf.KeyFile) + keyBytes, err := os.ReadFile(conf.KeyFile) if err != nil { log.Fatal(err.Error()) } @@ -64,7 +64,7 @@ func main() { var jsonBytes []byte if conf.JSON != "" { var err error - jsonBytes, err = ioutil.ReadFile(conf.JSON) + jsonBytes, err = os.ReadFile(conf.JSON) if err != nil { log.Fatal(err.Error()) }