garm/cmd/garm-cli/cmd/log.go
Gabriel Adrian Samfira e441b6ce89 Switch to log/slog
This change switches GARM to the new structured logging standard
library. This will allow us to set log levels and reduce some of
the log spam.

Given that we introduced new knobs to tweak logging, the number of
config options for logging now warrants it's own section.

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
2024-01-05 23:46:40 +00:00

101 lines
2.3 KiB
Go

package cmd
import (
"encoding/json"
"fmt"
"log"
"log/slog"
"net/http"
"net/url"
"os"
"os/signal"
"time"
"github.com/cloudbase/garm-provider-common/util"
apiParams "github.com/cloudbase/garm/apiserver/params"
"github.com/gorilla/websocket"
"github.com/spf13/cobra"
)
var logCmd = &cobra.Command{
Use: "debug-log",
SilenceUsage: true,
Short: "Stream garm log",
Long: `Stream all garm logging to the terminal.`,
RunE: func(cmd *cobra.Command, args []string) error {
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
parsedURL, err := url.Parse(mgr.BaseURL)
if err != nil {
return err
}
wsScheme := "ws"
if parsedURL.Scheme == "https" {
wsScheme = "wss"
}
u := url.URL{Scheme: wsScheme, Host: parsedURL.Host, Path: "/api/v1/ws"}
slog.Debug("connecting", "url", u.String())
header := http.Header{}
header.Add("Authorization", fmt.Sprintf("Bearer %s", mgr.Token))
c, response, err := websocket.DefaultDialer.Dial(u.String(), header)
if err != nil {
var resp apiParams.APIErrorResponse
var msg string
if err := json.NewDecoder(response.Body).Decode(&resp); err == nil {
msg = resp.Details
}
log.Fatalf("failed to stream logs: %s (%s)", msg, response.Status)
}
defer c.Close()
done := make(chan struct{})
go func() {
defer close(done)
for {
_, message, err := c.ReadMessage()
if err != nil {
slog.With(slog.Any("error", err)).Error("reading log message")
return
}
fmt.Print(util.SanitizeLogEntry(string(message)))
}
}()
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case <-done:
return nil
case t := <-ticker.C:
err := c.WriteMessage(websocket.TextMessage, []byte(t.String()))
if err != nil {
return err
}
case <-interrupt:
// Cleanly close the connection by sending a close message and then
// waiting (with timeout) for the server to close the connection.
err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
return err
}
select {
case <-done:
case <-time.After(time.Second):
}
return nil
}
}
},
}
func init() {
rootCmd.AddCommand(logCmd)
}