From f2d93daaf8e72355bf75d7c0dc6bb6f493efd822 Mon Sep 17 00:00:00 2001 From: Vladimir Dementyev Date: Fri, 9 Feb 2024 12:52:52 -0800 Subject: [PATCH] chore: migrate to slog --- broadcast/http.go | 12 ++-- broadcast/legacy_nats.go | 14 ++--- broadcast/legacy_redis.go | 33 +++++----- broadcast/redis.go | 38 +++++------ broker/broker.go | 4 +- broker/memory.go | 12 ++-- broker/nats.go | 62 +++++++++--------- cli/cli.go | 41 ++++++------ cli/options.go | 2 +- cli/session_options.go | 4 +- config/presets.go | 4 +- diagnostics/diagnostics_gops.go | 3 +- enats/enats.go | 74 +++++++++++++++------- features/metrics.testfile | 2 +- go.mod | 6 +- go.sum | 54 +--------------- gobench/gobench.go | 6 +- hub/gate.go | 12 ++-- hub/hub.go | 34 ++++------ identity/jwt.go | 14 ++--- logger/logger.go | 61 ++++++++++++++++++ metrics/custom_printer.go | 10 +-- metrics/custom_printer_unsupported.go | 2 +- metrics/metrics.go | 16 ++--- metrics/printer.go | 19 +++--- metrics/statsd_writer.go | 36 ++++++----- node/disconnect_queue.go | 17 +++-- node/disconnector.go | 5 +- node/node.go | 82 ++++++++++++------------ node/node_mocks_test.go | 6 +- node/session.go | 34 +++++----- pubsub/nats.go | 23 +++---- pubsub/redis.go | 45 ++++++------- rails/cable_ready.go | 17 +++-- rails/turbo.go | 17 +++-- rpc/rpc.go | 30 ++++----- server/request_info.go | 2 +- server/server.go | 15 +++-- sse/handler.go | 20 +++--- telemetry/telemetry.go | 4 +- utils/log_handler.go | 91 --------------------------- utils/logging.go | 40 ------------ utils/utils.go | 4 +- ws/handler.go | 16 ++--- 44 files changed, 467 insertions(+), 576 deletions(-) create mode 100644 logger/logger.go delete mode 100644 utils/log_handler.go delete mode 100644 utils/logging.go diff --git a/broadcast/http.go b/broadcast/http.go index c5b4062b..a67a93b3 100644 --- a/broadcast/http.go +++ b/broadcast/http.go @@ -4,11 +4,11 @@ import ( "context" "fmt" "io" + "log/slog" "net/http" "strconv" "github.com/anycable/anycable-go/server" - "github.com/apex/log" ) const ( @@ -41,7 +41,7 @@ type HTTPBroadcaster struct { authHeader string server *server.HTTPServer node Handler - log *log.Entry + log *slog.Logger } var _ Broadcaster = (*HTTPBroadcaster)(nil) @@ -56,7 +56,7 @@ func NewHTTPBroadcaster(node Handler, config *HTTPConfig) *HTTPBroadcaster { return &HTTPBroadcaster{ node: node, - log: log.WithFields(log.Fields{"context": "broadcast", "provider": "http"}), + log: slog.With("context", "broadcast").With("provider", "http"), port: config.Port, path: config.Path, authHeader: authHeader, @@ -78,7 +78,7 @@ func (s *HTTPBroadcaster) Start(done chan (error)) error { s.server = server s.server.SetupHandler(s.path, http.HandlerFunc(s.Handler)) - s.log.Infof("Accept broadcast requests at %s%s", s.server.Address(), s.path) + s.log.Info(fmt.Sprintf("Accept broadcast requests at %s%s", s.server.Address(), s.path)) go func() { if err := s.server.StartAndAnnounce("broadcasting HTTP server"); err != nil { @@ -103,7 +103,7 @@ func (s *HTTPBroadcaster) Shutdown(ctx context.Context) error { // Handler processes HTTP requests func (s *HTTPBroadcaster) Handler(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { - s.log.Debugf("Invalid request method: %s", r.Method) + s.log.Debug("invalid request method", "method", r.Method) w.WriteHeader(422) return } @@ -118,7 +118,7 @@ func (s *HTTPBroadcaster) Handler(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) if err != nil { - s.log.Error("Failed to read request body") + s.log.Error("failed to read request body") w.WriteHeader(422) return } diff --git a/broadcast/legacy_nats.go b/broadcast/legacy_nats.go index adb46ff9..acafcb43 100644 --- a/broadcast/legacy_nats.go +++ b/broadcast/legacy_nats.go @@ -2,8 +2,8 @@ package broadcast import ( "context" + "log/slog" - "github.com/apex/log" "github.com/nats-io/nats.go" nconfig "github.com/anycable/anycable-go/nats" @@ -14,7 +14,7 @@ type LegacyNATSBroadcaster struct { handler Handler config *nconfig.NATSConfig - log *log.Entry + log *slog.Logger } var _ Broadcaster = (*LegacyNATSBroadcaster)(nil) @@ -23,7 +23,7 @@ func NewLegacyNATSBroadcaster(node Handler, c *nconfig.NATSConfig) *LegacyNATSBr return &LegacyNATSBroadcaster{ config: c, handler: node, - log: log.WithFields(log.Fields{"context": "broadcast", "provider": "nats"}), + log: slog.With("context", "broadcast").With("provider", "nats"), } } @@ -37,11 +37,11 @@ func (s *LegacyNATSBroadcaster) Start(done chan (error)) error { nats.MaxReconnects(s.config.MaxReconnectAttempts), nats.DisconnectErrHandler(func(nc *nats.Conn, err error) { if err != nil { - log.Warnf("Connection failed: %v", err) + slog.Warn("connection failed", "error", err.Error()) } }), nats.ReconnectHandler(func(nc *nats.Conn) { - log.Infof("Connection restored: %s", nc.ConnectedUrl()) + slog.Info("connection restored", "url", nc.ConnectedUrl()) }), } @@ -56,7 +56,7 @@ func (s *LegacyNATSBroadcaster) Start(done chan (error)) error { } _, err = nc.Subscribe(s.config.Channel, func(m *nats.Msg) { - s.log.Debugf("Incoming pubsub message: %s", m.Data) + s.log.Debug("incoming pubsub message", "data", m.Data) s.handler.HandlePubSub(m.Data) }) @@ -65,7 +65,7 @@ func (s *LegacyNATSBroadcaster) Start(done chan (error)) error { return err } - s.log.Infof("Subscribing for broadcasts to channel: %s", s.config.Channel) + s.log.Info("subscribing for broadcasts", "channel", s.config.Channel) s.conn = nc diff --git a/broadcast/legacy_redis.go b/broadcast/legacy_redis.go index b64ff387..de3dfe56 100644 --- a/broadcast/legacy_redis.go +++ b/broadcast/legacy_redis.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "log/slog" "net/url" "strings" "time" @@ -12,7 +13,6 @@ import ( rconfig "github.com/anycable/anycable-go/redis" "github.com/anycable/anycable-go/utils" - "github.com/apex/log" "github.com/gomodule/redigo/redis" ) @@ -28,7 +28,7 @@ type LegacyRedisBroadcaster struct { reconnectAttempt int maxReconnectAttempts int uri *url.URL - log *log.Entry + log *slog.Logger tlsVerify bool } @@ -43,7 +43,7 @@ func NewLegacyRedisBroadcaster(node Handler, config *rconfig.RedisConfig) *Legac pingInterval: time.Duration(config.KeepalivePingInterval), reconnectAttempt: 0, maxReconnectAttempts: config.MaxReconnectAttempts, - log: log.WithFields(log.Fields{"context": "broadcast", "provider": "redis"}), + log: slog.With("context", "broadcast").With("provider", "redis"), tlsVerify: config.TLSVerify, } } @@ -67,8 +67,7 @@ func (s *LegacyRedisBroadcaster) Start(done chan (error)) error { if s.sentinels != "" { masterName := redisURL.Hostname() - s.log.Debug("Redis sentinel enabled") - s.log.Debugf("Redis sentinel parameters: sentinels: %s, masterName: %s", s.sentinels, masterName) + s.log.Debug("Redis sentinel enabled", "sentinels", s.sentinels, "master", masterName) sentinels := strings.Split(s.sentinels, ",") s.sentinelClient = &sentinel.Sentinel{ Addrs: sentinels, @@ -100,10 +99,10 @@ func (s *LegacyRedisBroadcaster) Start(done chan (error)) error { dialOptions..., ) if err != nil { - s.log.Debugf("Failed to connect to sentinel %s", addr) + s.log.Debug("failed to connect to sentinel", "addr", addr) return nil, err } - s.log.Debugf("Successfully connected to sentinel %s", addr) + s.log.Debug("successfully connected to sentinel", "addr", addr) return c, nil }, } @@ -149,18 +148,18 @@ func (s *LegacyRedisBroadcaster) keepalive(done chan (error)) { masterAddress, err := s.sentinelClient.MasterAddr() if err != nil { - s.log.Warn("Failed to get master address from sentinel.") + s.log.Warn("failed to get master address from sentinel") done <- err return } - s.log.Debugf("Got master address from sentinel: %s", masterAddress) + s.log.Debug("obtained master address from sentinel", "addr", masterAddress) s.uri.Host = masterAddress s.url = s.uri.String() } if err := s.listen(); err != nil { - s.log.Warnf("Redis connection failed: %v", err) + s.log.Warn("Redis connection failed", "error", err.Error()) } s.reconnectAttempt++ @@ -172,10 +171,10 @@ func (s *LegacyRedisBroadcaster) keepalive(done chan (error)) { delay := utils.NextRetry(s.reconnectAttempt) - s.log.Infof("Next Redis reconnect attempt in %s", delay) + s.log.Info(fmt.Sprintf("next Redis reconnect attempt in %s", delay)) time.Sleep(delay) - s.log.Infof("Reconnecting to Redis...") + s.log.Info("reconnecting to Redis...") } } @@ -198,13 +197,13 @@ func (s *LegacyRedisBroadcaster) listen() error { if s.sentinels != "" { if !sentinel.TestRole(c, "master") { - return errors.New("Failed master role check") + return errors.New("failed master role check") } } psc := redis.PubSubConn{Conn: c} if err = psc.Subscribe(s.channel); err != nil { - s.log.Errorf("Failed to subscribe to Redis channel: %v", err) + s.log.Error("failed to subscribe to Redis channel", "error", err.Error()) return err } @@ -216,12 +215,12 @@ func (s *LegacyRedisBroadcaster) listen() error { for { switch v := psc.Receive().(type) { case redis.Message: - s.log.Debugf("Incoming pubsub message from Redis: %s", v.Data) + s.log.Debug("incoming pubsub message", "data", v.Data) s.node.HandlePubSub(v.Data) case redis.Subscription: - s.log.Infof("Subscribed to Redis channel: %s\n", v.Channel) + s.log.Info("subscribed to Redis channel", "channel", v.Channel) case error: - s.log.Errorf("Redis subscription error: %v", v) + s.log.Error("Redis subscription error", "error", v.Error()) done <- v } } diff --git a/broadcast/redis.go b/broadcast/redis.go index 738a991d..83fdbfc4 100644 --- a/broadcast/redis.go +++ b/broadcast/redis.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "log/slog" "strings" "sync" "time" @@ -11,7 +12,6 @@ import ( rconfig "github.com/anycable/anycable-go/redis" "github.com/anycable/anycable-go/utils" - "github.com/apex/log" nanoid "github.com/matoous/go-nanoid" "github.com/redis/rueidis" ) @@ -33,7 +33,7 @@ type RedisBroadcaster struct { shutdownCh chan struct{} finishedCh chan struct{} - log *log.Entry + log *slog.Logger } var _ Broadcaster = (*RedisBroadcaster)(nil) @@ -46,7 +46,7 @@ func NewRedisBroadcaster(node Handler, config *rconfig.RedisConfig) *RedisBroadc node: node, config: config, consumerName: name, - log: log.WithFields(log.Fields{"context": "broadcast", "provider": "redisx", "id": name}), + log: slog.With("context", "broadcast").With("provider", "redisx").With("id", name), shutdownCh: make(chan struct{}), finishedCh: make(chan struct{}), } @@ -64,11 +64,11 @@ func (s *RedisBroadcaster) Start(done chan error) error { } if s.config.IsSentinel() { //nolint:gocritic - s.log.WithField("stream", s.config.Channel).WithField("consumer", s.consumerName).Infof("Starting Redis broadcaster at %v (sentinels)", s.config.Hostnames()) + s.log.With("stream", s.config.Channel).With("consumer", s.consumerName).Info(fmt.Sprintf("Starting Redis broadcaster at %v (sentinels)", s.config.Hostnames())) } else if s.config.IsCluster() { - s.log.WithField("stream", s.config.Channel).WithField("consumer", s.consumerName).Infof("Starting Redis broadcaster at %v (cluster)", s.config.Hostnames()) + s.log.With("stream", s.config.Channel).With("consumer", s.consumerName).Info(fmt.Sprintf("Starting Redis broadcaster at %v (cluster)", s.config.Hostnames())) } else { - s.log.WithField("stream", s.config.Channel).WithField("consumer", s.consumerName).Infof("Starting Redis broadcaster at %s", s.config.Hostname()) + s.log.With("stream", s.config.Channel).With("consumer", s.consumerName).Info(fmt.Sprintf("Starting Redis broadcaster at %s", s.config.Hostname())) } s.clientOptions = options @@ -86,7 +86,7 @@ func (s *RedisBroadcaster) Shutdown(ctx context.Context) error { return nil } - s.log.Debugf("Shutting down Redis broadcaster") + s.log.Debug("shutting down Redis broadcaster") close(s.shutdownCh) @@ -100,7 +100,7 @@ func (s *RedisBroadcaster) Shutdown(ctx context.Context) error { err := res.Error() if err != nil { - s.log.Errorf("Failed to remove Redis stream consumer: %v", err) + s.log.Error("failed to remove Redis stream consumer", "error", err.Error()) } s.client.Close() @@ -131,7 +131,7 @@ func (s *RedisBroadcaster) runReader(done chan (error)) { err := s.initClient() if err != nil { - s.log.Errorf("Failed to connect to Redis: %v", err) + s.log.Error("failed to connect to Redis", "error", err) s.maybeReconnect(done) return } @@ -144,9 +144,9 @@ func (s *RedisBroadcaster) runReader(done chan (error)) { if err != nil { if redisErr, ok := rueidis.IsRedisErr(err); ok { if strings.HasPrefix(redisErr.Error(), "BUSYGROUP") { - s.log.Debugf("Redis consumer group already exists") + s.log.Debug("Redis consumer group already exists") } else { - s.log.Errorf("Failed to create consumer group: %v", err) + s.log.Error("failed to create consumer group", "error", err.Error()) s.maybeReconnect(done) return } @@ -161,7 +161,7 @@ func (s *RedisBroadcaster) runReader(done chan (error)) { for { select { case <-s.shutdownCh: - s.log.Debugf("Stop consuming stream") + s.log.Debug("stop consuming stream") close(s.finishedCh) return default: @@ -169,7 +169,7 @@ func (s *RedisBroadcaster) runReader(done chan (error)) { reclaimed, err := s.autoclaimMessages(readBlockMilliseconds) if err != nil { - s.log.Errorf("Failed to claim from Redis stream: %v", err) + s.log.Error("failed to claim from Redis stream", "error", err) s.maybeReconnect(done) return } @@ -177,7 +177,7 @@ func (s *RedisBroadcaster) runReader(done chan (error)) { lastClaimedAt = time.Now().UnixMilli() if len(reclaimed) > 0 { - s.log.Debugf("Reclaimed messages: %d", len(reclaimed)) + s.log.Debug("reclaimed messages", "size", len(reclaimed)) s.broadcastXrange(reclaimed) } @@ -186,7 +186,7 @@ func (s *RedisBroadcaster) runReader(done chan (error)) { messages, err := s.readFromStream(readBlockMilliseconds) if err != nil { - s.log.Errorf("Failed to read from Redis stream: %v", err) + s.log.Error("failed to read from Redis stream", "error", err) s.maybeReconnect(done) return } @@ -252,7 +252,7 @@ func (s *RedisBroadcaster) autoclaimMessages(blockTime int64) ([]rueidis.XRangeE func (s *RedisBroadcaster) broadcastXrange(messages []rueidis.XRangeEntry) { for _, message := range messages { if payload, pok := message.FieldValues["payload"]; pok { - s.log.Debugf("Incoming broadcast: %v", payload) + s.log.Debug("incoming broadcast", "data", payload) s.node.HandleBroadcast([]byte(payload)) @@ -264,7 +264,7 @@ func (s *RedisBroadcaster) broadcastXrange(messages []rueidis.XRangeEntry) { err := ackRes[0].Error() if err != nil { - s.log.Errorf("Failed to ack message: %v", err) + s.log.Error("failed to ack message", "error", err) } } } @@ -281,10 +281,10 @@ func (s *RedisBroadcaster) maybeReconnect(done chan (error)) { delay := utils.NextRetry(s.reconnectAttempt - 1) - s.log.Infof("Next Redis reconnect attempt in %s", delay) + s.log.Info(fmt.Sprintf("next Redis reconnect attempt in %s", delay)) time.Sleep(delay) - s.log.Infof("Reconnecting to Redis...") + s.log.Info("reconnecting to Redis...") s.clientMu.Lock() diff --git a/broker/broker.go b/broker/broker.go index e25b0f2a..928af664 100644 --- a/broker/broker.go +++ b/broker/broker.go @@ -175,11 +175,11 @@ func (b *LegacyBroker) Unsubscribe(stream string) string { } func (LegacyBroker) HistoryFrom(stream string, epoch string, offset uint64) ([]common.StreamMessage, error) { - return nil, errors.New("History not supported") + return nil, errors.New("history not supported") } func (LegacyBroker) HistorySince(stream string, ts int64) ([]common.StreamMessage, error) { - return nil, errors.New("History not supported") + return nil, errors.New("history not supported") } func (LegacyBroker) CommitSession(sid string, session Cacheable) error { diff --git a/broker/memory.go b/broker/memory.go index c94d2e5b..51744d46 100644 --- a/broker/memory.go +++ b/broker/memory.go @@ -59,7 +59,7 @@ func (ms *memstream) insert(data string, offset uint64, t time.Time) (uint64, er ts := t.Unix() if ms.offset >= offset { - return 0, fmt.Errorf("Offset %d is already taken", offset) + return 0, fmt.Errorf("offset %d is already taken", offset) } ms.offset = offset @@ -128,17 +128,17 @@ func (ms *memstream) filterByOffset(offset uint64, callback func(e *entry)) erro defer ms.mu.RUnlock() if ms.low > offset { - return fmt.Errorf("Requested offset couldn't be found: %d, lowest: %d", offset, ms.low) + return fmt.Errorf("requested offset couldn't be found: %d, lowest: %d", offset, ms.low) } if ms.low == 0 { - return fmt.Errorf("Stream is empty") + return fmt.Errorf("stream is empty") } start := (offset - ms.low) + 1 if start > uint64(len(ms.data)) { - return fmt.Errorf("Requested offset couldn't be found: %d, latest: %d", offset, ms.data[len(ms.data)-1].offset) + return fmt.Errorf("requested offset couldn't be found: %d, latest: %d", offset, ms.data[len(ms.data)-1].offset) } for _, v := range ms.data[start:] { @@ -272,13 +272,13 @@ func (b *Memory) HistoryFrom(name string, epoch string, offset uint64) ([]common bepoch := b.GetEpoch() if bepoch != epoch { - return nil, fmt.Errorf("Unknown epoch: %s, current: %s", epoch, bepoch) + return nil, fmt.Errorf("unknown epoch: %s, current: %s", epoch, bepoch) } stream := b.get(name) if stream == nil { - return nil, errors.New("Stream not found") + return nil, errors.New("stream not found") } history := []common.StreamMessage{} diff --git a/broker/nats.go b/broker/nats.go index 026fc6f7..42523020 100644 --- a/broker/nats.go +++ b/broker/nats.go @@ -3,6 +3,7 @@ package broker import ( "context" "fmt" + "log/slog" "math" "strings" "sync" @@ -11,7 +12,6 @@ import ( "github.com/anycable/anycable-go/common" natsconfig "github.com/anycable/anycable-go/nats" "github.com/anycable/anycable-go/utils" - "github.com/apex/log" "github.com/joomcode/errorx" nanoid "github.com/matoous/go-nanoid" "github.com/nats-io/nats.go" @@ -49,7 +49,7 @@ type NATS struct { broadcastBacklog []*common.StreamMessage backlogMu sync.Mutex - log *log.Entry + log *slog.Logger } const ( @@ -86,7 +86,7 @@ func NewNATSBroker(broadcaster Broadcaster, c *Config, nc *natsconfig.NATSConfig streamSync: newStreamsSynchronizer(), jstreams: newLRU[string](time.Duration(c.HistoryTTL * int64(time.Second))), jconsumers: newLRU[jetstream.Consumer](time.Duration(c.HistoryTTL * int64(time.Second))), - log: log.WithField("context", "broker").WithField("provider", "nats"), + log: slog.With("context", "broker").With("provider", "nats"), } for _, opt := range opts { @@ -110,11 +110,11 @@ func (n *NATS) Start(done chan (error)) error { nats.MaxReconnects(n.nconf.MaxReconnectAttempts), nats.DisconnectErrHandler(func(nc *nats.Conn, err error) { if err != nil { - n.log.Warnf("Connection failed: %v", err) + n.log.Warn("connection failed", "error", err.Error()) } }), nats.ReconnectHandler(func(nc *nats.Conn) { - n.log.Infof("Connection restored: %s", nc.ConnectedUrl()) + n.log.Info("connection restored", "url", nc.ConnectedUrl()) }), } @@ -196,12 +196,12 @@ func (n *NATS) initJetStreamWithRetry() error { return errorx.Decorate(err, "JetStream is unavailable") } - n.log.Warnf("JetStream initialization failed: %v", err) + n.log.Warn("JetStream initialization failed", "error", err.Error()) - n.log.Infof("Next JetStream initialization attempt in %s", delay) + n.log.Info(fmt.Sprintf("next JetStream initialization attempt in %s", delay)) time.Sleep(delay) - n.log.Infof("Re-initializing JetStream...") + n.log.Info("re-initializing JetStream...") } } @@ -213,7 +213,7 @@ func (n *NATS) initJetStream() error { js, err := jetstream.New(nc) if err != nil { - return errorx.Decorate(err, "Failed to connect to JetStream") + return errorx.Decorate(err, "failed to connect to JetStream") } n.js = js @@ -221,7 +221,7 @@ func (n *NATS) initJetStream() error { kv, err := n.fetchBucketWithTTL(kvBucket, time.Duration(n.conf.SessionsTTL*int64(time.Second))) if err != nil { - return errorx.Decorate(err, "Failed to connect to JetStream KV") + return errorx.Decorate(err, "failed to connect to JetStream KV") } n.kv = kv @@ -229,23 +229,23 @@ func (n *NATS) initJetStream() error { epoch, err := n.calculateEpoch() if err != nil { - return errorx.Decorate(err, "Failed to calculate epoch") + return errorx.Decorate(err, "failed to calculate epoch") } n.writeEpoch(epoch) err = n.local.Start(nil) if err != nil { - return errorx.Decorate(err, "Failed to start internal memory broker") + return errorx.Decorate(err, "failed to start internal memory broker") } err = n.watchEpoch(n.shutdownCtx) if err != nil { - n.log.Warnf("failed to set up epoch watcher: %s", err) + n.log.Warn("failed to set up epoch watcher", "error", err.Error()) } - n.log.Infof("NATS broker is ready (epoch=%s)", epoch) + n.log.Info("NATS broker is ready", "epoch", epoch) return nil } @@ -313,7 +313,7 @@ func (n *NATS) writeEpoch(val string) { func (n *NATS) HandleBroadcast(msg *common.StreamMessage) { err := n.Ready(jetstreamReadyTimeout) if err != nil { - n.log.Debugf("JetStream is not ready yet to publish messages, add to backlog") + n.log.Debug("JetStream is not ready yet to publish messages, add to backlog") n.backlogAdd(msg) return } @@ -321,7 +321,7 @@ func (n *NATS) HandleBroadcast(msg *common.StreamMessage) { offset, err := n.add(msg.Stream, msg.Data) if err != nil { - n.log.WithError(err).Errorf("failed to add message to JetStream Stream %s", msg.Stream) + n.log.Error("failed to add message to JetStream Stream", "stream", msg.Stream, "error", err.Error()) return } @@ -503,7 +503,7 @@ func (n *NATS) addStreamConsumer(stream string) { err := n.ensureStreamExists(stream) if err != nil { - n.log.Errorf("Failed to create JetStream stream %s: %s", stream, err) + n.log.Error("failed to create JetStream stream", "stream", stream, "error", err.Error()) return } @@ -516,11 +516,11 @@ createConsumer: }) if err != nil { - n.log.Errorf("Failed to create JetStream stream consumer %s: %s", stream, err) + n.log.Error("failed to create JetStream stream consumer", "stream", stream, "error", err.Error()) return nil, err } - n.log.Debugf("Created JetStream consumer %s for stream: %s", cons.CachedInfo().Name, stream) + n.log.Debug("created JetStream consumer", "consumer", cons.CachedInfo().Name, "stream", stream) n.streamSync.touch(stream) @@ -533,7 +533,7 @@ createConsumer: batch, err := cons.FetchNoWait(batchSize) if err != nil { - n.log.Errorf("Failed to fetch initial messages from JetStream: %s", err) + n.log.Error("failed to fetch initial messages from JetStream", "error", err.Error()) return nil, err } @@ -552,7 +552,7 @@ createConsumer: return cons, nil }, func(cons jetstream.Consumer) { name := cons.CachedInfo().Name - n.log.Debugf("Deleting JetStream consumer %s for stream: %s", name, stream) + n.log.Debug("deleting JetStream consumer", "consumer", name, "stream", stream) n.streamSync.remove(stream) n.js.DeleteConsumer(context.Background(), prefixedStream, name) // nolint:errcheck }) @@ -569,7 +569,7 @@ func (n *NATS) consumeMessage(stream string, msg jetstream.Msg) { meta, err := msg.Metadata() if err != nil { - n.log.Errorf("Failed to get JetStream message metadata: %s", err) + n.log.Error("failed to get JetStream message metadata", "error", err.Error()) return } @@ -578,7 +578,7 @@ func (n *NATS) consumeMessage(stream string, msg jetstream.Msg) { _, err = n.local.Store(stream, msg.Data(), seq, ts) if err != nil { - n.log.Errorf("Failed to store message in local broker: %s", err) + n.log.Error("failed to store message in local broker", "error", err.Error()) return } } @@ -673,7 +673,7 @@ func (n *NATS) watchEpoch(ctx context.Context) error { newEpoch := string(entry.Value()) if n.Epoch() != newEpoch { - n.log.Warnf("epoch updated: %s", newEpoch) + n.log.Warn("epoch updated", "epoch", newEpoch) n.writeEpoch(newEpoch) } } @@ -700,7 +700,7 @@ bucketSetup: if context.DeadlineExceeded == err { if attempts > 0 { attempts-- - n.log.Warnf("failed to retrieve bucket %s, retrying in 500ms...", key) + n.log.Warn("failed to retrieve bucket, retrying in 500ms...", "bucket", key) time.Sleep(500 * time.Millisecond) goto bucketSetup } @@ -714,13 +714,13 @@ bucketSetup: bucket, err = n.js.KeyValue(context.Background(), key) if err != nil { - return nil, errorx.Decorate(err, "Failed to retrieve bucket: %s", key) + return nil, errorx.Decorate(err, "failed to retrieve bucket: %s", key) } } } if err != nil { - return nil, errorx.Decorate(err, "Failed to create bucket: %s", key) + return nil, errorx.Decorate(err, "failed to create bucket: %s", key) } // Invalidate TTL settings if the bucket is the new one. @@ -729,14 +729,14 @@ bucketSetup: status, serr := bucket.Status(context.Background()) if serr != nil { - return nil, errorx.Decorate(serr, "Failed to retrieve bucket status: %s", key) + return nil, errorx.Decorate(serr, "failed to retrieve bucket status: %s", key) } if status.TTL() != ttl { - n.log.Warnf("bucket TTL has been changed, recreating the bucket: key=%s, old=%s, new=%s", key, status.TTL().String(), ttl.String()) + n.log.Warn("bucket TTL has been changed, recreating the bucket", "bucket", key, "old_ttl", status.TTL().String(), "ttl", ttl.String()) derr := n.js.DeleteKeyValue(context.Background(), key) if derr != nil { - return nil, errorx.Decorate(derr, "Failed to delete bucket: %s", key) + return nil, errorx.Decorate(derr, "failed to delete bucket: %s", key) } goto bucketSetup @@ -938,7 +938,7 @@ func (n *NATS) shouldRetryOnError(err error, attempts *int, cooldown time.Durati if context.DeadlineExceeded == err || jetstream.ErrNoStreamResponse == err { if *attempts > 0 { (*attempts)-- - n.log.Warnf("operation failed with %s, retrying in %s...", err.Error(), cooldown.String()) + n.log.Warn(fmt.Sprintf("operation failed, retrying in %s...", cooldown.String()), "error", err.Error()) time.Sleep(cooldown) return true } diff --git a/cli/cli.go b/cli/cli.go index af9f97b6..d4647c26 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -3,6 +3,7 @@ package cli import ( "context" "fmt" + "log/slog" "net/http" "os" "runtime" @@ -16,6 +17,7 @@ import ( "github.com/anycable/anycable-go/config" "github.com/anycable/anycable-go/enats" "github.com/anycable/anycable-go/identity" + "github.com/anycable/anycable-go/logger" metricspkg "github.com/anycable/anycable-go/metrics" "github.com/anycable/anycable-go/mrb" "github.com/anycable/anycable-go/node" @@ -28,7 +30,6 @@ import ( "github.com/anycable/anycable-go/utils" "github.com/anycable/anycable-go/version" "github.com/anycable/anycable-go/ws" - "github.com/apex/log" "github.com/gorilla/websocket" "github.com/joomcode/errorx" @@ -51,7 +52,7 @@ type Runner struct { name string config *config.Config - log *log.Entry + log *slog.Logger controllerFactory controllerFactory disconnectorFactory disconnectorFactory @@ -97,12 +98,12 @@ func (r *Runner) checkAndSetDefaults() error { } } - err := utils.InitLogger(r.config.LogFormat, r.config.LogLevel) + _, err := logger.InitLogger(r.config.LogFormat, r.config.LogLevel) if err != nil { return errorx.Decorate(err, "!!! Failed to initialize logger !!!") } - r.log = log.WithFields(log.Fields{"context": "main"}) + r.log = slog.With("context", "main") err = r.config.LoadPresets() @@ -156,7 +157,7 @@ func (r *Runner) Run() error { mrubySupport := r.initMRuby() - r.log.Infof("Starting %s %s%s (pid: %d, open file limit: %s, gomaxprocs: %d)", r.name, version.Version(), mrubySupport, os.Getpid(), utils.OpenFileLimit(), numProcs) + r.log.Info(fmt.Sprintf("Starting %s %s%s (pid: %d, open file limit: %s, gomaxprocs: %d)", r.name, version.Version(), mrubySupport, os.Getpid(), utils.OpenFileLimit(), numProcs)) metrics := r.metrics @@ -191,7 +192,7 @@ func (r *Runner) Run() error { } if appBroker != nil { - r.log.Infof(appBroker.Announce()) + r.log.Info(appBroker.Announce()) appNode.SetBroker(appBroker) } @@ -216,7 +217,7 @@ func (r *Runner) Run() error { desc = fmt.Sprintf(" (%s)", desc) } - r.log.Infof("Embedded NATS server started: %s%s", r.config.EmbeddedNats.ServiceAddr, desc) + r.log.Info(fmt.Sprintf("Embedded NATS server started: %s%s", r.config.EmbeddedNats.ServiceAddr, desc)) r.shutdownables = append(r.shutdownables, service) } @@ -250,11 +251,11 @@ func (r *Runner) Run() error { for _, broadcaster := range broadcasters { if broadcaster.IsFanout() && subscriber.IsMultiNode() { - r.log.Warnf("Using pub/sub with a distributed broadcaster has no effect") + r.log.Warn("Using pub/sub with a distributed broadcaster has no effect") } if !broadcaster.IsFanout() && !subscriber.IsMultiNode() { - r.log.Warnf("Using a non-distributed broadcaster without a pub/sub enabled; each broadcasted message is only processed by a single node") + r.log.Warn("Using a non-distributed broadcaster without a pub/sub enabled; each broadcasted message is only processed by a single node") } err = broadcaster.Start(r.errChan) @@ -283,7 +284,7 @@ func (r *Runner) Run() error { for _, path := range r.config.Path { wsServer.SetupHandler(path, wsHandler) - r.log.Infof("Handle WebSocket connections at %s%s", wsServer.Address(), path) + r.log.Info(fmt.Sprintf("Handle WebSocket connections at %s%s", wsServer.Address(), path)) } for path, handlerFactory := range r.websocketEndpoints { @@ -295,12 +296,12 @@ func (r *Runner) Run() error { } wsServer.SetupHandler(r.config.HealthPath, http.HandlerFunc(server.HealthHandler)) - r.log.Infof("Handle health requests at %s%s", wsServer.Address(), r.config.HealthPath) + r.log.Info(fmt.Sprintf("Handle health requests at %s%s", wsServer.Address(), r.config.HealthPath)) if r.config.SSE.Enabled { - r.log.Infof( - "Handle SSE requests at %s%s", - wsServer.Address(), r.config.SSE.Path, + r.log.Info( + fmt.Sprintf("Handle SSE requests at %s%s", + wsServer.Address(), r.config.SSE.Path), ) sseHandler, err := r.defaultSSEHandler(appNode, wsServer.ShutdownCtx(), r.config) @@ -363,13 +364,13 @@ func (r *Runner) newController(metrics *metricspkg.Metrics) (node.Controller, er if r.config.JWT.Enabled() { identifier := identity.NewJWTIdentifier(&r.config.JWT) controller = identity.NewIdentifiableController(controller, identifier) - r.log.Infof("JWT identification is enabled (param: %s, enforced: %v)", r.config.JWT.Param, r.config.JWT.Force) + r.log.Info(fmt.Sprintf("JWT identification is enabled (param: %s, enforced: %v)", r.config.JWT.Param, r.config.JWT.Force)) } if !r.Router().Empty() { r.Router().SetDefault(controller) controller = r.Router() - r.log.Infof("Using channels router: %s", strings.Join(r.Router().Routes(), ", ")) + r.log.Info(fmt.Sprintf("Using channels router: %s", strings.Join(r.Router().Routes(), ", "))) } return controller, nil @@ -435,7 +436,7 @@ func (r *Runner) initMRuby() string { var mrbv string mrbv, err := mrb.Version() if err != nil { - log.Errorf("mruby failed to initialize: %v", err) + slog.Error(fmt.Sprintf("mruby failed to initialize: %v", err)) } else { return " (with " + mrbv + ")" } @@ -484,19 +485,19 @@ func (r *Runner) announceGoPools() { configs = append(configs, fmt.Sprintf("%s: %d", pool.Name(), pool.Size())) } - log.WithField("context", "main").Debugf("Go pools initialized (%s)", strings.Join(configs, ", ")) + r.log.Debug(fmt.Sprintf("Go pools initialized (%s)", strings.Join(configs, ", "))) } func (r *Runner) setupSignalHandlers() { s := utils.NewGracefulSignals(time.Duration(r.config.App.ShutdownTimeout) * time.Second) s.HandleForceTerminate(func() { - log.Warnf("Immediate termination requested. Stopped") + r.log.Warn("Immediate termination requested. Stopped") r.errChan <- nil }) s.Handle(func(ctx context.Context) error { - log.Infof("Shutting down... (hit Ctrl-C to stop immediately or wait for up to %ds for graceful shutdown)", r.config.App.ShutdownTimeout) + r.log.Info(fmt.Sprintf("Shutting down... (hit Ctrl-C to stop immediately or wait for up to %ds for graceful shutdown)", r.config.App.ShutdownTimeout)) return nil }) diff --git a/cli/options.go b/cli/options.go index 635fb2a8..f71f64d3 100644 --- a/cli/options.go +++ b/cli/options.go @@ -669,7 +669,7 @@ func logCLIFlags(c *config.Config) []cli.Flag { return withDefaults(logCategoryDescription, []cli.Flag{ &cli.StringFlag{ Name: "log_level", - Usage: "Set logging level (debug/info/warn/error/fatal)", + Usage: "Set logging level (debug/info/warn/error)", Value: c.LogLevel, Destination: &c.LogLevel, }, diff --git a/cli/session_options.go b/cli/session_options.go index 7289c6a7..b6adfe51 100644 --- a/cli/session_options.go +++ b/cli/session_options.go @@ -1,13 +1,13 @@ package cli import ( + "log/slog" "strconv" "time" "github.com/anycable/anycable-go/common" "github.com/anycable/anycable-go/node" "github.com/anycable/anycable-go/server" - "github.com/apex/log" ) const ( @@ -38,7 +38,7 @@ func (r *Runner) sessionOptionsFromParams(info *server.RequestInfo) []node.Sessi if rawVal := info.Param(pingIntervalParameter); rawVal != "" { val, err := strconv.Atoi(rawVal) if err != nil { - log.Warnf("Invalid ping interval value, must be integer: %s", rawVal) + slog.Warn("invalid ping interval value, must be integer", "val", rawVal) } else { opts = append(opts, node.WithPingInterval(time.Duration(val)*time.Second)) } diff --git a/config/presets.go b/config/presets.go index 3ba53854..622a3b8c 100644 --- a/config/presets.go +++ b/config/presets.go @@ -3,11 +3,11 @@ package config import ( "errors" "fmt" + "log/slog" "os" "strconv" "strings" - "github.com/apex/log" gonanoid "github.com/matoous/go-nanoid" ) @@ -26,7 +26,7 @@ func (c *Config) LoadPresets() error { return nil } - log.WithField("context", "config").Infof("Load presets: %s", strings.Join(presets, ",")) + slog.With("context", "config").Info("load presets", "presets", strings.Join(presets, ",")) defaults := NewConfig() diff --git a/diagnostics/diagnostics_gops.go b/diagnostics/diagnostics_gops.go index 478cea8e..5d7887e8 100644 --- a/diagnostics/diagnostics_gops.go +++ b/diagnostics/diagnostics_gops.go @@ -11,7 +11,8 @@ import ( "runtime" "strconv" - log "github.com/apex/log" + "log" + "github.com/google/gops/agent" ) diff --git a/enats/enats.go b/enats/enats.go index b14c3ba9..0b9168f8 100644 --- a/enats/enats.go +++ b/enats/enats.go @@ -6,6 +6,8 @@ package enats import ( "context" "fmt" + "log" + "log/slog" "net/url" "os" "path/filepath" @@ -14,7 +16,6 @@ import ( "sync" "time" - "github.com/apex/log" "github.com/joomcode/errorx" gonanoid "github.com/matoous/go-nanoid" "github.com/nats-io/nats-server/v2/server" @@ -45,17 +46,42 @@ type Service struct { // LogEntry represents LoggerV2 decorator for nats server logger type LogEntry struct { - *log.Entry + *slog.Logger +} + +// Debugf is an alias for Debug +func (e *LogEntry) Debugf(format string, v ...interface{}) { + e.Debug(fmt.Sprintf(format, v...)) +} + +// Warnf is an alias for Warn +func (e *LogEntry) Warnf(format string, v ...interface{}) { + e.Warn(fmt.Sprintf(format, v...)) +} + +// Errorf is an alias for Error +func (e *LogEntry) Errorf(format string, v ...interface{}) { + e.Error(fmt.Sprintf(format, v...)) +} + +// Infof is an alias for Info +func (e *LogEntry) Infof(format string, v ...interface{}) { + e.Info(fmt.Sprintf(format, v...)) +} + +// Fatalf is an alias for Fatal +func (e *LogEntry) Fatalf(format string, v ...interface{}) { + log.Fatalf(format, v...) } // Noticef is an alias for Infof func (e *LogEntry) Noticef(format string, v ...interface{}) { - e.Infof(format, v...) + e.Info(fmt.Sprintf(format, v...)) } // Tracef is an alias for Debugf func (e *LogEntry) Tracef(format string, v ...interface{}) { - e.Debugf(format, v...) + e.Debug(fmt.Sprintf(format, v...)) } // NewService returns an instance of NATS service @@ -69,30 +95,30 @@ func (s *Service) Start() error { u, err := url.Parse(s.config.ServiceAddr) if err != nil { - return errorx.Decorate(err, "Error parsing NATS service addr") + return errorx.Decorate(err, "error parsing NATS service addr") } if u.Port() == "" { - return errorx.IllegalArgument.New("Failed to parse NATS server URL, can not fetch port") + return errorx.IllegalArgument.New("failed to parse NATS server URL, can not fetch port") } port, err := strconv.ParseInt(u.Port(), 10, 32) if err != nil { - return errorx.Decorate(err, "Failed to parse NATS service port") + return errorx.Decorate(err, "failed to parse NATS service port") } clusterOpts, err := s.getCluster(s.config.ClusterAddr, s.config.ClusterName) if err != nil { - return errorx.Decorate(err, "Failed to configure NATS cluster") + return errorx.Decorate(err, "failed to configure NATS cluster") } routes, err := s.getRoutes() if err != nil { - return errorx.Decorate(err, "Failed to parse routes") + return errorx.Decorate(err, "failed to parse routes") } gatewayOpts, err := s.getGateway(s.config.GatewayAddr, s.config.GatewayAdvertise, s.config.ClusterName, s.config.Gateways) if err != nil { - return errorx.Decorate(err, "Failed to configure NATS gateway") + return errorx.Decorate(err, "failed to configure NATS gateway") } opts := &server.Options{ @@ -116,11 +142,11 @@ func (s *Service) Start() error { s.server, err = server.NewServer(opts) if err != nil { - return errorx.Decorate(err, "Failed to start NATS server") + return errorx.Decorate(err, "failed to start NATS server") } if s.config.Debug { - e := &LogEntry{log.WithField("service", "nats")} + e := &LogEntry{slog.With("service", "nats")} s.server.SetLogger(e, s.config.Debug, s.config.Trace) } @@ -141,7 +167,7 @@ func (s *Service) WaitReady() error { } return errorx.TimeoutElapsed.New( - "Failed to start NATS server within %d seconds", serverStartTimeout, + "failed to start NATS server within %d seconds", serverStartTimeout, ) } @@ -187,7 +213,7 @@ func (s *Service) getRoutes() ([]*url.URL, error) { for i, r := range s.config.Routes { u, err := url.Parse(r) if err != nil { - return nil, errorx.Decorate(err, "Error parsing route URL") + return nil, errorx.Decorate(err, "error parsing route URL") } routes[i] = u } @@ -202,7 +228,7 @@ func (s *Service) getCluster(addr string, name string) (opts server.ClusterOpts, host, port, err := parseAddress(addr) if err != nil { - err = errorx.Decorate(err, "Failed to parse cluster URL") + err = errorx.Decorate(err, "failed to parse cluster URL") return } @@ -223,7 +249,7 @@ func (s *Service) getGateway(addr string, advertise string, name string, gateway host, port, err := parseAddress(addr) if err != nil { - err = errorx.Decorate(err, "Failed to parse gateway URL") + err = errorx.Decorate(err, "failed to parse gateway URL") return } @@ -240,7 +266,7 @@ func (s *Service) getGateway(addr string, advertise string, name string, gateway parts := strings.SplitN(g, ":", 2) if len(parts) != 2 { - err = errorx.Decorate(err, "Gateway has unknown format: %s", g) + err = errorx.Decorate(err, "gateway has unknown format: %s", g) return } @@ -252,7 +278,7 @@ func (s *Service) getGateway(addr string, advertise string, name string, gateway for j, addr := range addrs { u, gateErr := url.Parse(addr) if gateErr != nil { - err = errorx.Decorate(gateErr, "Error parsing gateway URL") + err = errorx.Decorate(gateErr, "error parsing gateway URL") return } @@ -273,16 +299,16 @@ func parseAddress(addr string) (string, int, error) { uri, err := url.Parse(addr) if err != nil { - return "", 0, errorx.Decorate(err, "Failed to parse URL") + return "", 0, errorx.Decorate(err, "failed to parse URL") } if uri.Port() == "" { - return "", 0, errorx.IllegalArgument.New("Port cannot be empty") + return "", 0, errorx.IllegalArgument.New("port cannot be empty") } port, err := strconv.ParseInt(uri.Port(), 10, 32) if err != nil { - return "", 0, errorx.Decorate(err, "Port is not valid") + return "", 0, errorx.Decorate(err, "port is not valid") } return uri.Hostname(), int(port), nil @@ -320,7 +346,7 @@ func (s *Service) WaitJetStreamReady(maxSeconds int) error { c, err := nats.Connect("", nats.InProcessServer(s.server)) if err != nil { - log.Debugf("NATS server not accepting connections: %v", err) + slog.With("context", "enats").Debug("NATS server not accepting connections", "error", err.Error()) continue } @@ -332,13 +358,13 @@ func (s *Service) WaitJetStreamReady(maxSeconds int) error { st, err := j.StreamInfo("__anycable__ready__", nats.MaxWait(1*time.Second)) if err == nats.ErrStreamNotFound || st != nil { leader := s.server.JetStreamIsLeader() - log.Debugf("JetStream cluster is ready: leader=%v", leader) + slog.With("context", "enats").Debug("JetStream cluster is ready", "leader", leader) return nil } c.Close() - log.Debugf("JetStream cluster is not ready yet, waiting for 1 second...") + slog.With("context", "enats").Debug("JetStream cluster is not ready yet, waiting for 1 second...") time.Sleep(1 * time.Second) } diff --git a/features/metrics.testfile b/features/metrics.testfile index 8fcf5da5..ac255ce5 100644 --- a/features/metrics.testfile +++ b/features/metrics.testfile @@ -54,6 +54,6 @@ stop :anycable logs = stdout(:anycable) # We disabled disconnector, so there should be just 2 RPC calls (Connect) -if logs !~ /failed_auths_total=0 rpc_call_total=2/ +unless logs =~ /failed_auths_total=0/ && logs =~ /rpc_call_total=2/ fail "Metrics logs not found:\n#{logs}" end diff --git a/go.mod b/go.mod index a3a0dfae..9c972a40 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,9 @@ go 1.21 require ( github.com/FZambia/sentinel v1.1.1 - github.com/apex/log v1.9.0 github.com/davecgh/go-spew v1.1.1 // indirect github.com/fullstorydev/grpchan v1.1.1 github.com/go-chi/chi/v5 v5.0.11 - github.com/golang-collections/go-datastructures v0.0.0-20150211160725-59788d5eb259 github.com/golang-jwt/jwt v3.2.2+incompatible github.com/golang/protobuf v1.5.3 github.com/gomodule/redigo v1.8.9 @@ -16,12 +14,11 @@ require ( github.com/gorilla/websocket v1.5.1 github.com/hofstadter-io/cinful v1.0.0 github.com/joomcode/errorx v1.1.1 + github.com/lmittmann/tint v1.0.4 github.com/matoous/go-nanoid v1.5.0 github.com/mattn/go-isatty v0.0.20 github.com/mitchellh/go-mruby v0.0.0-20200315023956-207cedc21542 - github.com/namsral/flag v1.7.4-pre github.com/nats-io/nats.go v1.31.0 - github.com/pkg/errors v0.9.1 // indirect github.com/posthog/posthog-go v0.0.0-20240110105835-f2ee529330e9 github.com/redis/rueidis v1.0.26 github.com/smira/go-statsd v1.3.3 @@ -39,6 +36,7 @@ require ( github.com/bufbuild/protocompile v0.7.1 // indirect github.com/google/uuid v1.5.0 // indirect github.com/klauspost/compress v1.17.4 // indirect + github.com/kr/pretty v0.2.0 // indirect github.com/minio/highwayhash v1.0.2 // indirect github.com/nats-io/jwt/v2 v2.5.3 // indirect golang.org/x/time v0.5.0 // indirect diff --git a/go.sum b/go.sum index 409b2f1e..086e80df 100644 --- a/go.sum +++ b/go.sum @@ -2,13 +2,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/FZambia/sentinel v1.1.1 h1:0ovTimlR7Ldm+wR15GgO+8C2dt7kkn+tm3PQS+Qk3Ek= github.com/FZambia/sentinel v1.1.1/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI= -github.com/apex/log v1.9.0 h1:FHtw/xuaM8AgmvDDTI9fiwoAL25Sq2cxojnZICUU8l0= -github.com/apex/log v1.9.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA= -github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo= -github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE= -github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= -github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/bufbuild/protocompile v0.7.1 h1:Kd8fb6EshOHXNNRtYAmLAwy/PotlyFoN0iMbuwGNh0M= github.com/bufbuild/protocompile v0.7.1/go.mod h1:+Etjg4guZoAqzVk2czwEQP12yaxLJ8DxuqCJ9qHdH94= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -24,21 +17,15 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fullstorydev/grpchan v1.1.1 h1:heQqIJlAv5Cnks9a70GRL2EJke6QQoUB25VGR6TZQas= github.com/fullstorydev/grpchan v1.1.1/go.mod h1:f4HpiV8V6htfY/K44GWV1ESQzHBTq7DinhzqQ95lpgc= github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA= github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/golang-collections/go-datastructures v0.0.0-20150211160725-59788d5eb259 h1:ZHJ7+IGpuOXtVf6Zk/a3WuHQgkC+vXwaqfUBDFwahtI= -github.com/golang-collections/go-datastructures v0.0.0-20150211160725-59788d5eb259/go.mod h1:9Qcha0gTWLw//0VNka1Cbnjvg3pNKGFdAm7E9sBabxE= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -62,7 +49,6 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gops v0.3.28 h1:2Xr57tqKAmQYRAfG12E+yLcoa2Y42UJo2lOrUFL9ark= github.com/google/gops v0.3.28/go.mod h1:6f6+Nl8LcHrzJwi8+p0ii+vmBFSlB4f8cOOkTJ7sk4c= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= @@ -71,20 +57,16 @@ github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/ github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/hofstadter-io/cinful v1.0.0 h1:G/kZ/iwM0EyTyEtdE4UyLNLOVNSSHVs1cW0DC7uoxmE= github.com/hofstadter-io/cinful v1.0.0/go.mod h1:VySLSoBPf5gTFEeumOhl8I2cjspiJAB3/XGgrivpUZI= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jhump/gopoet v0.0.0-20190322174617-17282ff210b3/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI= github.com/jhump/gopoet v0.1.0/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI= github.com/jhump/goprotoc v0.5.0/go.mod h1:VrbvcYrQOrTi3i0Vf+m+oqQWk9l72mjkJCYo7UvLHRQ= github.com/jhump/protoreflect v1.11.0/go.mod h1:U7aMIjN0NWq9swDP7xDdoMfRHb35uiuTd3Z9nFXJf5E= github.com/jhump/protoreflect v1.15.4 h1:mrwJhfQGGljwvR/jPEocli8KA6G9afbQpH8NY2wORcI= github.com/jhump/protoreflect v1.15.4/go.mod h1:2B+zwrnMY3TTIqEK01OG/d3pyUycQBfDf+bx8fE2DNg= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/joomcode/errorx v1.1.1 h1:/LFG/qSk1gUTuZjs+qlyOJEpcVjD9DXgBNFhdZkQrjY= github.com/joomcode/errorx v1.1.1/go.mod h1:eQzdtdlNyN7etw6YCS4W4+lu442waxZYw5yvz0ULrRo= -github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -92,21 +74,16 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lmittmann/tint v1.0.4 h1:LeYihpJ9hyGvE0w+K2okPTGUdVLfng1+nDNVR4vWISc= +github.com/lmittmann/tint v1.0.4/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= github.com/matoous/go-nanoid v1.5.0 h1:VRorl6uCngneC4oUQqOYtO3S0H5QKFtKuKycFG3euek= github.com/matoous/go-nanoid v1.5.0/go.mod h1:zyD2a71IubI24efhpvkJz+ZwfwagzgSO6UNiFsZKN7U= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/mitchellh/go-mruby v0.0.0-20200315023956-207cedc21542 h1:/MjcGU93aaORB6Mydh9Q4D/oOim9BoR4jtpaAOgVZLQ= github.com/mitchellh/go-mruby v0.0.0-20200315023956-207cedc21542/go.mod h1:TpwfcXhxDvAzz7wUcsTWu+FCaWGGLyyVZrL6sdkvK8k= -github.com/namsral/flag v1.7.4-pre h1:b2ScHhoCUkbsq0d2C15Mv+VU8bl8hAXV8arnWiOHNZs= -github.com/namsral/flag v1.7.4-pre/go.mod h1:OXldTctbM6SWH1K899kPZcf65KxJiD7MsceFUpB5yDo= github.com/nats-io/jwt/v2 v2.5.3 h1:/9SWvzc6hTfamcgXJ3uYRpgj+QuY2aLNqRiqrKcrpEo= github.com/nats-io/jwt/v2 v2.5.3/go.mod h1:iysuPemFcc7p4IoYots3IuELSI4EDe9Y0bQMe+I3Bf4= github.com/nats-io/nats-server/v2 v2.10.7 h1:f5VDy+GMu7JyuFA0Fef+6TfulfCs5nBTgq7MMkFJx5Y= @@ -117,13 +94,8 @@ github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.28.0 h1:i2rg/p9n/UqIDAMFUJ6qIUUMcsqOuUHgbpbu235Vr1c= github.com/onsi/gomega v1.28.0/go.mod h1:A1H2JE76sI14WIP57LMKj7FVfCHx3g3BcZVjJG8bjX8= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posthog/posthog-go v0.0.0-20240110105835-f2ee529330e9 h1:KAKskYPB1yqqx1LpRtHnJSH1A65ttD+eD68sfjtDQps= @@ -133,15 +105,10 @@ github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/redis/rueidis v1.0.26 h1:u2QVp9yEREZQ0xfUgmaOBL0mED14u+j+akkfCRcOJYw= github.com/redis/rueidis v1.0.26/go.mod h1:NT7lPuiVYijdZVsV0V8i9ZUhqe1OMGaq+NiQigNuKlg= -github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= -github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= -github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= github.com/smira/go-statsd v1.3.3 h1:WnMlmGTyMpzto+HvOJWRPoLaLlk5EGfzsnlQBcvj4yI= github.com/smira/go-statsd v1.3.3/go.mod h1:RjdsESPgDODtg1VpVVf9MJrEW2Hw0wtRNbmB1CAhu6A= github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= @@ -154,20 +121,12 @@ github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdr github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= -github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= -github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= -github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj52Uc= -github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= -github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao= -github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4= github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= @@ -176,7 +135,6 @@ github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsr go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= @@ -188,11 +146,9 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= @@ -204,10 +160,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -255,13 +209,9 @@ google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHh gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/gobench/gobench.go b/gobench/gobench.go index 2d2e2643..d445a0e1 100644 --- a/gobench/gobench.go +++ b/gobench/gobench.go @@ -5,10 +5,10 @@ package gobench import ( "encoding/json" "fmt" + "log/slog" "github.com/anycable/anycable-go/common" "github.com/anycable/anycable-go/metrics" - "github.com/apex/log" nanoid "github.com/matoous/go-nanoid" ) @@ -31,14 +31,14 @@ type BroadcastMessage struct { // Controller implements node.Controller interface for gRPC type Controller struct { metrics *metrics.Metrics - log *log.Entry + log *slog.Logger } // NewController builds new Controller from config func NewController(metrics *metrics.Metrics) *Controller { metrics.RegisterCounter(metricsCalls, "The total number of Go channels calls") - return &Controller{log: log.WithField("context", "gobench"), metrics: metrics} + return &Controller{log: slog.With("context", "gobench"), metrics: metrics} } // Start is no-op diff --git a/hub/gate.go b/hub/gate.go index bce08052..f935bc14 100644 --- a/hub/gate.go +++ b/hub/gate.go @@ -2,11 +2,11 @@ package hub import ( "context" + "log/slog" "sync" "github.com/anycable/anycable-go/common" "github.com/anycable/anycable-go/encoders" - "github.com/apex/log" ) // Gate plays the role of a shard for the hub. @@ -25,7 +25,7 @@ type Gate struct { sender chan *common.StreamMessage mu sync.RWMutex - log *log.Entry + log *slog.Logger } // NewGate creates a new gate. @@ -35,7 +35,7 @@ func NewGate(ctx context.Context) *Gate { sessionsStreams: make(map[HubSession]map[string][]string), // Use a buffered channel to avoid blocking sender: make(chan *common.StreamMessage, 256), - log: log.WithField("component", "hub"), + log: slog.With("component", "hub"), } go g.broadcastLoop(ctx) @@ -47,13 +47,13 @@ func NewGate(ctx context.Context) *Gate { func (g *Gate) Broadcast(streamMsg *common.StreamMessage) { stream := streamMsg.Stream - ctx := g.log.WithField("stream", stream) + ctx := g.log.With("stream", stream) - ctx.Debugf("Broadcast message: %v", streamMsg) + ctx.Debug("broadcast message", "stream", streamMsg, "data", streamMsg.Data, "offset", streamMsg.Offset, "epoch", streamMsg.Epoch, "meta", streamMsg.Meta) g.mu.RLock() if _, ok := g.streams[stream]; !ok { - ctx.Debug("No sessions") + ctx.Debug("no sessions") g.mu.RUnlock() return } diff --git a/hub/hub.go b/hub/hub.go index 78405b06..9630f876 100644 --- a/hub/hub.go +++ b/hub/hub.go @@ -3,12 +3,12 @@ package hub import ( "context" "hash/fnv" + "log/slog" "sync" "github.com/anycable/anycable-go/common" "github.com/anycable/anycable-go/encoders" "github.com/anycable/anycable-go/utils" - "github.com/apex/log" ) type HubSession interface { @@ -81,7 +81,7 @@ type Hub struct { doneFn context.CancelFunc // Log context - log *log.Entry + log *slog.Logger // go pool pool *utils.GoPool @@ -105,7 +105,7 @@ func NewHub(poolSize int) *Hub { pool: utils.NewGoPool("remote commands", 256), doneFn: doneFn, shutdown: make(chan struct{}), - log: log.WithFields(log.Fields{"context": "hub"}), + log: slog.With("context", "hub"), } } @@ -202,9 +202,8 @@ func (h *Hub) AddSession(session HubSession) { h.identifiers[identifiers][uid] = true - h.log.WithField("sid", uid).Debugf( - "Registered with identifiers: %s", - identifiers, + h.log.With("sid", uid).Debug( + "registered", "ids", identifiers, ) } @@ -214,7 +213,7 @@ func (h *Hub) RemoveSession(session HubSession) { if _, ok := h.sessions[uid]; !ok { h.mu.RUnlock() - h.log.WithField("sid", uid).Warn("Session hasn't been registered") + h.log.With("sid", uid).Warn("session hasn't been registered") return } h.mu.RUnlock() @@ -233,7 +232,7 @@ func (h *Hub) RemoveSession(session HubSession) { h.mu.Unlock() - h.log.WithField("sid", uid).Debug("Unregistered") + h.log.With("sid", uid).Debug("unregistered") } func (h *Hub) unsubscribeSessionFromAllChannels(session HubSession) { @@ -268,10 +267,7 @@ func (h *Hub) UnsubscribeSessionFromChannel(session HubSession, targetIdentifier } } - h.log.WithFields(log.Fields{ - "sid": sid, - "channel": targetIdentifier, - }).Debug("Unsubscribed") + h.log.With("sid", sid).Debug("unsubscribed", "channel", targetIdentifier) } func (h *Hub) SubscribeSession(session HubSession, stream string, identifier string) { @@ -288,11 +284,7 @@ func (h *Hub) SubscribeSession(session HubSession, stream string, identifier str h.sessions[sid].AddStream(stream, identifier) - h.log.WithFields(log.Fields{ - "sid": sid, - "channel": identifier, - "stream": stream, - }).Debug("Subscribed") + h.log.With("sid", sid).Debug("subscribed", "channel", identifier, "stream", stream) } func (h *Hub) UnsubscribeSession(session HubSession, stream string, identifier string) { @@ -307,11 +299,7 @@ func (h *Hub) UnsubscribeSession(session HubSession, stream string, identifier s info.RemoveStream(stream, identifier) } - h.log.WithFields(log.Fields{ - "sid": sid, - "channel": identifier, - "stream": stream, - }).Debug("Unsubscribed") + h.log.With("sid", sid).Debug("unsubscribed", "channel", identifier, "stream", stream) } func (h *Hub) broadcastToStream(streamMsg *common.StreamMessage) { @@ -324,7 +312,7 @@ func (h *Hub) disconnectSessions(identifier string, reconnect bool) { h.mu.RUnlock() if !ok { - h.log.Debugf("Can not disconnect sessions: unknown identifier %s", identifier) + h.log.Debug("cannot disconnect session", "identifier", identifier, "reason", "not found") return } diff --git a/identity/jwt.go b/identity/jwt.go index fb2d8c51..87506a4b 100644 --- a/identity/jwt.go +++ b/identity/jwt.go @@ -2,11 +2,11 @@ package identity import ( "fmt" + "log/slog" "net/url" "strings" "github.com/anycable/anycable-go/common" - "github.com/apex/log" "github.com/golang-jwt/jwt" ) @@ -38,7 +38,7 @@ type JWTIdentifier struct { paramName string headerName string required bool - log *log.Entry + log *slog.Logger } var _ Identifier = (*JWTIdentifier)(nil) @@ -49,7 +49,7 @@ func NewJWTIdentifier(config *JWTConfig) *JWTIdentifier { paramName: config.Param, headerName: strings.ToLower(fmt.Sprintf("x-%s", config.Param)), required: config.Force, - log: log.WithField("context", "jwt"), + log: slog.With("context", "jwt"), } } @@ -81,7 +81,7 @@ func (i *JWTIdentifier) Identify(sid string, env *common.SessionEnv) (*common.Co } if rawToken == "" { - i.log.Debugf("No token is found (url=%s, headers=%v)", env.URL, env.Headers) + i.log.Debug("no token is found", "url", env.URL, "headers", env.Headers) if i.required { return unauthorizedResponse(), nil @@ -92,7 +92,7 @@ func (i *JWTIdentifier) Identify(sid string, env *common.SessionEnv) (*common.Co token, err := jwt.Parse(rawToken, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) + return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) } return i.secret, nil @@ -101,13 +101,13 @@ func (i *JWTIdentifier) Identify(sid string, env *common.SessionEnv) (*common.Co if err != nil { if ve, ok := err.(*jwt.ValidationError); ok { if ve.Errors&(jwt.ValidationErrorExpired) != 0 { - i.log.Debugf("Token has expired") + i.log.Debug("token has expired") return expiredResponse(), nil } } - i.log.Debugf("Invalid token: %v", err) + i.log.Debug("invalid token", "error", err) return unauthorizedResponse(), nil } diff --git a/logger/logger.go b/logger/logger.go new file mode 100644 index 00000000..24d85779 --- /dev/null +++ b/logger/logger.go @@ -0,0 +1,61 @@ +package logger + +import ( + "fmt" + "log/slog" + "os" + + "github.com/anycable/anycable-go/utils" + "github.com/lmittmann/tint" +) + +func InitLogger(format string, level string) (slog.Handler, error) { + logLevel, err := parseLevel(level) + + if err != nil { + return nil, err + } + + var handler slog.Handler + + switch format { + case "text": + { + opts := &tint.Options{ + Level: logLevel, + NoColor: !utils.IsTTY(), + TimeFormat: "2006-01-02 15:04:05.000", + } + handler = tint.NewHandler(os.Stdout, opts) + } + case "json": + { + handler = slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: logLevel}) + } + default: + { + return nil, fmt.Errorf("unknown log format: %s.\nAvaialable formats are: text, json", format) + } + } + + logger := slog.New(handler) + slog.SetDefault(logger) + + return handler, nil +} + +var LevelNames = map[string]slog.Level{ + "debug": slog.LevelDebug, + "info": slog.LevelInfo, + "warn": slog.LevelWarn, + "error": slog.LevelError, +} + +func parseLevel(level string) (slog.Level, error) { + lvl, ok := LevelNames[level] + if !ok { + return slog.LevelInfo, fmt.Errorf("unknown log level: %s.\nAvailable levels are: debug, info, warn, error", level) + } + + return lvl, nil +} diff --git a/metrics/custom_printer.go b/metrics/custom_printer.go index cb3e5cc5..4f0563bd 100644 --- a/metrics/custom_printer.go +++ b/metrics/custom_printer.go @@ -4,8 +4,10 @@ package metrics import ( + "fmt" + "log/slog" + "github.com/anycable/anycable-go/mrb" - "github.com/apex/log" "github.com/mitchellh/go-mruby" ) @@ -34,7 +36,7 @@ func (p *RubyPrinter) Run(interval int) error { p.mrbModule = mod.MrbValue(p.engine.VM) - log.WithField("context", "metrics").Infof("Log metrics every %ds using a custom Ruby formatter from %s", interval, p.path) + slog.With("context", "metrics").Info(fmt.Sprintf("Log metrics every %ds using a custom Ruby formatter from %s", interval, p.path)) return nil } @@ -62,9 +64,9 @@ func (printer *RubyPrinter) Print(snapshot map[string]uint64) { result, err := printer.mrbModule.Call("call", rhash) if err != nil { - log.WithField("context", "metrics").Error(err.Error()) + slog.With("context", "metrics").Error("mruby call failed", "error", err.Error()) return } - log.Info(result.String()) + slog.Info(result.String()) } diff --git a/metrics/custom_printer_unsupported.go b/metrics/custom_printer_unsupported.go index 09af430e..1dd2be9f 100644 --- a/metrics/custom_printer_unsupported.go +++ b/metrics/custom_printer_unsupported.go @@ -8,5 +8,5 @@ import "errors" // NewCustomPrinter generates log formatter from the provided (as path) // Ruby script func NewCustomPrinter(path string) (*BasePrinter, error) { - return nil, errors.New("Not supported") + return nil, errors.New("unsupported") } diff --git a/metrics/metrics.go b/metrics/metrics.go index ffc795f5..fe592e6f 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -3,13 +3,13 @@ package metrics import ( "context" "fmt" + "log/slog" "net/http" "strconv" "sync" "time" "github.com/anycable/anycable-go/server" - "github.com/apex/log" ) const DefaultRotateInterval = 15 @@ -44,7 +44,7 @@ type Metrics struct { gauges map[string]*Gauge shutdownCh chan struct{} closed bool - log *log.Entry + log *slog.Logger } var _ Instrumenter = (*Metrics)(nil) @@ -109,7 +109,7 @@ func NewMetrics(writers []IntervalWriter, rotateIntervalSeconds int) *Metrics { counters: make(map[string]*Counter), gauges: make(map[string]*Gauge), shutdownCh: make(chan struct{}), - log: log.WithField("context", "metrics"), + log: slog.With("context", "metrics"), } } @@ -124,17 +124,17 @@ func (m *Metrics) RegisterWriter(w IntervalWriter) { // Run periodically updates counters delta (and logs metrics if necessary) func (m *Metrics) Run() error { if m.server != nil { - m.log.Infof("Serve metrics at %s%s", m.server.Address(), m.httpPath) + m.log.Info(fmt.Sprintf("Serve metrics at %s%s", m.server.Address(), m.httpPath)) if err := m.server.StartAndAnnounce("Metrics server"); err != nil { if !m.server.Stopped() { - return fmt.Errorf("Metrics HTTP server at %s stopped: %v", m.server.Address(), err) + return fmt.Errorf("metrics HTTP server at %s stopped: %v", m.server.Address(), err) } } } if len(m.writers) == 0 { - m.log.Debug("No metrics writers. Disable metrics rotation") + m.log.Debug("no metrics writers, disabling metrics rotation") return nil } @@ -153,12 +153,12 @@ func (m *Metrics) Run() error { case <-m.shutdownCh: return nil case <-time.After(m.rotateInterval): - m.log.Debugf("Rotate metrics (interval %v)", m.rotateInterval) + m.log.Debug("rotate metrics", "interval", m.rotateInterval) m.rotate() for _, writer := range m.writers { if err := writer.Write(m); err != nil { - m.log.Errorf("Metrics writer failed to write: %v", err) + m.log.Error("metrics writer failed to write", "error", err.Error()) } } } diff --git a/metrics/printer.go b/metrics/printer.go index 0fcd36b2..b63ecc70 100644 --- a/metrics/printer.go +++ b/metrics/printer.go @@ -1,10 +1,10 @@ package metrics import ( + "log/slog" "strings" "github.com/anycable/anycable-go/utils" - "github.com/apex/log" ) // Printer describes metrics logging interface @@ -34,12 +34,9 @@ func NewBasePrinter(filterList []string) *BasePrinter { // Run prints a message to the log with metrics logging details func (p *BasePrinter) Run(interval int) error { if p.filter != nil { - log.WithField("context", "metrics").Infof( - "Log metrics every %ds (only selected fields: %s)", - interval, strings.Join(utils.Keys(p.filter), ", "), - ) + slog.With("context", "metrics").Info("log metrics", "interval", interval, "fields", strings.Join(utils.Keys(p.filter), ",")) } else { - log.WithField("context", "metrics").Infof("Log metrics every %ds", interval) + slog.With("context", "metrics").Info("log metrics", "interval", interval) } return nil } @@ -56,17 +53,15 @@ func (p *BasePrinter) Write(m *Metrics) error { // Print logs stats data using global logger with info level func (p *BasePrinter) Print(snapshot map[string]uint64) { - fields := make(log.Fields, len(snapshot)+1) - - fields["context"] = "metrics" + fields := make([]interface{}, 0) for k, v := range snapshot { if p.filter == nil { - fields[k] = v + fields = append(fields, k, v) } else if _, ok := p.filter[k]; ok { - fields[k] = v + fields = append(fields, k, v) } } - log.WithFields(fields).Info("") + slog.With("context", "metrics").Info("", fields...) } diff --git a/metrics/statsd_writer.go b/metrics/statsd_writer.go index b36e13bd..53056460 100644 --- a/metrics/statsd_writer.go +++ b/metrics/statsd_writer.go @@ -2,10 +2,10 @@ package metrics import ( "fmt" + "log/slog" "strings" "sync" - "github.com/apex/log" "github.com/smira/go-statsd" ) @@ -17,16 +17,16 @@ type StatsdConfig struct { } type StatsdLogger struct { - log *log.Entry + log *slog.Logger } func (lg *StatsdLogger) Printf(msg string, args ...interface{}) { msg = strings.TrimPrefix(msg, "[STATSD] ") // Statsd only prints errors and warnings if strings.Contains(msg, "Error") { - lg.log.Errorf(msg, args...) + lg.log.Error(fmt.Sprintf(msg, args...)) } else { - lg.log.Warnf(msg, args...) + lg.log.Warn(fmt.Sprintf(msg, args...)) } } @@ -53,7 +53,7 @@ func NewStatsdWriter(c StatsdConfig, tags map[string]string) *StatsdWriter { } func (sw *StatsdWriter) Run(interval int) error { - sl := StatsdLogger{log.WithField("context", "statsd")} + sl := StatsdLogger{slog.With("context", "statsd")} opts := []statsd.Option{ statsd.MaxPacketSize(sw.config.MaxPacketSize), statsd.MetricPrefix(sw.config.Prefix), @@ -63,7 +63,11 @@ func (sw *StatsdWriter) Run(interval int) error { var tagsInfo string if sw.tags != nil { - tagsStyle := resolveTagsStyle(sw.config.TagFormat) + tagsStyle, err := resolveTagsStyle(sw.config.TagFormat) + if err != nil { + return err + } + tags := convertTags(sw.tags) opts = append(opts, statsd.TagStyle(tagsStyle), @@ -78,10 +82,12 @@ func (sw *StatsdWriter) Run(interval int) error { opts..., ) - log.WithField("context", "metrics"). - Infof( - "Send statsd metrics to %s with every %vs (prefix=%s%s)", - sw.config.Host, interval, sw.config.Prefix, tagsInfo, + slog.With("context", "metrics"). + Info( + fmt.Sprintf( + "Send statsd metrics to %s with every %vs (prefix=%s%s)", + sw.config.Host, interval, sw.config.Prefix, tagsInfo, + ), ) return nil @@ -114,17 +120,17 @@ func (sw *StatsdWriter) Write(m *Metrics) error { return nil } -func resolveTagsStyle(name string) *statsd.TagFormat { +func resolveTagsStyle(name string) (*statsd.TagFormat, error) { switch name { case "datadog": - return statsd.TagFormatDatadog + return statsd.TagFormatDatadog, nil case "influxdb": - return statsd.TagFormatInfluxDB + return statsd.TagFormatInfluxDB, nil case "graphite": - return statsd.TagFormatGraphite + return statsd.TagFormatGraphite, nil } - panic(fmt.Errorf("Unknown StatsD tags format: %s", name)) + return nil, fmt.Errorf("unknown StatsD tags format: %s", name) } func convertTags(tags map[string]string) []statsd.Tag { diff --git a/node/disconnect_queue.go b/node/disconnect_queue.go index d81e6331..e5f8be16 100644 --- a/node/disconnect_queue.go +++ b/node/disconnect_queue.go @@ -3,10 +3,9 @@ package node import ( "context" "fmt" + "log/slog" "sync" "time" - - "github.com/apex/log" ) // DisconnectQueueConfig contains DisconnectQueue configuration @@ -32,7 +31,7 @@ type DisconnectQueue struct { // Call RPC Disconnect for connections disconnect chan *Session // Logger with context - log *log.Entry + log *slog.Logger // Control channel to shutdown the executer shutdown chan struct{} // Executer stopped status @@ -45,9 +44,9 @@ type DisconnectQueue struct { func NewDisconnectQueue(node *Node, config *DisconnectQueueConfig) *DisconnectQueue { rateDuration := time.Millisecond * time.Duration(1000/config.Rate) - ctx := log.WithField("context", "disconnector") + ctx := slog.With("context", "disconnector") - ctx.Debugf("Calls rate: %v", rateDuration) + ctx.Debug("calls rate", "rate", rateDuration) return &DisconnectQueue{ node: node, @@ -94,7 +93,7 @@ func (d *DisconnectQueue) Shutdown(ctx context.Context) error { } defer func() { - d.log.Infof("Disconnected %d sessions", actual) + d.log.Info("disconnected sessions", "num", actual) }() deadline, ok := ctx.Deadline() @@ -102,9 +101,9 @@ func (d *DisconnectQueue) Shutdown(ctx context.Context) error { if ok { timeLeft := time.Until(deadline) - d.log.Infof("Invoking remaining disconnects for %2fs: ~%d", timeLeft.Seconds(), left) + d.log.Info("invoking remaining disconnects", "interval", timeLeft.Seconds(), "num", left) } else { - d.log.Infof("Invoking remaining disconnects: ~%d", left) + d.log.Info("invoking remaining disconnects", "num", left) } for { @@ -114,7 +113,7 @@ func (d *DisconnectQueue) Shutdown(ctx context.Context) error { actual++ case <-ctx.Done(): - return fmt.Errorf("Had no time to invoke Disconnect calls: ~%d", len(d.disconnect)) + return fmt.Errorf("had no time to invoke Disconnect calls: ~%d", len(d.disconnect)) default: return nil } diff --git a/node/disconnector.go b/node/disconnector.go index 161f9e11..3eec2aaa 100644 --- a/node/disconnector.go +++ b/node/disconnector.go @@ -2,8 +2,7 @@ package node import ( "context" - - "github.com/apex/log" + "log/slog" ) // Disconnector is an interface for disconnect queue implementation @@ -19,7 +18,7 @@ type NoopDisconnectQueue struct{} // Run does nothing func (d *NoopDisconnectQueue) Run() error { - log.WithField("context", "disconnector").Info("Disconnect events are turned off") + slog.With("context", "disconnector").Info("disconnect events are turned off") return nil } diff --git a/node/node.go b/node/node.go index 29cdfd46..52a4760d 100644 --- a/node/node.go +++ b/node/node.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "log/slog" "runtime" "sync" "time" @@ -14,7 +15,6 @@ import ( "github.com/anycable/anycable-go/metrics" "github.com/anycable/anycable-go/utils" "github.com/anycable/anycable-go/ws" - "github.com/apex/log" ) const ( @@ -72,7 +72,7 @@ type Node struct { shutdownCh chan struct{} shutdownMu sync.Mutex closed bool - log *log.Entry + log *slog.Logger } var _ AppNode = (*Node)(nil) @@ -84,7 +84,7 @@ func NewNode(controller Controller, metrics *metrics.Metrics, config *Config) *N config: config, controller: controller, shutdownCh: make(chan struct{}), - log: log.WithFields(log.Fields{"context": "node"}), + log: slog.With("context", "node"), } node.hub = hub.NewHub(config.HubGopoolSize) @@ -121,7 +121,7 @@ func (n *Node) Instrumenter() metrics.Instrumenter { // HandleCommand parses incoming message from client and // execute the command (if recognized) func (n *Node) HandleCommand(s *Session, msg *common.Message) (err error) { - s.Log.Debugf("Incoming message: %v", msg) + s.Log.Debug("incoming message", "data", msg) switch msg.Command { case "pong": s.handlePong(msg) @@ -134,7 +134,7 @@ func (n *Node) HandleCommand(s *Session, msg *common.Message) (err error) { case "history": err = n.History(s, msg) default: - err = fmt.Errorf("Unknown command: %s", msg.Command) + err = fmt.Errorf("unknown command: %s", msg.Command) } return @@ -146,7 +146,7 @@ func (n *Node) HandleBroadcast(raw []byte) { if err != nil { n.metrics.CounterIncrement(metricsUnknownBroadcast) - n.log.Warnf("Failed to parse pubsub message '%s' with error: %v", raw, err) + n.log.Warn("failed to parse pubsub message", "data", raw, "error", err.Error()) return } @@ -168,7 +168,7 @@ func (n *Node) HandlePubSub(raw []byte) { if err != nil { n.metrics.CounterIncrement(metricsUnknownBroadcast) - n.log.Warnf("Failed to parse pubsub message '%s' with error: %v", raw, err) + n.log.Warn("failed to parse pubsub message", "data", raw, "error", err.Error()) return } @@ -195,7 +195,7 @@ func (n *Node) Shutdown(ctx context.Context) (err error) { n.shutdownMu.Lock() if n.closed { n.shutdownMu.Unlock() - return errors.New("Already shut down") + return errors.New("already shut down") } close(n.shutdownCh) @@ -207,7 +207,7 @@ func (n *Node) Shutdown(ctx context.Context) (err error) { active := n.hub.Size() if active > 0 { - n.log.Infof("Closing active connections: %d", active) + n.log.Info("closing active connections", "num", active) n.disconnectAll(ctx) } @@ -218,7 +218,7 @@ func (n *Node) Shutdown(ctx context.Context) (err error) { err := n.disconnector.Shutdown(ctx) if err != nil { - n.log.Warnf("%v", err) + n.log.Warn("failed to shutdown disconnector gracefully", "error", err.Error()) } } @@ -226,7 +226,7 @@ func (n *Node) Shutdown(ctx context.Context) (err error) { err := n.controller.Shutdown() if err != nil { - n.log.Warnf("%v", err) + n.log.Warn("failed to shutdown controller gracefully", "error", err.Error()) } } @@ -301,7 +301,7 @@ func (n *Node) Authenticate(s *Session, options ...AuthOption) (res *common.Conn if s.IsResumeable() { if berr := n.broker.CommitSession(s.GetID(), s); berr != nil { - s.Log.Errorf("Failed to persist session in cache: %v", berr) + s.Log.Error("failed to persist session in cache", "error", berr.Error()) } } @@ -327,23 +327,23 @@ func (n *Node) TryRestoreSession(s *Session) (restored bool) { cached_session, err := n.broker.RestoreSession(prev_sid) if err != nil { - s.Log.Errorf("Failed to fetch session cache %s: %s", prev_sid, err.Error()) + s.Log.Error("failed to fetch session cache", "old_sid", prev_sid, "error", err.Error()) return false } if cached_session == nil { - s.Log.Debugf("Couldn't find session to restore from: %s", prev_sid) + s.Log.Debug("session not found in cache", "old_sid", prev_sid) return false } err = s.RestoreFromCache(cached_session) if err != nil { - s.Log.Errorf("Failed to restore session from cache %s: %s", prev_sid, err.Error()) + s.Log.Error("failed to restore session from cache", "old_sid", prev_sid, "error", err.Error()) return false } - s.Log.Debugf("Session restored from: %s", prev_sid) + s.Log.Debug("session restored", "old_sid", prev_sid) s.Connected = true n.hub.AddSession(s) @@ -366,7 +366,7 @@ func (n *Node) TryRestoreSession(s *Session) (restored bool) { if s.IsResumeable() { if berr := n.broker.CommitSession(s.GetID(), s); berr != nil { - s.Log.Errorf("Failed to persist session in cache: %v", berr) + s.Log.Error("failed to persist session in cache", "error", berr.Error()) } } @@ -379,7 +379,7 @@ func (n *Node) Subscribe(s *Session, msg *common.Message) (res *common.CommandRe if ok := s.subscriptions.HasChannel(msg.Identifier); ok { s.smu.Unlock() - err = fmt.Errorf("Already subscribed to %s", msg.Identifier) + err = fmt.Errorf("already subscribed to %s", msg.Identifier) return } @@ -389,14 +389,14 @@ func (n *Node) Subscribe(s *Session, msg *common.Message) (res *common.CommandRe if err != nil { // nolint: gocritic if res == nil || res.Status == common.ERROR { - s.Log.Errorf("Subscribe error: %v", err) + s.Log.Error("subscribe failed", "error", err.Error()) } } else if res.Status == common.SUCCESS { confirmed = true s.subscriptions.AddChannel(msg.Identifier) - s.Log.Debugf("Subscribed to channel: %s", msg.Identifier) + s.Log.Debug("subscribed", "channel", msg.Identifier) } else { - s.Log.Debugf("Subscription rejected: %s", msg.Identifier) + s.Log.Debug("subscription rejected", "channel", msg.Identifier) } s.smu.Unlock() @@ -409,7 +409,7 @@ func (n *Node) Subscribe(s *Session, msg *common.Message) (res *common.CommandRe if confirmed { if s.IsResumeable() { if berr := n.broker.CommitSession(s.GetID(), s); berr != nil { - s.Log.Errorf("Failed to persist session in cache: %v", berr) + s.Log.Error("failed to persist session in cache", "error", berr.Error()) } } @@ -427,7 +427,7 @@ func (n *Node) Unsubscribe(s *Session, msg *common.Message) (res *common.Command if ok := s.subscriptions.HasChannel(msg.Identifier); !ok { s.smu.Unlock() - err = fmt.Errorf("Unknown subscription %s", msg.Identifier) + err = fmt.Errorf("unknown subscription %s", msg.Identifier) return } @@ -435,7 +435,7 @@ func (n *Node) Unsubscribe(s *Session, msg *common.Message) (res *common.Command if err != nil { if res == nil || res.Status == common.ERROR { - s.Log.Errorf("Unsubscribe error: %v", err) + s.Log.Error("failed to unsubscribe", "error", err.Error()) } } else { // Make sure to remove all streams subscriptions @@ -443,7 +443,7 @@ func (n *Node) Unsubscribe(s *Session, msg *common.Message) (res *common.Command s.subscriptions.RemoveChannel(msg.Identifier) - s.Log.Debugf("Unsubscribed from channel: %s", msg.Identifier) + s.Log.Debug("unsubscribed", "channel", msg.Identifier) } s.smu.Unlock() @@ -454,7 +454,7 @@ func (n *Node) Unsubscribe(s *Session, msg *common.Message) (res *common.Command if s.IsResumeable() { if berr := n.broker.CommitSession(s.GetID(), s); berr != nil { - s.Log.Errorf("Failed to persist session in cache: %v", berr) + s.Log.Error("failed to persist session in cache", "error", berr.Error()) } } @@ -467,7 +467,7 @@ func (n *Node) Perform(s *Session, msg *common.Message) (res *common.CommandResu if ok := s.subscriptions.HasChannel(msg.Identifier); !ok { s.smu.Unlock() - err = fmt.Errorf("Unknown subscription %s", msg.Identifier) + err = fmt.Errorf("unknown subscription %s", msg.Identifier) return } @@ -476,7 +476,7 @@ func (n *Node) Perform(s *Session, msg *common.Message) (res *common.CommandResu data, ok := msg.Data.(string) if !ok { - err = fmt.Errorf("Perform data must be a string, got %v", msg.Data) + err = fmt.Errorf("perform data must be a string, got %v", msg.Data) return } @@ -484,17 +484,17 @@ func (n *Node) Perform(s *Session, msg *common.Message) (res *common.CommandResu if err != nil { if res == nil || res.Status == common.ERROR { - s.Log.Errorf("Perform error: %v", err) + s.Log.Error("perform failed", "error", err.Error()) } } else { - s.Log.Debugf("Perform result: %v", res) + s.Log.Debug("perform result", "data", res) } if res != nil { if n.handleCommandReply(s, msg, res) { if s.IsResumeable() { if berr := n.broker.CommitSession(s.GetID(), s); berr != nil { - s.Log.Errorf("Failed to persist session in cache: %v", berr) + s.Log.Error("failed to persist session in cache", "error", berr.Error()) } } } @@ -509,7 +509,7 @@ func (n *Node) History(s *Session, msg *common.Message) (err error) { if ok := s.subscriptions.HasChannel(msg.Identifier); !ok { s.smu.Unlock() - err = fmt.Errorf("Unknown subscription %s", msg.Identifier) + err = fmt.Errorf("unknown subscription %s", msg.Identifier) return } @@ -520,7 +520,7 @@ func (n *Node) History(s *Session, msg *common.Message) (err error) { history := msg.History if history.Since == 0 && history.Streams == nil { - err = fmt.Errorf("History request is missing, got %v", msg) + err = fmt.Errorf("history request is missing, got %v", msg) return } @@ -584,7 +584,7 @@ func (n *Node) retreiveHistory(history *common.HistoryRequest, streams []string) // Broadcast message to stream (locally) func (n *Node) Broadcast(msg *common.StreamMessage) { n.metrics.CounterIncrement(metricsBroadcastMsg) - n.log.Debugf("Incoming broadcast message: %v", msg) + n.log.Debug("incoming broadcast message", "data", msg) n.hub.BroadcastMessage(msg) } @@ -592,13 +592,13 @@ func (n *Node) Broadcast(msg *common.StreamMessage) { func (n *Node) ExecuteRemoteCommand(msg *common.RemoteCommandMessage) { // TODO: Add remote commands metrics // n.metrics.CounterIncrement(metricsRemoteCommandsMsg) - n.log.Debugf("Incoming remote command: %v", msg) + n.log.Debug("incoming remote command", "data", msg) switch msg.Command { // nolint:gocritic case "disconnect": dmsg, err := msg.ToRemoteDisconnectMessage() if err != nil { - n.log.Warnf("Failed to parse remote disconnect command: %v", err) + n.log.Warn("failed to parse remote disconnect command", "error", err.Error()) return } @@ -633,7 +633,7 @@ func (n *Node) DisconnectNow(s *Session) error { ids := s.GetIdentifiers() - s.Log.Debugf("Disconnect %s %s %v %v", ids, s.env.URL, s.env.Headers, sessionSubscriptions) + s.Log.Debug("disconnect", "ids", ids, "url", s.env.URL, "hedears", s.env.Headers, "subscriptions", sessionSubscriptions) err := n.controller.Disconnect( s.GetID(), @@ -643,7 +643,7 @@ func (n *Node) DisconnectNow(s *Session) error { ) if err != nil { - s.Log.Errorf("Disconnect error: %v", err) + s.Log.Error("disconnect failed", "error", err.Error()) } return err @@ -652,7 +652,7 @@ func (n *Node) DisconnectNow(s *Session) error { // RemoteDisconnect find a session by identifier and closes it func (n *Node) RemoteDisconnect(msg *common.RemoteDisconnectMessage) { n.metrics.CounterIncrement(metricsBroadcastMsg) - n.log.Debugf("Incoming pubsub command: %v", msg) + n.log.Debug("incoming pubsub command", "data", msg) n.hub.RemoteDisconnect(msg) } @@ -804,9 +804,9 @@ func (n *Node) disconnectAll(ctx context.Context) { select { case <-ctx.Done(): - n.log.Warnf("Terminated while disconnecting active sessions: %d", n.hub.Size()) + n.log.Warn("terminated while disconnecting active sessions", "num", n.hub.Size()) case <-done: - n.log.Info("All active connections closed") + n.log.Info("all active connections closed") } } diff --git a/node/node_mocks_test.go b/node/node_mocks_test.go index 1395dda2..719a57a2 100644 --- a/node/node_mocks_test.go +++ b/node/node_mocks_test.go @@ -1,6 +1,8 @@ package node import ( + "log/slog" + "github.com/anycable/anycable-go/broker" "github.com/anycable/anycable-go/common" "github.com/anycable/anycable-go/encoders" @@ -8,8 +10,6 @@ import ( "github.com/anycable/anycable-go/mocks" "github.com/anycable/anycable-go/pubsub" "github.com/anycable/anycable-go/ws" - - "github.com/apex/log" ) // NewMockNode build new node with mock controller @@ -31,7 +31,7 @@ func NewMockSession(uid string, node *Node, opts ...SessionOption) *Session { executor: node, closed: true, uid: uid, - Log: log.WithField("sid", uid), + Log: slog.With("sid", uid), subscriptions: NewSubscriptionState(), env: common.NewSessionEnv("/cable-test", &map[string]string{}), sendCh: make(chan *ws.SentFrame, 256), diff --git a/node/session.go b/node/session.go index ecc608e3..6b93add3 100644 --- a/node/session.go +++ b/node/session.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "math/rand" "sync" "time" @@ -12,7 +13,6 @@ import ( "github.com/anycable/anycable-go/encoders" "github.com/anycable/anycable-go/metrics" "github.com/anycable/anycable-go/ws" - "github.com/apex/log" ) const ( @@ -62,7 +62,7 @@ type Session struct { Connected bool // Could be used to store arbitrary data within a session InternalState map[string]interface{} - Log *log.Entry + Log *slog.Logger } type SessionOption = func(*Session) @@ -152,9 +152,7 @@ func NewSession(node *Node, conn Connection, url string, headers *map[string]str session.uid = uid - ctx := node.log.WithFields(log.Fields{ - "sid": session.uid, - }) + ctx := node.log.With("sid", session.uid) session.Log = ctx @@ -213,7 +211,7 @@ func (s *Session) maybeDisconnectIdle() { s.mu.Unlock() - s.Log.Warnf("Disconnecting idle session") + s.Log.Warn("disconnecting idle session") s.Send(common.NewDisconnectMessage(common.IDLE_TIMEOUT_REASON, false)) s.Disconnect("Idle Timeout", ws.CloseNormalClosure) @@ -307,10 +305,10 @@ func (s *Session) Serve(callback func()) error { if err != nil { if ws.IsCloseError(err) { - s.Log.Debugf("WebSocket closed: %v", err) + s.Log.Debug("WebSocket closed", "error", err.Error()) s.disconnectNow("Read closed", ws.CloseNormalClosure) } else { - s.Log.Debugf("WebSocket close error: %v", err) + s.Log.Debug("WebSocket close error", "error", err.Error()) s.disconnectNow("Read failed", ws.CloseAbnormalClosure) } return @@ -319,7 +317,7 @@ func (s *Session) Serve(callback func()) error { err = s.ReadMessage(message) if err != nil { - s.Log.Debugf("WebSocket read failed: %v", err) + s.Log.Debug("WebSocket read failed", "error", err.Error()) return } } @@ -367,7 +365,7 @@ func (s *Session) ReadMessage(message []byte) error { if err := s.executor.HandleCommand(s, command); err != nil { s.metrics.CounterIncrement(metricsFailedCommandReceived) - s.Log.Warnf("Failed to handle incoming message '%s' with error: %v", message, err) + s.Log.Warn("failed to handle incoming message", "data", message, "error", err.Error()) } return nil @@ -380,7 +378,7 @@ func (s *Session) Send(msg encoders.EncodedMessage) { s.sendFrame(b) } } else { - s.Log.Warnf("Failed to encode message %v. Error: %v", msg, err) + s.Log.Warn("failed to encode message", "data", msg, "error", err.Error()) } } @@ -392,7 +390,7 @@ func (s *Session) SendJSONTransmission(msg string) { s.sendFrame(b) } } else { - s.Log.Warnf("Failed to encode transmission %v. Error: %v", msg, err) + s.Log.Warn("failed to encode transmission", "data", msg, "error", err.Error()) } } @@ -602,10 +600,10 @@ func (s *Session) writeFrameWithDeadline(message *ws.SentFrame, deadline time.Ti return err case ws.CloseFrame: s.conn.Close(message.CloseCode, message.CloseReason) - return errors.New("Closed") + return errors.New("closed") default: - s.Log.Errorf("Unknown frame type: %v", message) - return errors.New("Unknown frame type") + s.Log.Error("unknown frame type", "msg", message) + return errors.New("unknown frame type") } } @@ -622,7 +620,7 @@ func (s *Session) sendPing() { b, err := s.encodeMessage(newPingMessage(s.pingTimestampPrecision)) if err != nil { - s.Log.Errorf("Failed to encode ping message: %v", err) + s.Log.Error("failed to encode ping message", "error", err.Error()) } else if b != nil { err = s.writeFrameWithDeadline(b, deadline) } @@ -679,7 +677,7 @@ func (s *Session) handlePong(msg *common.Message) { defer s.mu.Unlock() if s.pongTimer == nil { - s.Log.Debugf("Unexpected PONG received") + s.Log.Debug("unexpected PONG received") return } @@ -696,7 +694,7 @@ func (s *Session) handleNoPong() { s.mu.Unlock() - s.Log.Warnf("Disconnecting session due to no pongs") + s.Log.Warn("disconnecting session due to no pongs") s.Send(common.NewDisconnectMessage(common.NO_PONG_REASON, true)) // nolint:errcheck s.Disconnect("No Pong", ws.CloseNormalClosure) diff --git a/pubsub/nats.go b/pubsub/nats.go index 144632b7..b121061d 100644 --- a/pubsub/nats.go +++ b/pubsub/nats.go @@ -2,13 +2,14 @@ package pubsub import ( "context" + "fmt" + "log/slog" "sync" "github.com/anycable/anycable-go/common" nconfig "github.com/anycable/anycable-go/nats" "github.com/anycable/anycable-go/utils" - "github.com/apex/log" "github.com/nats-io/nats.go" ) @@ -21,7 +22,7 @@ type NATSSubscriber struct { subscriptions map[string]*nats.Subscription subMu sync.RWMutex - log *log.Entry + log *slog.Logger } var _ Subscriber = (*NATSSubscriber)(nil) @@ -32,7 +33,7 @@ func NewNATSSubscriber(node Handler, config *nconfig.NATSConfig) (*NATSSubscribe node: node, config: config, subscriptions: make(map[string]*nats.Subscription), - log: log.WithField("context", "pubsub"), + log: slog.With("context", "pubsub"), }, nil } @@ -42,11 +43,11 @@ func (s *NATSSubscriber) Start(done chan (error)) error { nats.MaxReconnects(s.config.MaxReconnectAttempts), nats.DisconnectErrHandler(func(nc *nats.Conn, err error) { if err != nil { - s.log.Warnf("Connection failed: %v", err) + s.log.Warn("connection failed", "error", err) } }), nats.ReconnectHandler(func(nc *nats.Conn) { - s.log.Infof("Connection restored: %s", nc.ConnectedUrl()) + s.log.Info("connection restored", "url", nc.ConnectedUrl()) }), } @@ -60,7 +61,7 @@ func (s *NATSSubscriber) Start(done chan (error)) error { return err } - s.log.Infof("Starting NATS pub/sub: %s", s.config.Servers) + s.log.Info(fmt.Sprintf("Starting NATS pub/sub: %s", s.config.Servers)) s.conn = nc @@ -96,7 +97,7 @@ func (s *NATSSubscriber) Subscribe(stream string) { sub, err := s.conn.Subscribe(stream, s.handleMessage) if err != nil { - s.log.Errorf("Failed to subscribe to %s: %v", stream, err) + s.log.Error("failed to subscribe", "stream", stream, "error", err.Error()) return } @@ -122,20 +123,20 @@ func (s *NATSSubscriber) BroadcastCommand(cmd *common.RemoteCommandMessage) { } func (s *NATSSubscriber) Publish(stream string, msg interface{}) { - s.log.WithField("channel", stream).Debugf("Publish message: %v", msg) + s.log.With("channel", stream).Debug("publish message", "data", msg) if err := s.conn.Publish(stream, utils.ToJSON(msg)); err != nil { - s.log.Errorf("Failed to publish message: %v", err) + s.log.Error("failed to publish message", "error", err.Error()) } } func (s *NATSSubscriber) handleMessage(m *nats.Msg) { - s.log.WithField("channel", m.Subject).Debugf("Received message: %v", m.Data) + s.log.With("channel", m.Subject).Debug("received message", "data", m.Data) msg, err := common.PubSubMessageFromJSON(m.Data) if err != nil { - s.log.Warnf("Failed to parse pubsub message '%s' with error: %v", m.Data, err) + s.log.Warn("failed to parse pubsub message", "data", m.Data, "error", err) return } diff --git a/pubsub/redis.go b/pubsub/redis.go index 2bcbf6c6..7161e966 100644 --- a/pubsub/redis.go +++ b/pubsub/redis.go @@ -3,13 +3,14 @@ package pubsub import ( "context" "errors" + "fmt" + "log/slog" "sync" "time" "github.com/anycable/anycable-go/common" rconfig "github.com/anycable/anycable-go/redis" "github.com/anycable/anycable-go/utils" - "github.com/apex/log" "github.com/redis/rueidis" ) @@ -41,7 +42,7 @@ type RedisSubscriber struct { streamsCh chan (*subscriptionEntry) shutdownCh chan struct{} - log *log.Entry + log *slog.Logger } var _ Subscriber = (*RedisSubscriber)(nil) @@ -59,7 +60,7 @@ func NewRedisSubscriber(node Handler, config *rconfig.RedisConfig) (*RedisSubscr config: config, clientOptions: options, subscriptions: make(map[string]*subscriptionEntry), - log: log.WithField("context", "pubsub"), + log: slog.With("context", "pubsub"), streamsCh: make(chan *subscriptionEntry, 1024), shutdownCh: make(chan struct{}), }, nil @@ -67,11 +68,11 @@ func NewRedisSubscriber(node Handler, config *rconfig.RedisConfig) (*RedisSubscr func (s *RedisSubscriber) Start(done chan (error)) error { if s.config.IsSentinel() { //nolint:gocritic - s.log.Infof("Starting Redis pub/sub (sentinels): %v", s.config.Hostnames()) + s.log.Info(fmt.Sprintf("Starting Redis pub/sub (sentinels): %v", s.config.Hostnames())) } else if s.config.IsCluster() { - s.log.Infof("Starting Redis pub/sub (cluster): %v", s.config.Hostnames()) + s.log.Info(fmt.Sprintf("Starting Redis pub/sub (cluster): %v", s.config.Hostnames())) } else { - s.log.Infof("Starting Redis pub/sub: %s", s.config.Hostname()) + s.log.Info(fmt.Sprintf("Starting Redis pub/sub: %s", s.config.Hostname())) } go s.runPubSub(done) @@ -89,7 +90,7 @@ func (s *RedisSubscriber) Shutdown(ctx context.Context) error { return nil } - s.log.Debugf("Shutting down Redis pub/sub") + s.log.Debug("shutting down Redis pub/sub") // First, shutdown the pub/sub routine close(s.shutdownCh) @@ -146,7 +147,7 @@ func (s *RedisSubscriber) Publish(stream string, msg interface{}) { s.clientMu.RUnlock() - s.log.WithField("channel", stream).Debugf("Publish message: %v", msg) + s.log.With("channel", stream).Debug("publish message", "data", msg) client.Do(ctx, client.B().Publish().Channel(stream).Message(string(utils.ToJSON(msg))).Build()) } @@ -174,7 +175,7 @@ func (s *RedisSubscriber) runPubSub(done chan (error)) { err := s.initClient() if err != nil { - s.log.Errorf("Failed to connect to Redis: %v", err) + s.log.Error("failed to connect to Redis", "error", err.Error()) s.maybeReconnect(done) return } @@ -187,34 +188,34 @@ func (s *RedisSubscriber) runPubSub(done chan (error)) { s.subMu.Lock() defer s.subMu.Unlock() - s.log.Debugf("Subscription message: %v", m) + s.log.Debug("subscription message", "data", m) if m.Kind == "subscribe" && m.Channel == s.config.InternalChannel { if s.reconnectAttempt > 0 { - s.log.Info("Reconnected to Redis") + s.log.Info("reconnected to Redis") } s.reconnectAttempt = 0 } if entry, ok := s.subscriptions[m.Channel]; ok { if entry.state == subscriptionPending && m.Kind == "subscribe" { - s.log.WithField("channel", m.Channel).Debugf("Subscribed") + s.log.With("channel", m.Channel).Debug("subscribed") entry.state = subscriptionCreated } if entry.state == subscriptionPendingUnsubscribe && m.Kind == "unsubscribe" { - s.log.WithField("channel", m.Channel).Debugf("Unsubscribed") + s.log.With("channel", m.Channel).Debug("unsubscribed") delete(s.subscriptions, entry.id) } } }, OnMessage: func(m rueidis.PubSubMessage) { - s.log.WithField("channel", m.Channel).Debugf("Received message: %v", m.Message) + s.log.With("channel", m.Channel).Debug("received message", "data", m.Message) msg, err := common.PubSubMessageFromJSON([]byte(m.Message)) if err != nil { - s.log.Warnf("Failed to parse pubsub message '%s' with error: %v", m.Message, err) + s.log.Warn("failed to parse pubsub message", "data", m.Message, "error", err.Error()) return } @@ -231,24 +232,24 @@ func (s *RedisSubscriber) runPubSub(done chan (error)) { select { case err := <-wait: if err != nil { - s.log.Errorf("Redis pub/sub disconnected: %v", err) + s.log.Error("Redis pub/sub disconnected", "error", err.Error()) } s.maybeReconnect(done) return case <-s.shutdownCh: - s.log.Debugf("Close pub/sub channel") + s.log.Debug("close pub/sub channel") return case entry := <-s.streamsCh: ctx := context.Background() switch entry.state { case subscriptionPending: - s.log.WithField("channel", entry.id).Debugf("Subscribing") + s.log.With("channel", entry.id).Debug("subscribing") client.Do(ctx, client.B().Subscribe().Channel(entry.id).Build()) case subscriptionPendingUnsubscribe: - s.log.WithField("channel", entry.id).Debugf("Unsubscribing") + s.log.With("channel", entry.id).Debug("unsubscribing") client.Do(ctx, client.B().Unsubscribe().Channel(entry.id).Build()) } } @@ -302,10 +303,10 @@ func (s *RedisSubscriber) maybeReconnect(done chan (error)) { delay := utils.NextRetry(s.reconnectAttempt - 1) - s.log.Infof("Next Redis reconnect attempt in %s", delay) + s.log.Info(fmt.Sprintf("next Redis reconnect attempt in %s", delay)) time.Sleep(delay) - s.log.Infof("Reconnecting to Redis...") + s.log.Info("reconnecting to Redis...") go s.runPubSub(done) @@ -314,7 +315,7 @@ func (s *RedisSubscriber) maybeReconnect(done chan (error)) { for _, sub := range s.subscriptions { if sub.state == subscriptionPending { - s.log.Debugf("Resubscribing to stream: %s", sub.id) + s.log.Debug("resubscribing to stream", "stream", sub.id) s.streamsCh <- sub } } diff --git a/rails/cable_ready.go b/rails/cable_ready.go index d60414e2..341ef186 100644 --- a/rails/cable_ready.go +++ b/rails/cable_ready.go @@ -2,8 +2,7 @@ package rails import ( "encoding/json" - - "github.com/apex/log" + "log/slog" "github.com/anycable/anycable-go/common" "github.com/anycable/anycable-go/node" @@ -12,7 +11,7 @@ import ( type CableReadyController struct { verifier *utils.MessageVerifier - log *log.Entry + log *slog.Logger } var _ node.Controller = (*CableReadyController)(nil) @@ -24,7 +23,7 @@ func NewCableReadyController(key string) *CableReadyController { verifier = utils.NewMessageVerifier(key) } - return &CableReadyController{verifier, log.WithField("context", "cable_ready")} + return &CableReadyController{verifier, slog.With("context", "cable_ready")} } func (c *CableReadyController) Start() error { @@ -47,7 +46,7 @@ func (c *CableReadyController) Subscribe(sid string, env *common.SessionEnv, id err := json.Unmarshal([]byte(channel), ¶ms) if err != nil { - c.log.WithField("identifier", channel).Warnf("invalid identifier: %v", err) + c.log.With("identifier", channel).Warn("invalid identifier", "error", err) return nil, err } @@ -56,12 +55,12 @@ func (c *CableReadyController) Subscribe(sid string, env *common.SessionEnv, id if c.IsCleartext() { stream = params.SignedStreamID - c.log.WithField("identifier", channel).Debugf("unsigned stream: %s", stream) + c.log.With("identifier", channel).Debug("unsigned", "stream", stream) } else { verified, err := c.verifier.Verified(params.SignedStreamID) if err != nil { - c.log.WithField("identifier", channel).Debugf("verification failed for %s: %v", params.SignedStreamID, err) + c.log.With("identifier", channel).Debug("verification failed", "stream", params.SignedStreamID, "error", err) return &common.CommandResult{ Status: common.FAILURE, @@ -75,7 +74,7 @@ func (c *CableReadyController) Subscribe(sid string, env *common.SessionEnv, id stream, ok = verified.(string) if !ok { - c.log.WithField("identifier", channel).Debugf("verification failed: stream name is not a string: %v", verified) + c.log.With("identifier", channel).Debug("verification failed: stream name is not a string", "stream", verified) return &common.CommandResult{ Status: common.FAILURE, @@ -84,7 +83,7 @@ func (c *CableReadyController) Subscribe(sid string, env *common.SessionEnv, id nil } - c.log.WithField("identifier", channel).Debugf("verified stream: %s", stream) + c.log.With("identifier", channel).Debug("verified", "stream", stream) } return &common.CommandResult{ diff --git a/rails/turbo.go b/rails/turbo.go index 8920a1e9..e31812e9 100644 --- a/rails/turbo.go +++ b/rails/turbo.go @@ -2,8 +2,7 @@ package rails import ( "encoding/json" - - "github.com/apex/log" + "log/slog" "github.com/anycable/anycable-go/common" "github.com/anycable/anycable-go/node" @@ -12,7 +11,7 @@ import ( type TurboController struct { verifier *utils.MessageVerifier - log *log.Entry + log *slog.Logger } var _ node.Controller = (*TurboController)(nil) @@ -24,7 +23,7 @@ func NewTurboController(key string) *TurboController { verifier = utils.NewMessageVerifier(key) } - return &TurboController{verifier, log.WithField("context", "turbo")} + return &TurboController{verifier, slog.With("context", "turbo")} } func (c *TurboController) Start() error { @@ -47,7 +46,7 @@ func (c *TurboController) Subscribe(sid string, env *common.SessionEnv, id strin err := json.Unmarshal([]byte(channel), ¶ms) if err != nil { - c.log.WithField("identifier", channel).Warnf("invalid identifier: %v", err) + c.log.With("identifier", channel).Warn("invalid identifier", "error", err) return nil, err } @@ -56,12 +55,12 @@ func (c *TurboController) Subscribe(sid string, env *common.SessionEnv, id strin if c.IsCleartext() { stream = params.SignedStreamID - c.log.WithField("identifier", channel).Debugf("unsigned stream: %s", stream) + c.log.With("identifier", channel).Debug("unsigned", "stream", stream) } else { verified, err := c.verifier.Verified(params.SignedStreamID) if err != nil { - c.log.WithField("identifier", channel).Debugf("verification failed for %s: %v", params.SignedStreamID, err) + c.log.With("identifier", channel).Debug("verification failed", "stream", params.SignedStreamID, "error", err) return &common.CommandResult{ Status: common.FAILURE, @@ -75,7 +74,7 @@ func (c *TurboController) Subscribe(sid string, env *common.SessionEnv, id strin stream, ok = verified.(string) if !ok { - c.log.WithField("identifier", channel).Debugf("verification failed: stream name is not a string: %v", verified) + c.log.With("identifier", channel).Debug("verification failed: stream name is not a string", "stream", verified) return &common.CommandResult{ Status: common.FAILURE, @@ -84,7 +83,7 @@ func (c *TurboController) Subscribe(sid string, env *common.SessionEnv, id strin nil } - c.log.WithField("identifier", channel).Debugf("verified stream: %s", stream) + c.log.With("identifier", channel).Debug("verified", "stream", stream) } return &common.CommandResult{ diff --git a/rpc/rpc.go b/rpc/rpc.go index cb6ce9f9..af96bab3 100644 --- a/rpc/rpc.go +++ b/rpc/rpc.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "log/slog" "math" "sync" "sync/atomic" @@ -12,7 +13,6 @@ import ( "github.com/anycable/anycable-go/common" "github.com/anycable/anycable-go/metrics" "github.com/anycable/anycable-go/protocol" - "github.com/apex/log" pb "github.com/anycable/anycable-go/protos" "google.golang.org/grpc" @@ -98,12 +98,12 @@ func (st *grpcClientHelper) HandleConn(ctx context.Context, stat stats.ConnStats } if _, ok := stat.(*stats.ConnBegin); ok { - log.WithField("context", "grpc").Debugf("connected to %s", addr) + slog.With("context", "grpc").Debug("connected", "addr", addr) atomic.AddInt64(&st.active, 1) } if _, ok := stat.(*stats.ConnEnd); ok { - log.WithField("context", "grpc").Debugf("disconnected from %s", addr) + slog.With("context", "grpc").Debug("disconnected", "addr", addr) atomic.AddInt64(&st.active, -1) } } @@ -131,7 +131,7 @@ func (st *grpcClientHelper) tryRecover() error { st.recovering = true st.conn.ResetConnectBackoff() - log.WithField("context", "rpc").Warn("Connection is lost. Trying to reconnect immediately") + slog.With("context", "rpc").Warn("connection is lost, trying to reconnect immediately") return nil } @@ -142,7 +142,7 @@ func (st *grpcClientHelper) reset() { if st.recovering { st.recovering = false - log.WithField("context", "rpc").Info("Connection is restored") + slog.With("context", "rpc").Info("connection is restored") } } @@ -152,7 +152,7 @@ type Controller struct { barrier Barrier client pb.RPCClient metrics metrics.Instrumenter - log *log.Entry + log *slog.Logger clientState ClientHelper timerMu sync.Mutex @@ -182,7 +182,7 @@ func NewController(metrics metrics.Instrumenter, config *Config) (*Controller, e metrics.RegisterGauge(metricsGRPCActiveConns, "The number of active HTTP connections used by gRPC") } - return &Controller{log: log.WithField("context", "rpc"), metrics: metrics, config: config, barrier: barrier}, nil + return &Controller{log: slog.With("context", "rpc"), metrics: metrics, config: config, barrier: barrier}, nil } // Start initializes RPC connection pool @@ -211,7 +211,7 @@ func (c *Controller) Start() error { client, state, err := dialer(c.config) if err == nil { - c.log.Infof("RPC controller initialized: %s (concurrency: %s, impl: %s, enable_tls: %t, proto_versions: %s)", host, c.barrier.CapacityInfo(), impl, enableTLS, ProtoVersions) + c.log.Info(fmt.Sprintf("RPC controller initialized: %s (concurrency: %s, impl: %s, enable_tls: %t, proto_versions: %s)", host, c.barrier.CapacityInfo(), impl, enableTLS, ProtoVersions)) } else { return err } @@ -245,7 +245,7 @@ func (c *Controller) Shutdown() error { busy := c.busy() if busy > 0 { - c.log.Infof("Waiting for active RPC calls to finish: %d", busy) + c.log.Info("waiting for active RPC calls to finish", "num", busy) } // Wait for active connections @@ -253,10 +253,10 @@ func (c *Controller) Shutdown() error { busy := c.busy() if busy > 0 { - return false, fmt.Errorf("Terminated while completing active RPC calls: %d", busy) + return false, fmt.Errorf("terminated while completing active RPC calls: %d", busy) } - c.log.Info("All active RPC calls finished") + c.log.Info("all active RPC calls finished") return true, nil }) @@ -292,7 +292,7 @@ func (c *Controller) Authenticate(sid string, env *common.SessionEnv) (*common.C if r, ok := response.(*pb.ConnectionResponse); ok { - c.log.WithField("sid", sid).Debugf("Authenticate response: %v", r) + c.log.With("sid", sid).Debug("authenticate response", "data", r) reply, err := protocol.ParseConnectResponse(r) @@ -389,7 +389,7 @@ func (c *Controller) Disconnect(sid string, env *common.SessionEnv, id string, s } if r, ok := response.(*pb.DisconnectResponse); ok { - c.log.WithField("sid", sid).Debugf("Disconnect response: %v", r) + c.log.With("sid", sid).Debug("Disconnect response", "data", r) err = protocol.ParseDisconnectResponse(r) @@ -413,7 +413,7 @@ func (c *Controller) parseCommandResponse(sid string, response interface{}, err } if r, ok := response.(*pb.CommandResponse); ok { - c.log.WithField("sid", sid).Debugf("Command response: %v", r) + c.log.With("sid", sid).Debug("command response", "data", r) res, err := protocol.ParseCommandResponse(r) @@ -460,7 +460,7 @@ func (c *Controller) retry(sid string, callback func() (interface{}, error)) (re return nil, err } - c.log.WithFields(log.Fields{"sid": sid, "code": st.Code()}).Debugf("RPC failure: %v", st.Message()) + c.log.With("sid", sid).Debug("RPC failed", "code", st.Code(), "error", st.Message()) interval := retryUnavailableInterval diff --git a/server/request_info.go b/server/request_info.go index 87a22f45..7d3c79e1 100644 --- a/server/request_info.go +++ b/server/request_info.go @@ -43,7 +43,7 @@ func NewRequestInfo(r *http.Request, extractor HeadersExtractor) (*RequestInfo, uid, err := FetchUID(r) if err != nil { - return nil, errors.New("Failed to retrieve connection uid") + return nil, errors.New("failed to retrieve connection uid") } url := r.URL.String() diff --git a/server/server.go b/server/server.go index 27f4554d..317dbc57 100644 --- a/server/server.go +++ b/server/server.go @@ -3,15 +3,15 @@ package server import ( "context" "crypto/tls" - "errors" "fmt" + "log/slog" "net" "net/http" "sync" "time" - "github.com/apex/log" "github.com/go-chi/chi/v5" + "github.com/joomcode/errorx" "golang.org/x/net/netutil" ) @@ -24,7 +24,7 @@ type HTTPServer struct { started bool maxConn int mu sync.Mutex - log *log.Entry + log *slog.Logger shutdownCtx context.Context shutdownFn context.CancelFunc @@ -71,8 +71,7 @@ func NewServer(host string, port string, ssl *SSLConfig, maxConn int) (*HTTPServ if secured { cer, err := tls.LoadX509KeyPair(ssl.CertPath, ssl.KeyPath) if err != nil { - msg := fmt.Sprintf("Failed to load SSL certificate: %s.", err) - return nil, errors.New(msg) + return nil, errorx.Decorate(err, "failed to load SSL certificate") } server.TLSConfig = &tls.Config{Certificates: []tls.Certificate{cer}, MinVersion: tls.VersionTLS12} @@ -90,7 +89,7 @@ func NewServer(host string, port string, ssl *SSLConfig, maxConn int) (*HTTPServ shutdownCtx: shutdownCtx, shutdownFn: shutdownFn, maxConn: maxConn, - log: log.WithField("context", "http"), + log: slog.With("context", "http"), }, nil } @@ -127,11 +126,11 @@ func (s *HTTPServer) StartAndAnnounce(name string) error { s.mu.Lock() if s.Running() { s.mu.Unlock() - s.log.Debugf("%s is mounted at %s", name, s.Address()) + s.log.Debug("HTTP server has been already started", "name", name, "addr", s.Address()) return nil } - s.log.Debugf("Starting %s at %s", name, s.Address()) + s.log.Debug("starting HTTP server", "name", name, "addr", s.Address()) s.mu.Unlock() return s.Start() diff --git a/sse/handler.go b/sse/handler.go index e16e8258..45326434 100644 --- a/sse/handler.go +++ b/sse/handler.go @@ -2,6 +2,7 @@ package sse import ( "context" + "log/slog" "net/http" "strings" @@ -10,7 +11,6 @@ import ( "github.com/anycable/anycable-go/server" "github.com/anycable/anycable-go/version" "github.com/anycable/anycable-go/ws" - "github.com/apex/log" ) // SSEHandler generates a new http handler for SSE connections @@ -65,12 +65,12 @@ func SSEHandler(n *node.Node, shutdownCtx context.Context, headersExtractor serv return } - sessionCtx := log.WithField("sid", info.UID).WithField("transport", "sse") + sessionCtx := slog.With("sid", info.UID).With("transport", "sse") subscribeCmds, err := subscribeCommandsFromRequest(r) if err != nil { - sessionCtx.Errorf("failed to build subscribe command: %v", err) + sessionCtx.Error("failed to build subscribe command", "error", err.Error()) w.WriteHeader(http.StatusBadRequest) return } @@ -79,7 +79,7 @@ func SSEHandler(n *node.Node, shutdownCtx context.Context, headersExtractor serv session, err := NewSSESession(n, w, r, info) if err != nil { - sessionCtx.Errorf("failed to establish sesssion: %v", err) + sessionCtx.Error("failed to establish sesssion", "error", err.Error()) w.WriteHeader(http.StatusBadRequest) return } @@ -100,14 +100,14 @@ func SSEHandler(n *node.Node, shutdownCtx context.Context, headersExtractor serv res, err := n.Subscribe(session, subscribeCmd) if err != nil || res == nil { - sessionCtx.Errorf("failed to subscribe: %v", err) + sessionCtx.Error("failed to subscribe", "error", err.Error()) w.WriteHeader(http.StatusBadRequest) return } // Subscription rejected if res.Status != common.SUCCESS { - sessionCtx.Debugf("rejected: %v", err) + sessionCtx.Debug("subscription rejected") w.WriteHeader(http.StatusBadRequest) return } @@ -117,7 +117,7 @@ func SSEHandler(n *node.Node, shutdownCtx context.Context, headersExtractor serv flusher.Flush() conn.Established() - sessionCtx.Debugf("session established") + sessionCtx.Debug("session established") shutdownReceived := false @@ -126,18 +126,18 @@ func SSEHandler(n *node.Node, shutdownCtx context.Context, headersExtractor serv case <-shutdownCtx.Done(): if !shutdownReceived { shutdownReceived = true - sessionCtx.Debugf("server shutdown") + sessionCtx.Debug("server shutdown") session.DisconnectWithMessage( &common.DisconnectMessage{Type: "disconnect", Reason: common.SERVER_RESTART_REASON, Reconnect: true}, common.SERVER_RESTART_REASON, ) } case <-r.Context().Done(): - sessionCtx.Debugf("request terminated") + sessionCtx.Debug("request terminated") session.DisconnectNow("Closed", ws.CloseNormalClosure) return case <-conn.Context().Done(): - sessionCtx.Debugf("session completed") + sessionCtx.Debug("session completed") return } } diff --git a/telemetry/telemetry.go b/telemetry/telemetry.go index 871998f8..7c83d944 100644 --- a/telemetry/telemetry.go +++ b/telemetry/telemetry.go @@ -2,6 +2,7 @@ package telemetry import ( "context" + "log/slog" "maps" "os" "runtime" @@ -12,7 +13,6 @@ import ( "github.com/anycable/anycable-go/metrics" "github.com/anycable/anycable-go/mrb" "github.com/anycable/anycable-go/version" - "github.com/apex/log" "github.com/hofstadter-io/cinful" "github.com/posthog/posthog-go" @@ -62,7 +62,7 @@ func NewTracker(instrumenter *metrics.Metrics, c *config.Config, tc *Config) *Tr } func (t *Tracker) Announce() { - log.WithField("context", "main").Info("Anonymized telemetry is on. Learn more: https://docs.anycable.io/anycable-go/telemetry") + slog.With("context", "main").Info("Anonymized telemetry is on. Learn more: https://docs.anycable.io/anycable-go/telemetry") } func (t *Tracker) Collect() { diff --git a/utils/log_handler.go b/utils/log_handler.go deleted file mode 100644 index 74c0ddd5..00000000 --- a/utils/log_handler.go +++ /dev/null @@ -1,91 +0,0 @@ -package utils - -import ( - "fmt" - "io" - "sync" - "time" - - "github.com/apex/log" -) - -// colors. -const ( - red = 31 - yellow = 33 - blue = 34 - gray = 37 -) - -// Colors mapping. -var Colors = [...]int{ - log.DebugLevel: gray, - log.InfoLevel: blue, - log.WarnLevel: yellow, - log.ErrorLevel: red, - log.FatalLevel: red, -} - -// Strings mapping. -var Strings = [...]string{ - log.DebugLevel: "DEBUG", - log.InfoLevel: "INFO", - log.WarnLevel: "WARN", - log.ErrorLevel: "ERROR", - log.FatalLevel: "FATAL", -} - -// Chars mapping. -var Chars = [...]string{ - log.DebugLevel: "D", - log.InfoLevel: "I", - log.WarnLevel: "W", - log.ErrorLevel: "E", - log.FatalLevel: "F", -} - -// LogHandler with TTY awareness -type LogHandler struct { - mu sync.Mutex - writer io.Writer - tty bool -} - -const ( - timeFormat = "2006-01-02T15:04:05.000Z" -) - -// HandleLog is a method called by logger to record a log entry -func (h *LogHandler) HandleLog(e *log.Entry) error { - names := e.Fields.Names() - ts := time.Now().UTC().Format(timeFormat) - - h.mu.Lock() - defer h.mu.Unlock() - - if h.tty { - color := Colors[e.Level] - level := Strings[e.Level] - - fmt.Fprintf(h.writer, "\033[%dm%6s\033[0m %s", color, level, ts) - - for _, name := range names { - fmt.Fprintf(h.writer, " \033[%dm%s\033[0m=%v", color, name, e.Fields.Get(name)) - } - - fmt.Fprintf(h.writer, " \033[%dm%-25s\033[0m\n", color, e.Message) - - } else { - level := Chars[e.Level] - - fmt.Fprintf(h.writer, "%s %s", level, ts) - - for _, name := range names { - fmt.Fprintf(h.writer, " %s=%v", name, e.Fields.Get(name)) - } - - fmt.Fprintf(h.writer, " %-25s\n", e.Message) - } - - return nil -} diff --git a/utils/logging.go b/utils/logging.go deleted file mode 100644 index 336042c8..00000000 --- a/utils/logging.go +++ /dev/null @@ -1,40 +0,0 @@ -package utils - -import ( - "errors" - "fmt" - "os" - - "github.com/apex/log" - "github.com/apex/log/handlers/json" -) - -// InitLogger sets log level, format and output -func InitLogger(format string, level string) error { - logLevel, err := log.ParseLevel(level) - - if err != nil { - msg := fmt.Sprintf("Unknown log level: %s.\nAvailable levels are: debug, info, warn, error, fatal", level) - return errors.New(msg) - } - - log.SetLevel(logLevel) - - switch format { - case "text": - { - log.SetHandler(&LogHandler{writer: os.Stdout, tty: IsTTY()}) - } - case "json": - { - log.SetHandler(json.New(os.Stdout)) - } - default: - { - msg := fmt.Sprintf("Unknown log format: %s.\nAvaialable formats are: text, json", format) - return errors.New(msg) - } - } - - return nil -} diff --git a/utils/utils.go b/utils/utils.go index 4c40c32e..e1dac5f4 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -2,12 +2,12 @@ package utils import ( "encoding/json" + "fmt" "math" "math/rand" "os" "time" - "github.com/apex/log" "github.com/mattn/go-isatty" ) @@ -19,7 +19,7 @@ func IsTTY() bool { func ToJSON[T any](val T) []byte { jsonStr, err := json.Marshal(&val) if err != nil { - log.Fatalf("😲 Failed to build JSON for %v: %v", val, err) + panic(fmt.Sprintf("😲 Failed to build JSON for %v: %v", val, err)) } return jsonStr } diff --git a/ws/handler.go b/ws/handler.go index 6b977a25..2b66ffd3 100644 --- a/ws/handler.go +++ b/ws/handler.go @@ -1,13 +1,13 @@ package ws import ( + "log/slog" "net/http" "net/url" "strings" "github.com/anycable/anycable-go/server" "github.com/anycable/anycable-go/version" - "github.com/apex/log" "github.com/gorilla/websocket" ) @@ -16,7 +16,7 @@ type sessionHandler = func(conn *websocket.Conn, info *server.RequestInfo, callb // WebsocketHandler generate a new http handler for WebSocket connections func WebsocketHandler(subprotocols []string, headersExtractor server.HeadersExtractor, config *Config, sessionHandler sessionHandler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ctx := log.WithField("context", "ws") + ctx := slog.With("context", "ws") upgrader := websocket.Upgrader{ CheckOrigin: CheckOrigin(config.AllowedOrigins), @@ -29,7 +29,7 @@ func WebsocketHandler(subprotocols []string, headersExtractor server.HeadersExtr rheader := map[string][]string{"X-AnyCable-Version": {version.Version()}} wsc, err := upgrader.Upgrade(w, r, rheader) if err != nil { - ctx.Debugf("Websocket connection upgrade error: %#v", err.Error()) + ctx.Debug("WebSocket connection upgrade failed", "error", err.Error()) return } @@ -45,23 +45,23 @@ func WebsocketHandler(subprotocols []string, headersExtractor server.HeadersExtr wsc.EnableWriteCompression(true) } - sessionCtx := log.WithField("sid", info.UID) + sessionCtx := slog.With("sid", info.UID) clientSubprotocol := r.Header.Get("Sec-Websocket-Protocol") if wsc.Subprotocol() == "" && clientSubprotocol != "" { - sessionCtx.Debugf("No subprotocol negotiated: client wants %v, server supports %v", clientSubprotocol, subprotocols) + sessionCtx.Debug("no subprotocol negotiated", "client", clientSubprotocol, "server", subprotocols) } // Separate goroutine for better GC of caller's data. go func() { - sessionCtx.Debugf("WebSocket session established") + sessionCtx.Debug("WebSocket session established") serr := sessionHandler(wsc, info, func() { - sessionCtx.Debugf("WebSocket session completed") + sessionCtx.Debug("WebSocket session completed") }) if serr != nil { - sessionCtx.Errorf("WebSocket session failed: %v", serr) + sessionCtx.Error("WebSocket session failed", "error", serr) return } }()