From db9f89459132cab016b408ae5438025ff28c2e4c Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Sat, 14 Sep 2024 17:52:29 +0200 Subject: [PATCH 1/3] proxy: Switch to using "go.uber.org/zap" for logging. --- proxy/main.go | 71 +++++-- proxy/proxy_remote.go | 92 ++++++--- proxy/proxy_server.go | 320 ++++++++++++++++++++++++-------- proxy/proxy_server_test.go | 11 +- proxy/proxy_session.go | 36 +++- proxy/proxy_tokens_etcd.go | 28 ++- proxy/proxy_tokens_etcd_test.go | 7 +- proxy/proxy_tokens_static.go | 37 +++- 8 files changed, 450 insertions(+), 152 deletions(-) diff --git a/proxy/main.go b/proxy/main.go index 18f7bf53..083de00c 100644 --- a/proxy/main.go +++ b/proxy/main.go @@ -22,9 +22,9 @@ package main import ( + "errors" "flag" "fmt" - "log" "net" "net/http" "os" @@ -36,6 +36,8 @@ import ( "github.com/dlintw/goconf" "github.com/gorilla/mux" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" signaling "github.com/strukturag/nextcloud-spreed-signaling" ) @@ -56,7 +58,6 @@ const ( ) func main() { - log.SetFlags(log.Lshortfile) flag.Parse() if *showVersion { @@ -69,26 +70,54 @@ func main() { signal.Notify(sigChan, syscall.SIGHUP) signal.Notify(sigChan, syscall.SIGUSR1) - log.Printf("Starting up version %s/%s as pid %d", version, runtime.Version(), os.Getpid()) + fmt.Printf("Starting up version %s/%s as pid %d\n", version, runtime.Version(), os.Getpid()) config, err := goconf.ReadConfigFile(*configFlag) if err != nil { - log.Fatal("Could not read configuration: ", err) + fmt.Printf("Could not read configuration: %s\n", err) + os.Exit(1) } + var logConfig zap.Config + if debug, _ := config.GetBool("app", "debug"); debug { + logConfig = zap.NewDevelopmentConfig() + } else { + logConfig = zap.NewProductionConfig() + logConfig.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder + } + log, err := logConfig.Build( + // Only log stack traces when panicing. + zap.AddStacktrace(zap.DPanicLevel), + ) + if err != nil { + fmt.Printf("Could not create logger: %s\n", err) + os.Exit(1) + } + + restoreGlobalLogs := zap.ReplaceGlobals(log) + defer restoreGlobalLogs() + cpus := runtime.NumCPU() runtime.GOMAXPROCS(cpus) - log.Printf("Using a maximum of %d CPUs", cpus) + log.Debug("Using number of CPUs", + zap.Int("cpus", cpus), + ) r := mux.NewRouter() - proxy, err := NewProxyServer(r, version, config) + proxy, err := NewProxyServer(log, r, version, config) if err != nil { - log.Fatal(err) + log.Fatal("Error creating proxy server", + zap.Error(err), + ) } if err := proxy.Start(config); err != nil { - log.Fatal(err) + if !errors.Is(err, ErrCancelled) { + log.Fatal("Error starting proxy server", + zap.Error(err), + ) + } } defer proxy.Stop() @@ -104,10 +133,13 @@ func main() { for _, address := range strings.Split(addr, " ") { go func(address string) { - log.Println("Listening on", address) + log := log.With(zap.String("addr", address)) + log.Debug("Listening") listener, err := net.Listen("tcp", address) if err != nil { - log.Fatal("Could not start listening: ", err) + log.Fatal("Could not start listening", + zap.Error(err), + ) } srv := &http.Server{ Handler: r, @@ -117,7 +149,9 @@ func main() { WriteTimeout: time.Duration(writeTimeout) * time.Second, } if err := srv.Serve(listener); err != nil { - log.Fatal("Could not start server: ", err) + log.Fatal("Could not start server", + zap.Error(err), + ) } }(address) } @@ -129,21 +163,26 @@ loop: case sig := <-sigChan: switch sig { case os.Interrupt: - log.Println("Interrupted") + log.Debug("Interrupted") break loop case syscall.SIGHUP: - log.Printf("Received SIGHUP, reloading %s", *configFlag) + log.Info("Received SIGHUP, reloading", + zap.String("filename", *configFlag), + ) if config, err := goconf.ReadConfigFile(*configFlag); err != nil { - log.Printf("Could not read configuration from %s: %s", *configFlag, err) + log.Error("Could not read configuration", + zap.String("filename", *configFlag), + zap.Error(err), + ) } else { proxy.Reload(config) } case syscall.SIGUSR1: - log.Printf("Received SIGUSR1, scheduling server to shutdown") + log.Info("Received SIGUSR1, scheduling server to shutdown") proxy.ScheduleShutdown() } case <-proxy.ShutdownChannel(): - log.Printf("All clients disconnected, shutting down") + log.Info("All clients disconnected, shutting down") break loop } } diff --git a/proxy/proxy_remote.go b/proxy/proxy_remote.go index 838ceccb..fb3718a6 100644 --- a/proxy/proxy_remote.go +++ b/proxy/proxy_remote.go @@ -27,7 +27,6 @@ import ( "crypto/tls" "encoding/json" "errors" - "log" "net/http" "net/url" "strconv" @@ -37,6 +36,7 @@ import ( "github.com/golang-jwt/jwt/v4" "github.com/gorilla/websocket" + "go.uber.org/zap" signaling "github.com/strukturag/nextcloud-spreed-signaling" ) @@ -60,6 +60,7 @@ var ( ) type RemoteConnection struct { + log *zap.Logger mu sync.Mutex url *url.URL conn *websocket.Conn @@ -82,13 +83,18 @@ type RemoteConnection struct { messageCallbacks map[string]chan *signaling.ProxyServerMessage } -func NewRemoteConnection(proxyUrl string, tokenId string, tokenKey *rsa.PrivateKey, tlsConfig *tls.Config) (*RemoteConnection, error) { +func NewRemoteConnection(log *zap.Logger, proxyUrl string, tokenId string, tokenKey *rsa.PrivateKey, tlsConfig *tls.Config) (*RemoteConnection, error) { u, err := url.Parse(proxyUrl) if err != nil { return nil, err } + log = log.With( + zap.Stringer("url", u), + ) + result := &RemoteConnection{ + log: log, url: u, closer: signaling.NewCloser(), @@ -114,7 +120,9 @@ func (c *RemoteConnection) String() string { func (c *RemoteConnection) reconnect() { u, err := c.url.Parse("proxy") if err != nil { - log.Printf("Could not resolve url to proxy at %s: %s", c, err) + c.log.Error("Could not resolve url to proxy", + zap.Error(err), + ) c.scheduleReconnect() return } @@ -131,12 +139,14 @@ func (c *RemoteConnection) reconnect() { conn, _, err := dialer.DialContext(context.TODO(), u.String(), nil) if err != nil { - log.Printf("Error connecting to proxy at %s: %s", c, err) + c.log.Error("Error connecting to proxy", + zap.Error(err), + ) c.scheduleReconnect() return } - log.Printf("Connected to %s", c) + c.log.Debug("Connected") c.closed.Store(false) c.mu.Lock() @@ -147,7 +157,9 @@ func (c *RemoteConnection) reconnect() { c.reconnectInterval.Store(int64(initialReconnectInterval)) if err := c.sendHello(); err != nil { - log.Printf("Error sending hello request to proxy at %s: %s", c, err) + c.log.Error("Error sending hello request to proxy", + zap.Error(err), + ) c.scheduleReconnect() return } @@ -161,7 +173,9 @@ func (c *RemoteConnection) reconnect() { func (c *RemoteConnection) scheduleReconnect() { if err := c.sendClose(); err != nil && err != ErrNotConnected { - log.Printf("Could not send close message to %s: %s", c, err) + c.log.Error("Could not send close message", + zap.Error(err), + ) } c.close() @@ -314,19 +328,27 @@ func (c *RemoteConnection) readPump(conn *websocket.Conn) { websocket.CloseNormalClosure, websocket.CloseGoingAway, websocket.CloseNoStatusReceived) { - log.Printf("Error reading from %s: %v", c, err) + c.log.Error("Error reading", + zap.Error(err), + ) } break } if msgType != websocket.TextMessage { - log.Printf("unexpected message type %q (%s)", msgType, string(msg)) + c.log.Warn("Unexpected message type", + zap.Int("type", msgType), + zap.Binary("message", msg), + ) continue } var message signaling.ProxyServerMessage if err := json.Unmarshal(msg, &message); err != nil { - log.Printf("could not decode message %s: %s", string(msg), err) + c.log.Error("Error decoding", + zap.ByteString("message", msg), + zap.Error(err), + ) continue } @@ -353,7 +375,9 @@ func (c *RemoteConnection) sendPing() bool { msg := strconv.FormatInt(now.UnixNano(), 10) c.conn.SetWriteDeadline(now.Add(writeWait)) // nolint if err := c.conn.WriteMessage(websocket.PingMessage, []byte(msg)); err != nil { - log.Printf("Could not send ping to proxy at %s: %v", c, err) + c.log.Error("Could not send ping", + zap.Error(err), + ) go c.scheduleReconnect() return false } @@ -385,16 +409,22 @@ func (c *RemoteConnection) processHello(msg *signaling.ProxyServerMessage) { switch msg.Type { case "error": if msg.Error.Code == "no_such_session" { - log.Printf("Session %s could not be resumed on %s, registering new", c.sessionId, c) + c.log.Info("Session could not be resumed, registering new", + zap.String("sessionid", c.sessionId), + ) c.sessionId = "" if err := c.sendHello(); err != nil { - log.Printf("Could not send hello request to %s: %s", c, err) + c.log.Error("Error sending hello request to proxy", + zap.Error(err), + ) c.scheduleReconnect() } return } - log.Printf("Hello connection to %s failed with %+v, reconnecting", c, msg.Error) + c.log.Warn("Hello connection failed, reconnecting", + zap.Any("error", msg.Error), + ) c.scheduleReconnect() case "hello": resumed := c.sessionId == msg.Hello.SessionId @@ -402,16 +432,25 @@ func (c *RemoteConnection) processHello(msg *signaling.ProxyServerMessage) { country := "" if msg.Hello.Server != nil { if country = msg.Hello.Server.Country; country != "" && !signaling.IsValidCountry(country) { - log.Printf("Proxy %s sent invalid country %s in hello response", c, country) + c.log.Warn("Proxy sent invalid country in hello response", + zap.String("country", country), + ) country = "" } } if resumed { - log.Printf("Resumed session %s on %s", c.sessionId, c) + c.log.Info("Resumed session", + zap.String("sessionid", c.sessionId), + ) } else if country != "" { - log.Printf("Received session %s from %s (in %s)", c.sessionId, c, country) + c.log.Info("Received session", + zap.String("sessionid", c.sessionId), + zap.String("country", country), + ) } else { - log.Printf("Received session %s from %s", c.sessionId, c) + c.log.Info("Received session", + zap.String("sessionid", c.sessionId), + ) } pending := c.pendingMessages @@ -422,11 +461,16 @@ func (c *RemoteConnection) processHello(msg *signaling.ProxyServerMessage) { } if err := c.sendMessageLocked(context.Background(), m); err != nil { - log.Printf("Could not send pending message %+v to %s: %s", m, c, err) + c.log.Error("Could not send pending message", + zap.Any("message", m), + zap.Error(err), + ) } } default: - log.Printf("Received unsupported hello response %+v from %s, reconnecting", msg, c) + c.log.Warn("Received unsupported hello response, reconnecting", + zap.Any("message", msg), + ) c.scheduleReconnect() } } @@ -448,7 +492,9 @@ func (c *RemoteConnection) processMessage(msg *signaling.ProxyServerMessage) { case "event": c.processEvent(msg) default: - log.Printf("Received unsupported message %+v from %s", msg, c) + c.log.Warn("Received unsupported message", + zap.Any("message", msg), + ) } } @@ -456,7 +502,9 @@ func (c *RemoteConnection) processEvent(msg *signaling.ProxyServerMessage) { switch msg.Event.Type { case "update-load": default: - log.Printf("Received unsupported event %+v from %s", msg, c) + c.log.Warn("Received unsupported event", + zap.Any("message", msg), + ) } } diff --git a/proxy/proxy_server.go b/proxy/proxy_server.go index 68b77e25..ccf0be1b 100644 --- a/proxy/proxy_server.go +++ b/proxy/proxy_server.go @@ -30,7 +30,6 @@ import ( "errors" "fmt" "io" - "log" "net" "net/http" "net/http/pprof" @@ -50,6 +49,7 @@ import ( "github.com/gorilla/websocket" "github.com/notedit/janus-go" "github.com/prometheus/client_golang/prometheus/promhttp" + "go.uber.org/zap" signaling "github.com/strukturag/nextcloud-spreed-signaling" ) @@ -77,6 +77,8 @@ var ( defaultProxyFeatures = []string{ ProxyFeatureRemoteStreams, } + + ErrCancelled = errors.New("cancelled") ) type ContextKey string @@ -98,6 +100,7 @@ var ( ) type ProxyServer struct { + log *zap.Logger version string country string welcomeMessage string @@ -175,30 +178,38 @@ func GetLocalIP() (string, error) { return "", nil } -func getTargetBandwidths(config *goconf.ConfigFile) (int, int) { +func getTargetBandwidths(log *zap.Logger, config *goconf.ConfigFile) (int, int) { maxIncoming, _ := config.GetInt("bandwidth", "incoming") if maxIncoming < 0 { maxIncoming = 0 } if maxIncoming > 0 { - log.Printf("Target bandwidth for incoming streams: %d MBit/s", maxIncoming) + log.Info("Target bandwidth for incoming streams", + zap.Int("mbits", maxIncoming), + ) } else { - log.Printf("Target bandwidth for incoming streams: unlimited") + log.Info("Target bandwidth for incoming streams", + zap.String("mbits", "unlimited"), + ) } maxOutgoing, _ := config.GetInt("bandwidth", "outgoing") if maxOutgoing < 0 { maxOutgoing = 0 } if maxIncoming > 0 { - log.Printf("Target bandwidth for outgoing streams: %d MBit/s", maxOutgoing) + log.Info("Target bandwidth for outgoing streams", + zap.Int("mbits", maxOutgoing), + ) } else { - log.Printf("Target bandwidth for outgoing streams: unlimited") + log.Info("Target bandwidth for outgoing streams", + zap.String("mbits", "unlimited"), + ) } return maxIncoming, maxOutgoing } -func NewProxyServer(r *mux.Router, version string, config *goconf.ConfigFile) (*ProxyServer, error) { +func NewProxyServer(log *zap.Logger, r *mux.Router, version string, config *goconf.ConfigFile) (*ProxyServer, error) { hashKey := make([]byte, 64) if _, err := rand.Read(hashKey); err != nil { return nil, fmt.Errorf("Could not generate random hash key: %s", err) @@ -218,9 +229,9 @@ func NewProxyServer(r *mux.Router, version string, config *goconf.ConfigFile) (* switch tokenType { case TokenTypeEtcd: - tokens, err = NewProxyTokensEtcd(config) + tokens, err = NewProxyTokensEtcd(log, config) case TokenTypeStatic: - tokens, err = NewProxyTokensStatic(config) + tokens, err = NewProxyTokensStatic(log, config) default: return nil, fmt.Errorf("Unsupported token type configured: %s", tokenType) } @@ -235,10 +246,14 @@ func NewProxyServer(r *mux.Router, version string, config *goconf.ConfigFile) (* } if !statsAllowedIps.Empty() { - log.Printf("Only allowing access to the stats endpoint from %s", statsAllowed) + log.Info("Access to the stats endpoint only allowed from ips", + zap.Stringer("ips", statsAllowedIps), + ) } else { - log.Printf("No IPs configured for the stats endpoint, only allowing access from 127.0.0.1") statsAllowedIps = signaling.DefaultAllowedIps() + log.Info("No IPs configured for the stats endpoint, only allowing access from default ips", + zap.Stringer("ips", statsAllowedIps), + ) } trustedProxies, _ := config.GetString("app", "trustedproxies") @@ -248,20 +263,26 @@ func NewProxyServer(r *mux.Router, version string, config *goconf.ConfigFile) (* } if !trustedProxiesIps.Empty() { - log.Printf("Trusted proxies: %s", trustedProxiesIps) + log.Info("Trusted proxies", + zap.Any("proxies", trustedProxiesIps), + ) } else { trustedProxiesIps = signaling.DefaultTrustedProxies - log.Printf("No trusted proxies configured, only allowing for %s", trustedProxiesIps) + log.Info("No trusted proxies configured, allowing for default list", + zap.Any("proxies", trustedProxiesIps), + ) } country, _ := config.GetString("app", "country") country = strings.ToUpper(country) if signaling.IsValidCountry(country) { - log.Printf("Sending %s as country information", country) + log.Info("Sending country information", + zap.String("country", country), + ) } else if country != "" { return nil, fmt.Errorf("Invalid country: %s", country) } else { - log.Printf("Not sending country information") + log.Info("Not sending country information") } welcome := map[string]string{ @@ -291,7 +312,9 @@ func NewProxyServer(r *mux.Router, version string, config *goconf.ConfigFile) (* if err != nil { return nil, fmt.Errorf("Could not parse private key from %s: %s", tokenKeyFilename, err) } - log.Printf("Using \"%s\" as token id for remote streams", tokenId) + log.Info("Using token id for remote streams", + zap.String("tokenid", tokenId), + ) remoteHostname, _ = config.GetString("app", "hostname") if remoteHostname == "" { @@ -301,25 +324,28 @@ func NewProxyServer(r *mux.Router, version string, config *goconf.ConfigFile) (* } } if remoteHostname == "" { - log.Printf("WARNING: Could not determine hostname for remote streams, will be disabled. Please configure manually.") + log.Warn("Could not determine hostname for remote streams, will be disabled. Please configure manually.") } else { - log.Printf("Using \"%s\" as hostname for remote streams", remoteHostname) + log.Info("Using hostname for remote streams", + zap.String("hostname", remoteHostname), + ) } skipverify, _ := config.GetBool("backend", "skipverify") if skipverify { - log.Println("WARNING: Remote stream requests verification is disabled!") + log.Warn("Remote stream requests verification is disabled!") remoteTlsConfig = &tls.Config{ InsecureSkipVerify: skipverify, } } } else { - log.Printf("No token id configured, remote streams will be disabled") + log.Warn("No token id configured, remote streams will be disabled") } - maxIncoming, maxOutgoing := getTargetBandwidths(config) + maxIncoming, maxOutgoing := getTargetBandwidths(log, config) result := &ProxyServer{ + log: log, version: version, country: country, welcomeMessage: string(welcomeMessage) + "\n", @@ -359,7 +385,7 @@ func NewProxyServer(r *mux.Router, version string, config *goconf.ConfigFile) (* result.upgrader.CheckOrigin = result.checkOrigin if debug, _ := config.GetBool("app", "debug"); debug { - log.Println("Installing debug handlers in \"/debug/pprof\"") + log.Debug("Installing debug handlers in \"/debug/pprof\"") r.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index)) r.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline)) r.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile)) @@ -418,17 +444,26 @@ func (s *ProxyServer) Start(config *goconf.ConfigFile) error { mcu.SetOnDisconnected(s.onMcuDisconnected) err = mcu.Start(ctx) if err != nil { - log.Printf("Could not create %s MCU at %s: %s", mcuType, s.url, err) + s.log.Error("Could not create MCU", + zap.String("type", mcuType), + zap.String("url", s.url), + zap.Error(err), + ) } } if err == nil { break } - log.Printf("Could not initialize %s MCU at %s (%s) will retry in %s", mcuType, s.url, err, backoff.NextWait()) + s.log.Error("Could not initialize MCU, will retry", + zap.String("type", mcuType), + zap.String("url", s.url), + zap.Duration("wait", backoff.NextWait()), + zap.Error(err), + ) backoff.Wait(ctx) if ctx.Err() != nil { - return fmt.Errorf("Cancelled") + return ErrCancelled } } @@ -531,7 +566,9 @@ func (s *ProxyServer) expireSessions() { continue } - log.Printf("Delete expired session %s", session.PublicId()) + s.log.Info("Delete expired session", + zap.String("sessionid", session.PublicId()), + ) s.deleteSessionLocked(session.Sid()) } } @@ -575,30 +612,44 @@ func (s *ProxyServer) Reload(config *goconf.ConfigFile) { statsAllowed, _ := config.GetString("stats", "allowed_ips") if statsAllowedIps, err := signaling.ParseAllowedIps(statsAllowed); err == nil { if !statsAllowedIps.Empty() { - log.Printf("Only allowing access to the stats endpoint from %s", statsAllowed) + s.log.Info("Access to the stats endpoint only allowed from ips", + zap.Stringer("ips", statsAllowedIps), + ) } else { - log.Printf("No IPs configured for the stats endpoint, only allowing access from 127.0.0.1") statsAllowedIps = signaling.DefaultAllowedIps() + s.log.Info("No IPs configured for the stats endpoint, only allowing access from default ips", + zap.Stringer("ips", statsAllowedIps), + ) } s.statsAllowedIps.Store(statsAllowedIps) } else { - log.Printf("Error parsing allowed stats ips from \"%s\": %s", statsAllowedIps, err) + s.log.Error("Error parsing allowed stats ips", + zap.String("allowed", statsAllowed), + zap.Error(err), + ) } trustedProxies, _ := config.GetString("app", "trustedproxies") if trustedProxiesIps, err := signaling.ParseAllowedIps(trustedProxies); err == nil { if !trustedProxiesIps.Empty() { - log.Printf("Trusted proxies: %s", trustedProxiesIps) + s.log.Info("Trusted proxies", + zap.Any("proxies", trustedProxiesIps), + ) } else { trustedProxiesIps = signaling.DefaultTrustedProxies - log.Printf("No trusted proxies configured, only allowing for %s", trustedProxiesIps) + s.log.Info("No trusted proxies configured, allowing for default list", + zap.Any("proxies", trustedProxiesIps), + ) } s.trustedProxies.Store(trustedProxiesIps) } else { - log.Printf("Error parsing trusted proxies from \"%s\": %s", trustedProxies, err) + s.log.Error("Error parsing trusted proxies", + zap.String("trusted", trustedProxies), + zap.Error(err), + ) } - maxIncoming, maxOutgoing := getTargetBandwidths(config) + maxIncoming, maxOutgoing := getTargetBandwidths(s.log, config) oldIncoming := s.maxIncoming.Swap(int64(maxIncoming)) oldOutgoing := s.maxOutgoing.Swap(int64(maxOutgoing)) if oldIncoming != int64(maxIncoming) || oldOutgoing != int64(maxOutgoing) { @@ -625,18 +676,27 @@ func (s *ProxyServer) welcomeHandler(w http.ResponseWriter, r *http.Request) { func (s *ProxyServer) proxyHandler(w http.ResponseWriter, r *http.Request) { addr := signaling.GetRealUserIP(r, s.trustedProxies.Load()) + log := s.log.With( + zap.String("addr", addr), + ) header := http.Header{} header.Set("Server", "nextcloud-spreed-signaling-proxy/"+s.version) header.Set("X-Spreed-Signaling-Features", strings.Join(s.welcomeMsg.Features, ", ")) conn, err := s.upgrader.Upgrade(w, r, header) if err != nil { - log.Printf("Could not upgrade request from %s: %s", addr, err) + log.Error("Could not upgrade request", + zap.String("addr", addr), + zap.Error(err), + ) return } client, err := NewProxyClient(s, conn, addr) if err != nil { - log.Printf("Could not create client for %s: %s", addr, err) + log.Error("Could not create client", + zap.String("addr", addr), + zap.Error(err), + ) return } @@ -645,11 +705,15 @@ func (s *ProxyServer) proxyHandler(w http.ResponseWriter, r *http.Request) { } func (s *ProxyServer) clientClosed(client *signaling.Client) { - log.Printf("Connection from %s closed", client.RemoteAddr()) + s.log.Info("Connection closed", + zap.String("addr", client.RemoteAddr()), + ) } func (s *ProxyServer) onMcuConnected() { - log.Printf("Connection to %s established", s.url) + s.log.Info("Connection established", + zap.String("url", s.url), + ) msg := &signaling.ProxyServerMessage{ Type: "event", Event: &signaling.EventProxyServerMessage{ @@ -668,7 +732,9 @@ func (s *ProxyServer) onMcuDisconnected() { return } - log.Printf("Connection to %s lost", s.url) + s.log.Info("Connection lost", + zap.String("url", s.url), + ) msg := &signaling.ProxyServerMessage{ Type: "event", Event: &signaling.EventProxyServerMessage{ @@ -698,31 +764,38 @@ func (s *ProxyServer) sendShutdownScheduled(session *ProxySession) { } func (s *ProxyServer) processMessage(client *ProxyClient, data []byte) { + log := s.log.With( + zap.String("addr", client.RemoteAddr()), + ) + session := client.GetSession() + if session != nil { + log = log.With( + zap.String("sessionid", session.PublicId()), + ) + } if proxyDebugMessages { - log.Printf("Message: %s", string(data)) + log.Debug("Message received", + zap.ByteString("message", data), + ) } var message signaling.ProxyClientMessage if err := message.UnmarshalJSON(data); err != nil { - if session := client.GetSession(); session != nil { - log.Printf("Error decoding message from client %s: %v", session.PublicId(), err) - } else { - log.Printf("Error decoding message from %s: %v", client.RemoteAddr(), err) - } + log.Error("Error decoding message", + zap.Error(err), + ) client.SendError(signaling.InvalidFormat) return } if err := message.CheckValid(); err != nil { - if session := client.GetSession(); session != nil { - log.Printf("Invalid message %+v from client %s: %v", message, session.PublicId(), err) - } else { - log.Printf("Invalid message %+v from %s: %v", message, client.RemoteAddr(), err) - } + log.Error("Invalid message", + zap.Any("message", message), + zap.Error(err), + ) client.SendMessage(message.NewErrorServerMessage(signaling.InvalidFormat)) return } - session := client.GetSession() if session == nil { if message.Type != "hello" { client.SendMessage(message.NewErrorServerMessage(signaling.HelloExpected)) @@ -741,7 +814,10 @@ func (s *ProxyServer) processMessage(client *ProxyClient, data []byte) { return } - log.Printf("Resumed session %s", session.PublicId()) + log = log.With( + zap.String("sessionid", session.PublicId()), + ) + log.Debug("Resumed session") session.MarkUsed() if s.shutdownScheduled.Load() { s.sendShutdownScheduled(session) @@ -866,6 +942,10 @@ func (s *ProxyServer) processCommand(ctx context.Context, client *ProxyClient, s statsCommandMessagesTotal.WithLabelValues(cmd.Type).Inc() + log := s.log.With( + zap.String("sessionid", session.PublicId()), + ) + switch cmd.Type { case "create-publisher": if s.shutdownScheduled.Load() { @@ -874,18 +954,26 @@ func (s *ProxyServer) processCommand(ctx context.Context, client *ProxyClient, s } id := uuid.New().String() + log := log.With( + zap.Any("streamtype", cmd.StreamType), + zap.String("id", id), + ) publisher, err := s.mcu.NewPublisher(ctx, session, id, cmd.Sid, cmd.StreamType, cmd.Bitrate, cmd.MediaTypes, &emptyInitiator{}) if err == context.DeadlineExceeded { - log.Printf("Timeout while creating %s publisher %s for %s", cmd.StreamType, id, session.PublicId()) + log.Warn("Timeout creating publisher") session.sendMessage(message.NewErrorServerMessage(TimeoutCreatingPublisher)) return } else if err != nil { - log.Printf("Error while creating %s publisher %s for %s: %s", cmd.StreamType, id, session.PublicId(), err) + log.Error("Error creating publisher", + zap.Error(err), + ) session.sendMessage(message.NewWrappedErrorServerMessage(err)) return } - log.Printf("Created %s publisher %s as %s for %s", cmd.StreamType, publisher.Id(), id, session.PublicId()) + log.Info("Created publisher", + zap.String("publisherid", publisher.Id()), + ) session.StorePublisher(ctx, id, publisher) s.StoreClient(id, publisher) @@ -906,9 +994,15 @@ func (s *ProxyServer) processCommand(ctx context.Context, client *ProxyClient, s var subscriber signaling.McuSubscriber var err error - handleCreateError := func(err error) { + log := log.With( + zap.Any("streamtype", cmd.StreamType), + zap.String("id", id), + zap.String("publisherid", publisherId), + ) + + handleCreateError := func(log *zap.Logger, err error) { if err == context.DeadlineExceeded { - log.Printf("Timeout while creating %s subscriber on %s for %s", cmd.StreamType, publisherId, session.PublicId()) + log.Warn("Timeout creating subscriber") session.sendMessage(message.NewErrorServerMessage(TimeoutCreatingSubscriber)) return } else if errors.Is(err, signaling.ErrRemoteStreamsNotSupported) { @@ -916,11 +1010,16 @@ func (s *ProxyServer) processCommand(ctx context.Context, client *ProxyClient, s return } - log.Printf("Error while creating %s subscriber on %s for %s: %s", cmd.StreamType, publisherId, session.PublicId(), err) + log.Error("Error creating subscriber", + zap.Error(err), + ) session.sendMessage(message.NewWrappedErrorServerMessage(err)) } if cmd.RemoteUrl != "" { + log = log.With( + zap.String("remoteurl", cmd.RemoteUrl), + ) if s.tokenId == "" || s.tokenKey == nil || s.remoteHostname == "" { session.sendMessage(message.NewErrorServerMessage(RemoteSubscribersNotSupported)) return @@ -950,7 +1049,7 @@ func (s *ProxyServer) processCommand(ctx context.Context, client *ProxyClient, s subCtx, cancel := context.WithTimeout(ctx, remotePublisherTimeout) defer cancel() - log.Printf("Creating remote subscriber for %s on %s", publisherId, cmd.RemoteUrl) + log.Info("Creating remote subscriber") controller := &proxyRemotePublisher{ proxy: s, @@ -961,7 +1060,7 @@ func (s *ProxyServer) processCommand(ctx context.Context, client *ProxyClient, s var publisher signaling.McuRemotePublisher publisher, err = remoteMcu.NewRemotePublisher(subCtx, session, controller, cmd.StreamType) if err != nil { - handleCreateError(err) + handleCreateError(log, err) return } @@ -971,19 +1070,23 @@ func (s *ProxyServer) processCommand(ctx context.Context, client *ProxyClient, s subscriber, err = remoteMcu.NewRemoteSubscriber(subCtx, session, publisher) if err != nil { - handleCreateError(err) + handleCreateError(log, err) return } - log.Printf("Created remote %s subscriber %s as %s for %s on %s", cmd.StreamType, subscriber.Id(), id, session.PublicId(), cmd.RemoteUrl) + log.Info("Created remote subscriber", + zap.String("subscriberid", subscriber.Id()), + ) } else { subscriber, err = s.mcu.NewSubscriber(ctx, session, publisherId, cmd.StreamType, &emptyInitiator{}) if err != nil { - handleCreateError(err) + handleCreateError(log, err) return } - log.Printf("Created %s subscriber %s as %s for %s", cmd.StreamType, subscriber.Id(), id, session.PublicId()) + log.Info("Created subscriber", + zap.String("subscriberid", subscriber.Id()), + ) } session.StoreSubscriber(ctx, id, subscriber) @@ -1023,7 +1126,11 @@ func (s *ProxyServer) processCommand(ctx context.Context, client *ProxyClient, s } go func() { - log.Printf("Closing %s publisher %s as %s", client.StreamType(), client.Id(), cmd.ClientId) + s.log.Info("Closing publisher", + zap.Any("streamtype", cmd.StreamType), + zap.String("id", client.Id()), + zap.String("clientid", cmd.ClientId), + ) client.Close(context.Background()) }() @@ -1058,7 +1165,11 @@ func (s *ProxyServer) processCommand(ctx context.Context, client *ProxyClient, s } go func() { - log.Printf("Closing %s subscriber %s as %s", client.StreamType(), client.Id(), cmd.ClientId) + s.log.Info("Closing subscriber", + zap.Any("streamtype", cmd.StreamType), + zap.String("id", client.Id()), + zap.String("clientid", cmd.ClientId), + ) client.Close(context.Background()) }() @@ -1083,22 +1194,36 @@ func (s *ProxyServer) processCommand(ctx context.Context, client *ProxyClient, s return } + log := s.log.With( + zap.Any("streamtype", publisher.StreamType()), + zap.String("clientid", cmd.ClientId), + zap.String("hostname", cmd.Hostname), + zap.Int("port", cmd.Port), + zap.Int("rtcpport", cmd.RtcpPort), + ) + if err := publisher.PublishRemote(ctx, session.PublicId(), cmd.Hostname, cmd.Port, cmd.RtcpPort); err != nil { var je *janus.ErrorMsg if !errors.As(err, &je) || je.Err.Code != signaling.JANUS_VIDEOROOM_ERROR_ID_EXISTS { - log.Printf("Error publishing %s %s to remote %s (port=%d, rtcpPort=%d): %s", publisher.StreamType(), cmd.ClientId, cmd.Hostname, cmd.Port, cmd.RtcpPort, err) + log.Error("Error publishing to remote", + zap.Error(err), + ) session.sendMessage(message.NewWrappedErrorServerMessage(err)) return } if err := publisher.UnpublishRemote(ctx, session.PublicId()); err != nil { - log.Printf("Error unpublishing old %s %s to remote %s (port=%d, rtcpPort=%d): %s", publisher.StreamType(), cmd.ClientId, cmd.Hostname, cmd.Port, cmd.RtcpPort, err) + log.Error("Error unpublishing old to remote", + zap.Error(err), + ) session.sendMessage(message.NewWrappedErrorServerMessage(err)) return } if err := publisher.PublishRemote(ctx, session.PublicId(), cmd.Hostname, cmd.Port, cmd.RtcpPort); err != nil { - log.Printf("Error publishing %s %s to remote %s (port=%d, rtcpPort=%d): %s", publisher.StreamType(), cmd.ClientId, cmd.Hostname, cmd.Port, cmd.RtcpPort, err) + log.Error("Error publishing to remote", + zap.Error(err), + ) session.sendMessage(message.NewWrappedErrorServerMessage(err)) return } @@ -1127,7 +1252,10 @@ func (s *ProxyServer) processCommand(ctx context.Context, client *ProxyClient, s streams, err := publisher.GetStreams(ctx) if err != nil { - log.Printf("Could not get streams of publisher %s: %s", publisher.Id(), err) + s.log.Error("Could not get streams of publisher", + zap.String("publisherid", publisher.Id()), + zap.Error(err), + ) session.sendMessage(message.NewWrappedErrorServerMessage(err)) return } @@ -1142,7 +1270,9 @@ func (s *ProxyServer) processCommand(ctx context.Context, client *ProxyClient, s } session.sendMessage(response) default: - log.Printf("Unsupported command %+v", message.Command) + s.log.Warn("Unsupported command", + zap.Any("command", message.Command), + ) session.sendMessage(message.NewErrorServerMessage(UnsupportedCommand)) } } @@ -1196,8 +1326,16 @@ func (s *ProxyServer) processPayload(ctx context.Context, client *ProxyClient, s return } + log := s.log.With( + zap.Any("streamtype", mcuClient.StreamType()), + zap.String("clientid", payload.ClientId), + ) + if err := mcuData.CheckValid(); err != nil { - log.Printf("Received invalid payload %+v for %s client %s: %s", mcuData, mcuClient.StreamType(), payload.ClientId, err) + log.Error("Received invalid payload", + zap.Any("payload", mcuData), + zap.Error(err), + ) session.sendMessage(message.NewErrorServerMessage(UnsupportedPayload)) return } @@ -1205,7 +1343,10 @@ func (s *ProxyServer) processPayload(ctx context.Context, client *ProxyClient, s mcuClient.SendMessage(ctx, nil, mcuData, func(err error, response map[string]interface{}) { var responseMsg *signaling.ProxyServerMessage if err != nil { - log.Printf("Error sending %+v to %s client %s: %s", mcuData, mcuClient.StreamType(), payload.ClientId, err) + log.Error("Error processing request for client", + zap.Any("request", mcuData), + zap.Error(err), + ) responseMsg = message.NewWrappedErrorServerMessage(err) } else { responseMsg = &signaling.ProxyServerMessage{ @@ -1228,27 +1369,36 @@ func (s *ProxyServer) parseToken(tokenValue string) (*signaling.TokenClaims, str token, err := jwt.ParseWithClaims(tokenValue, &signaling.TokenClaims{}, func(token *jwt.Token) (interface{}, error) { // Don't forget to validate the alg is what you expect: if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok { - log.Printf("Unexpected signing method: %v", token.Header["alg"]) + s.log.Warn("Unexpected signing method", + zap.Any("method", token.Header["alg"]), + ) reason = "unsupported-signing-method" return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) } claims, ok := token.Claims.(*signaling.TokenClaims) if !ok { - log.Printf("Unsupported claims type: %+v", token.Claims) + s.log.Warn("Unsupported claims type", + zap.Any("claims", token.Claims), + ) reason = "unsupported-claims" return nil, fmt.Errorf("Unsupported claims type") } + log := s.log.With( + zap.String("issuer", claims.Issuer), + ) tokenKey, err := s.tokens.Get(claims.Issuer) if err != nil { - log.Printf("Could not get token for %s: %s", claims.Issuer, err) + s.log.Error("Could not get token", + zap.Error(err), + ) reason = "missing-issuer" return nil, err } if tokenKey == nil || tokenKey.key == nil { - log.Printf("Issuer %s is not supported", claims.Issuer) + log.Warn("Issuer is not supported") reason = "unsupported-issuer" return nil, fmt.Errorf("No key found for issuer") } @@ -1279,7 +1429,9 @@ func (s *ProxyServer) parseToken(tokenValue string) (*signaling.TokenClaims, str func (s *ProxyServer) NewSession(hello *signaling.HelloProxyClientMessage) (*ProxySession, error) { if proxyDebugMessages { - log.Printf("Hello: %+v", hello) + s.log.Debug("Hello received", + zap.Any("hello", hello), + ) } claims, reason, err := s.parseToken(hello.Token) @@ -1303,8 +1455,11 @@ func (s *ProxyServer) NewSession(hello *signaling.HelloProxyClientMessage) (*Pro return nil, err } - log.Printf("Created session %s for %+v", encoded, claims) - session := NewProxySession(s, sid, encoded) + s.log.Info("Created session", + zap.String("sessionid", encoded), + zap.Any("claims", claims), + ) + session := NewProxySession(s.log, s, sid, encoded) s.StoreSession(sid, session) statsSessionsCurrent.Inc() statsSessionsTotal.Inc() @@ -1461,7 +1616,10 @@ func (s *ProxyServer) statsHandler(w http.ResponseWriter, r *http.Request) { stats := s.getStats() statsData, err := json.MarshalIndent(stats, "", " ") if err != nil { - log.Printf("Could not serialize stats %+v: %s", stats, err) + s.log.Error("Could not serialize stats", + zap.Any("stats", stats), + zap.Error(err), + ) http.Error(w, "Internal server error", http.StatusInternalServerError) return } @@ -1486,7 +1644,7 @@ func (s *ProxyServer) getRemoteConnection(url string) (*RemoteConnection, error) return conn, nil } - conn, err := NewRemoteConnection(url, s.tokenId, s.tokenKey, s.remoteTlsConfig) + conn, err := NewRemoteConnection(s.log, url, s.tokenId, s.tokenKey, s.remoteTlsConfig) if err != nil { return nil, err } diff --git a/proxy/proxy_server_test.go b/proxy/proxy_server_test.go index 9dd3714f..9c008fae 100644 --- a/proxy/proxy_server_test.go +++ b/proxy/proxy_server_test.go @@ -40,6 +40,8 @@ import ( "github.com/gorilla/websocket" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.uber.org/zap/zaptest" + signaling "github.com/strukturag/nextcloud-spreed-signaling" ) @@ -94,7 +96,8 @@ func newProxyServerForTest(t *testing.T) (*ProxyServer, *rsa.PrivateKey, *httpte config := goconf.NewConfigFile() config.AddOption("tokens", TokenIdForTest, pubkey.Name()) - proxy, err = NewProxyServer(r, "0.0", config) + log := zaptest.NewLogger(t) + proxy, err = NewProxyServer(log, r, "0.0", config) require.NoError(err) server := httptest.NewServer(r) @@ -106,7 +109,6 @@ func newProxyServerForTest(t *testing.T) (*ProxyServer, *rsa.PrivateKey, *httpte } func TestTokenValid(t *testing.T) { - signaling.CatchLogForTest(t) proxy, key, _ := newProxyServerForTest(t) claims := &signaling.TokenClaims{ @@ -129,7 +131,6 @@ func TestTokenValid(t *testing.T) { } func TestTokenNotSigned(t *testing.T) { - signaling.CatchLogForTest(t) proxy, _, _ := newProxyServerForTest(t) claims := &signaling.TokenClaims{ @@ -154,7 +155,6 @@ func TestTokenNotSigned(t *testing.T) { } func TestTokenUnknown(t *testing.T) { - signaling.CatchLogForTest(t) proxy, key, _ := newProxyServerForTest(t) claims := &signaling.TokenClaims{ @@ -179,7 +179,6 @@ func TestTokenUnknown(t *testing.T) { } func TestTokenInFuture(t *testing.T) { - signaling.CatchLogForTest(t) proxy, key, _ := newProxyServerForTest(t) claims := &signaling.TokenClaims{ @@ -204,7 +203,6 @@ func TestTokenInFuture(t *testing.T) { } func TestTokenExpired(t *testing.T) { - signaling.CatchLogForTest(t) proxy, key, _ := newProxyServerForTest(t) claims := &signaling.TokenClaims{ @@ -261,7 +259,6 @@ func TestPublicIPs(t *testing.T) { } func TestWebsocketFeatures(t *testing.T) { - signaling.CatchLogForTest(t) assert := assert.New(t) _, _, server := newProxyServerForTest(t) diff --git a/proxy/proxy_session.go b/proxy/proxy_session.go index f2c9f499..e1ba1355 100644 --- a/proxy/proxy_session.go +++ b/proxy/proxy_session.go @@ -23,11 +23,12 @@ package main import ( "context" - "log" "sync" "sync/atomic" "time" + "go.uber.org/zap" + signaling "github.com/strukturag/nextcloud-spreed-signaling" ) @@ -37,6 +38,7 @@ const ( ) type ProxySession struct { + log *zap.Logger proxy *ProxyServer id string sid uint64 @@ -55,8 +57,12 @@ type ProxySession struct { subscriberIds map[signaling.McuSubscriber]string } -func NewProxySession(proxy *ProxyServer, sid uint64, id string) *ProxySession { +func NewProxySession(log *zap.Logger, proxy *ProxyServer, sid uint64, id string) *ProxySession { + log = log.With( + zap.String("sessionid", id), + ) result := &ProxySession{ + log: log, proxy: proxy, id: id, sid: sid, @@ -124,7 +130,12 @@ func (s *ProxySession) SetClient(client *ProxyClient) *ProxyClient { func (s *ProxySession) OnUpdateOffer(client signaling.McuClient, offer map[string]interface{}) { id := s.proxy.GetClientId(client) if id == "" { - log.Printf("Received offer %+v from unknown %s client %s (%+v)", offer, client.StreamType(), client.Id(), client) + s.log.Warn("Received offer from unknown client", + zap.Any("offer", offer), + zap.Any("streamtype", client.StreamType()), + zap.String("id", client.Id()), + zap.Any("client", client), + ) return } @@ -144,7 +155,12 @@ func (s *ProxySession) OnUpdateOffer(client signaling.McuClient, offer map[strin func (s *ProxySession) OnIceCandidate(client signaling.McuClient, candidate interface{}) { id := s.proxy.GetClientId(client) if id == "" { - log.Printf("Received candidate %+v from unknown %s client %s (%+v)", candidate, client.StreamType(), client.Id(), client) + s.log.Warn("Received candidate from unknown client", + zap.Any("candidate", candidate), + zap.Any("streamtype", client.StreamType()), + zap.String("id", client.Id()), + zap.Any("client", client), + ) return } @@ -177,7 +193,11 @@ func (s *ProxySession) sendMessage(message *signaling.ProxyServerMessage) { func (s *ProxySession) OnIceCompleted(client signaling.McuClient) { id := s.proxy.GetClientId(client) if id == "" { - log.Printf("Received ice completed event from unknown %s client %s (%+v)", client.StreamType(), client.Id(), client) + s.log.Warn("Received ice completed event from unknown client", + zap.Any("streamtype", client.StreamType()), + zap.String("id", client.Id()), + zap.Any("client", client), + ) return } @@ -194,7 +214,11 @@ func (s *ProxySession) OnIceCompleted(client signaling.McuClient) { func (s *ProxySession) SubscriberSidUpdated(subscriber signaling.McuSubscriber) { id := s.proxy.GetClientId(subscriber) if id == "" { - log.Printf("Received subscriber sid updated event from unknown %s subscriber %s (%+v)", subscriber.StreamType(), subscriber.Id(), subscriber) + s.log.Warn("Received subscriber sid updated event from unknown subscriber", + zap.Any("streamtype", subscriber.StreamType()), + zap.String("id", subscriber.Id()), + zap.Any("subscriber", subscriber), + ) return } diff --git a/proxy/proxy_tokens_etcd.go b/proxy/proxy_tokens_etcd.go index cbee8034..ad9b8afa 100644 --- a/proxy/proxy_tokens_etcd.go +++ b/proxy/proxy_tokens_etcd.go @@ -25,13 +25,13 @@ import ( "bytes" "context" "fmt" - "log" "strings" "sync/atomic" "time" "github.com/dlintw/goconf" "github.com/golang-jwt/jwt/v4" + "go.uber.org/zap" signaling "github.com/strukturag/nextcloud-spreed-signaling" ) @@ -46,13 +46,14 @@ type tokenCacheEntry struct { } type tokensEtcd struct { + log *zap.Logger client *signaling.EtcdClient tokenFormats atomic.Value tokenCache *signaling.LruCache } -func NewProxyTokensEtcd(config *goconf.ConfigFile) (ProxyTokens, error) { +func NewProxyTokensEtcd(log *zap.Logger, config *goconf.ConfigFile) (ProxyTokens, error) { client, err := signaling.NewEtcdClient(config, "tokens") if err != nil { return nil, err @@ -63,6 +64,7 @@ func NewProxyTokensEtcd(config *goconf.ConfigFile) (ProxyTokens, error) { } result := &tokensEtcd{ + log: log, client: client, tokenCache: signaling.NewLruCache(tokenCacheSize), } @@ -94,7 +96,9 @@ func (t *tokensEtcd) getByKey(id string, key string) (*ProxyToken, error) { if len(resp.Kvs) == 0 { return nil, nil } else if len(resp.Kvs) > 1 { - log.Printf("Received multiple keys for %s, using last", key) + t.log.Warn("Received multiple entries, using last", + zap.String("key", key), + ) } keyValue := resp.Kvs[len(resp.Kvs)-1].Value @@ -123,7 +127,11 @@ func (t *tokensEtcd) Get(id string) (*ProxyToken, error) { for _, k := range t.getKeys(id) { token, err := t.getByKey(id, k) if err != nil { - log.Printf("Could not get public key from %s for %s: %s", k, id, err) + t.log.Error("Could not get public key", + zap.String("id", id), + zap.String("key", k), + zap.Error(err), + ) continue } else if token == nil { continue @@ -151,18 +159,24 @@ func (t *tokensEtcd) load(config *goconf.ConfigFile, ignoreErrors bool) error { } t.tokenFormats.Store(tokenFormats) - log.Printf("Using %v as token formats", tokenFormats) + t.log.Info("Using token formats", + zap.Any("formats", tokenFormats), + ) return nil } func (t *tokensEtcd) Reload(config *goconf.ConfigFile) { if err := t.load(config, true); err != nil { - log.Printf("Error reloading etcd tokens: %s", err) + t.log.Error("Error reloading etcd tokens", + zap.Error(err), + ) } } func (t *tokensEtcd) Close() { if err := t.client.Close(); err != nil { - log.Printf("Error while closing etcd client: %s", err) + t.log.Error("Error closing etcd client", + zap.Error(err), + ) } } diff --git a/proxy/proxy_tokens_etcd_test.go b/proxy/proxy_tokens_etcd_test.go index 957f7d51..c135dc9e 100644 --- a/proxy/proxy_tokens_etcd_test.go +++ b/proxy/proxy_tokens_etcd_test.go @@ -41,8 +41,7 @@ import ( "github.com/stretchr/testify/require" "go.etcd.io/etcd/server/v3/embed" "go.etcd.io/etcd/server/v3/lease" - - signaling "github.com/strukturag/nextcloud-spreed-signaling" + "go.uber.org/zap/zaptest" ) var ( @@ -115,7 +114,8 @@ func newTokensEtcdForTesting(t *testing.T) (*tokensEtcd, *embed.Etcd) { cfg.AddOption("etcd", "endpoints", etcd.Config().ListenClientUrls[0].String()) cfg.AddOption("tokens", "keyformat", "/%s, /testing/%s/key") - tokens, err := NewProxyTokensEtcd(cfg) + log := zaptest.NewLogger(t) + tokens, err := NewProxyTokensEtcd(log, cfg) require.NoError(t, err) t.Cleanup(func() { tokens.Close() @@ -155,7 +155,6 @@ func generateAndSaveKey(t *testing.T, etcd *embed.Etcd, name string) *rsa.Privat } func TestProxyTokensEtcd(t *testing.T) { - signaling.CatchLogForTest(t) assert := assert.New(t) tokens, etcd := newTokensEtcdForTesting(t) diff --git a/proxy/proxy_tokens_static.go b/proxy/proxy_tokens_static.go index ac22c2b7..a858f84b 100644 --- a/proxy/proxy_tokens_static.go +++ b/proxy/proxy_tokens_static.go @@ -23,22 +23,26 @@ package main import ( "fmt" - "log" "os" "sort" "sync/atomic" "github.com/dlintw/goconf" "github.com/golang-jwt/jwt/v4" + "go.uber.org/zap" + signaling "github.com/strukturag/nextcloud-spreed-signaling" ) type tokensStatic struct { + log *zap.Logger tokenKeys atomic.Value } -func NewProxyTokensStatic(config *goconf.ConfigFile) (ProxyTokens, error) { - result := &tokensStatic{} +func NewProxyTokensStatic(log *zap.Logger, config *goconf.ConfigFile) (ProxyTokens, error) { + result := &tokensStatic{ + log: log, + } if err := result.load(config, false); err != nil { return nil, err } @@ -68,22 +72,31 @@ func (t *tokensStatic) load(config *goconf.ConfigFile, ignoreErrors bool) error tokenKeys := make(map[string]*ProxyToken) for id, filename := range options { + log := t.log.With( + zap.String("tokenid", id), + ) if filename == "" { if !ignoreErrors { return fmt.Errorf("No filename given for token %s", id) } - log.Printf("No filename given for token %s, ignoring", id) + log.Warn("No filename given for token, ignoring") continue } + log = log.With( + zap.String("filename", filename), + ) + keyData, err := os.ReadFile(filename) if err != nil { if !ignoreErrors { return fmt.Errorf("Could not read public key from %s: %s", filename, err) } - log.Printf("Could not read public key from %s, ignoring: %s", filename, err) + log.Error("Could not read public key for token, ignoring", + zap.Error(err), + ) continue } key, err := jwt.ParseRSAPublicKeyFromPEM(keyData) @@ -92,7 +105,9 @@ func (t *tokensStatic) load(config *goconf.ConfigFile, ignoreErrors bool) error return fmt.Errorf("Could not parse public key from %s: %s", filename, err) } - log.Printf("Could not parse public key from %s, ignoring: %s", filename, err) + log.Error("Could not parse public key for token, ignoring", + zap.Error(err), + ) continue } @@ -103,14 +118,16 @@ func (t *tokensStatic) load(config *goconf.ConfigFile, ignoreErrors bool) error } if len(tokenKeys) == 0 { - log.Printf("No token keys loaded") + t.log.Warn("No token keys loaded") } else { var keyIds []string for k := range tokenKeys { keyIds = append(keyIds, k) } sort.Strings(keyIds) - log.Printf("Enabled token keys: %v", keyIds) + t.log.Info("Enabled token keys", + zap.Any("tokenids", keyIds), + ) } t.setTokenKeys(tokenKeys) return nil @@ -118,7 +135,9 @@ func (t *tokensStatic) load(config *goconf.ConfigFile, ignoreErrors bool) error func (t *tokensStatic) Reload(config *goconf.ConfigFile) { if err := t.load(config, true); err != nil { - log.Printf("Error reloading static tokens: %s", err) + t.log.Error("Error reloading static tokens", + zap.Error(err), + ) } } From d5a388f51002cd1860a59735db140366c0cde063 Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Sat, 14 Sep 2024 21:23:22 +0200 Subject: [PATCH 2/3] server: Switch to using "go.uber.org/zap" for logging. --- api_signaling.go | 9 +- async_events.go | 12 +- async_events_nats.go | 60 ++-- async_events_test.go | 6 +- backend_client.go | 69 +++- backend_client_test.go | 16 +- backend_configuration.go | 8 +- backend_configuration_test.go | 65 ++-- backend_server.go | 163 ++++++++-- backend_server_test.go | 39 +-- backend_storage_etcd.go | 55 +++- backend_storage_etcd_test.go | 4 +- backend_storage_static.go | 85 +++-- capabilities.go | 114 +++++-- capabilities_test.go | 11 +- certificate_reloader.go | 37 ++- client.go | 109 +++++-- clientsession.go | 159 +++++++-- clientsession_test.go | 6 +- deferred_executor.go | 17 +- deferred_executor_test.go | 18 +- dns_monitor.go | 12 +- dns_monitor_test.go | 3 +- etcd_client.go | 53 ++- etcd_client_test.go | 6 +- federation.go | 81 +++-- federation_test.go | 18 +- file_watcher.go | 16 +- file_watcher_test.go | 21 +- geoip.go | 50 ++- geoip_test.go | 16 +- grpc_client.go | 169 +++++++--- grpc_client_test.go | 12 +- grpc_common.go | 12 +- grpc_remote_client.go | 22 +- grpc_server.go | 38 ++- grpc_server_test.go | 10 +- hub.go | 568 ++++++++++++++++++++++++++------- hub_test.go | 102 ++---- janus_client.go | 55 +++- mcu_janus.go | 158 +++++++-- mcu_janus_client.go | 27 +- mcu_janus_publisher.go | 84 +++-- mcu_janus_remote_publisher.go | 56 +++- mcu_janus_remote_subscriber.go | 45 ++- mcu_janus_subscriber.go | 66 +++- mcu_proxy.go | 369 +++++++++++++++------ mcu_proxy_test.go | 19 +- mcu_test.go | 19 +- natsclient.go | 42 ++- natsclient_loopback.go | 10 +- natsclient_loopback_test.go | 3 +- natsclient_test.go | 7 +- proxy/proxy_client.go | 6 +- proxy/proxy_server.go | 4 +- proxy/proxy_tokens_etcd.go | 2 +- proxy_config_etcd.go | 57 +++- proxy_config_etcd_test.go | 4 +- proxy_config_static.go | 22 +- proxy_config_static_test.go | 5 +- remotesession.go | 19 +- room.go | 123 +++++-- room_ping.go | 19 +- room_ping_test.go | 9 +- room_test.go | 16 +- roomsessions_builtin.go | 20 +- roomsessions_builtin_test.go | 3 +- server/main.go | 207 ++++++++---- test_helpers.go | 48 +-- throttle.go | 18 +- throttle_test.go | 3 +- transient_data_test.go | 1 - virtualsession.go | 39 ++- virtualsession_test.go | 3 - 74 files changed, 2755 insertions(+), 1104 deletions(-) diff --git a/api_signaling.go b/api_signaling.go index 84851f73..9de7c208 100644 --- a/api_signaling.go +++ b/api_signaling.go @@ -25,7 +25,6 @@ import ( "encoding/json" "errors" "fmt" - "log" "net/url" "sort" "strings" @@ -33,6 +32,7 @@ import ( "github.com/golang-jwt/jwt/v4" "github.com/pion/sdp/v3" + "go.uber.org/zap" ) const ( @@ -265,7 +265,12 @@ func NewErrorDetail(code string, message string, details interface{}) *Error { if details != nil { var err error if rawDetails, err = json.Marshal(details); err != nil { - log.Printf("Could not marshal details %+v for error %s with %s: %s", details, code, message, err) + zap.L().Error("Could not marshal error details", + zap.String("code", code), + zap.String("message", message), + zap.Any("details", details), + zap.Error(err), + ) return NewError("internal_error", "Could not marshal error details") } } diff --git a/async_events.go b/async_events.go index 4fb34d88..7b1c1aef 100644 --- a/async_events.go +++ b/async_events.go @@ -21,7 +21,11 @@ */ package signaling -import "sync" +import ( + "sync" + + "go.uber.org/zap" +) type AsyncBackendRoomEventListener interface { ProcessBackendRoomRequest(message *AsyncMessage) @@ -60,13 +64,13 @@ type AsyncEvents interface { PublishSessionMessage(sessionId string, backend *Backend, message *AsyncMessage) error } -func NewAsyncEvents(url string) (AsyncEvents, error) { - client, err := NewNatsClient(url) +func NewAsyncEvents(log *zap.Logger, url string) (AsyncEvents, error) { + client, err := NewNatsClient(log, url) if err != nil { return nil, err } - return NewAsyncEventsNats(client) + return NewAsyncEventsNats(log, client) } type asyncBackendRoomSubscriber struct { diff --git a/async_events_nats.go b/async_events_nats.go index 0db35023..83b16106 100644 --- a/async_events_nats.go +++ b/async_events_nats.go @@ -22,11 +22,11 @@ package signaling import ( - "log" "sync" "time" "github.com/nats-io/nats.go" + "go.uber.org/zap" ) func GetSubjectForBackendRoomId(roomId string, backend *Backend) string { @@ -58,6 +58,7 @@ func GetSubjectForSessionId(sessionId string, backend *Backend) string { } type asyncSubscriberNats struct { + log *zap.Logger key string client NatsClient @@ -68,7 +69,7 @@ type asyncSubscriberNats struct { processMessage func(*nats.Msg) } -func newAsyncSubscriberNats(key string, client NatsClient) (*asyncSubscriberNats, error) { +func newAsyncSubscriberNats(log *zap.Logger, key string, client NatsClient) (*asyncSubscriberNats, error) { receiver := make(chan *nats.Msg, 64) sub, err := client.Subscribe(key, receiver) if err != nil { @@ -76,6 +77,9 @@ func newAsyncSubscriberNats(key string, client NatsClient) (*asyncSubscriberNats } result := &asyncSubscriberNats{ + log: log.With( + zap.String("key", key), + ), key: key, client: client, @@ -89,7 +93,9 @@ func newAsyncSubscriberNats(key string, client NatsClient) (*asyncSubscriberNats func (s *asyncSubscriberNats) run() { defer func() { if err := s.subscription.Unsubscribe(); err != nil { - log.Printf("Error unsubscribing %s: %s", s.key, err) + s.log.Error("Error unsubscribing", + zap.Error(err), + ) } }() @@ -115,8 +121,8 @@ type asyncBackendRoomSubscriberNats struct { asyncBackendRoomSubscriber } -func newAsyncBackendRoomSubscriberNats(key string, client NatsClient) (*asyncBackendRoomSubscriberNats, error) { - sub, err := newAsyncSubscriberNats(key, client) +func newAsyncBackendRoomSubscriberNats(log *zap.Logger, key string, client NatsClient) (*asyncBackendRoomSubscriberNats, error) { + sub, err := newAsyncSubscriberNats(log, key, client) if err != nil { return nil, err } @@ -132,7 +138,10 @@ func newAsyncBackendRoomSubscriberNats(key string, client NatsClient) (*asyncBac func (s *asyncBackendRoomSubscriberNats) doProcessMessage(msg *nats.Msg) { var message AsyncMessage if err := s.client.Decode(msg, &message); err != nil { - log.Printf("Could not decode NATS message %+v, %s", msg, err) + s.log.Error("Could not decode NATS message", + zap.Any("message", msg), + zap.Error(err), + ) return } @@ -144,8 +153,8 @@ type asyncRoomSubscriberNats struct { *asyncSubscriberNats } -func newAsyncRoomSubscriberNats(key string, client NatsClient) (*asyncRoomSubscriberNats, error) { - sub, err := newAsyncSubscriberNats(key, client) +func newAsyncRoomSubscriberNats(log *zap.Logger, key string, client NatsClient) (*asyncRoomSubscriberNats, error) { + sub, err := newAsyncSubscriberNats(log, key, client) if err != nil { return nil, err } @@ -161,7 +170,10 @@ func newAsyncRoomSubscriberNats(key string, client NatsClient) (*asyncRoomSubscr func (s *asyncRoomSubscriberNats) doProcessMessage(msg *nats.Msg) { var message AsyncMessage if err := s.client.Decode(msg, &message); err != nil { - log.Printf("Could not decode nats message %+v, %s", msg, err) + s.log.Error("Could not decode NATS message", + zap.Any("message", msg), + zap.Error(err), + ) return } @@ -173,8 +185,8 @@ type asyncUserSubscriberNats struct { asyncUserSubscriber } -func newAsyncUserSubscriberNats(key string, client NatsClient) (*asyncUserSubscriberNats, error) { - sub, err := newAsyncSubscriberNats(key, client) +func newAsyncUserSubscriberNats(log *zap.Logger, key string, client NatsClient) (*asyncUserSubscriberNats, error) { + sub, err := newAsyncSubscriberNats(log, key, client) if err != nil { return nil, err } @@ -190,7 +202,10 @@ func newAsyncUserSubscriberNats(key string, client NatsClient) (*asyncUserSubscr func (s *asyncUserSubscriberNats) doProcessMessage(msg *nats.Msg) { var message AsyncMessage if err := s.client.Decode(msg, &message); err != nil { - log.Printf("Could not decode nats message %+v, %s", msg, err) + s.log.Error("Could not decode NATS message", + zap.Any("message", msg), + zap.Error(err), + ) return } @@ -202,8 +217,8 @@ type asyncSessionSubscriberNats struct { asyncSessionSubscriber } -func newAsyncSessionSubscriberNats(key string, client NatsClient) (*asyncSessionSubscriberNats, error) { - sub, err := newAsyncSubscriberNats(key, client) +func newAsyncSessionSubscriberNats(log *zap.Logger, key string, client NatsClient) (*asyncSessionSubscriberNats, error) { + sub, err := newAsyncSubscriberNats(log, key, client) if err != nil { return nil, err } @@ -219,7 +234,10 @@ func newAsyncSessionSubscriberNats(key string, client NatsClient) (*asyncSession func (s *asyncSessionSubscriberNats) doProcessMessage(msg *nats.Msg) { var message AsyncMessage if err := s.client.Decode(msg, &message); err != nil { - log.Printf("Could not decode nats message %+v, %s", msg, err) + s.log.Error("Could not decode NATS message", + zap.Any("message", msg), + zap.Error(err), + ) return } @@ -227,6 +245,7 @@ func (s *asyncSessionSubscriberNats) doProcessMessage(msg *nats.Msg) { } type asyncEventsNats struct { + log *zap.Logger mu sync.Mutex client NatsClient @@ -236,8 +255,9 @@ type asyncEventsNats struct { sessionSubscriptions map[string]*asyncSessionSubscriberNats } -func NewAsyncEventsNats(client NatsClient) (AsyncEvents, error) { +func NewAsyncEventsNats(log *zap.Logger, client NatsClient) (AsyncEvents, error) { events := &asyncEventsNats{ + log: log, client: client, backendRoomSubscriptions: make(map[string]*asyncBackendRoomSubscriberNats), @@ -298,7 +318,7 @@ func (e *asyncEventsNats) RegisterBackendRoomListener(roomId string, backend *Ba sub, found := e.backendRoomSubscriptions[key] if !found { var err error - if sub, err = newAsyncBackendRoomSubscriberNats(key, e.client); err != nil { + if sub, err = newAsyncBackendRoomSubscriberNats(e.log, key, e.client); err != nil { return err } @@ -332,7 +352,7 @@ func (e *asyncEventsNats) RegisterRoomListener(roomId string, backend *Backend, sub, found := e.roomSubscriptions[key] if !found { var err error - if sub, err = newAsyncRoomSubscriberNats(key, e.client); err != nil { + if sub, err = newAsyncRoomSubscriberNats(e.log, key, e.client); err != nil { return err } @@ -366,7 +386,7 @@ func (e *asyncEventsNats) RegisterUserListener(roomId string, backend *Backend, sub, found := e.userSubscriptions[key] if !found { var err error - if sub, err = newAsyncUserSubscriberNats(key, e.client); err != nil { + if sub, err = newAsyncUserSubscriberNats(e.log, key, e.client); err != nil { return err } @@ -400,7 +420,7 @@ func (e *asyncEventsNats) RegisterSessionListener(sessionId string, backend *Bac sub, found := e.sessionSubscriptions[key] if !found { var err error - if sub, err = newAsyncSessionSubscriberNats(key, e.client); err != nil { + if sub, err = newAsyncSessionSubscriberNats(e.log, key, e.client); err != nil { return err } diff --git a/async_events_test.go b/async_events_test.go index b72a30a0..525c35f3 100644 --- a/async_events_test.go +++ b/async_events_test.go @@ -51,7 +51,8 @@ func getAsyncEventsForTest(t *testing.T) AsyncEvents { func getRealAsyncEventsForTest(t *testing.T) AsyncEvents { url := startLocalNatsServer(t) - events, err := NewAsyncEvents(url) + log := GetLoggerForTest(t) + events, err := NewAsyncEvents(log, url) if err != nil { require.NoError(t, err) } @@ -59,7 +60,8 @@ func getRealAsyncEventsForTest(t *testing.T) AsyncEvents { } func getLoopbackAsyncEventsForTest(t *testing.T) AsyncEvents { - events, err := NewAsyncEvents(NatsLoopbackUrl) + log := GetLoggerForTest(t) + events, err := NewAsyncEvents(log, NatsLoopbackUrl) if err != nil { require.NoError(t, err) } diff --git a/backend_client.go b/backend_client.go index 8b60869f..297a043d 100644 --- a/backend_client.go +++ b/backend_client.go @@ -28,12 +28,12 @@ import ( "errors" "fmt" "io" - "log" "net/http" "net/url" "strings" "github.com/dlintw/goconf" + "go.uber.org/zap" ) var ( @@ -45,6 +45,7 @@ var ( ) type BackendClient struct { + log *zap.Logger hub *Hub version string backends *BackendConfiguration @@ -53,15 +54,15 @@ type BackendClient struct { capabilities *Capabilities } -func NewBackendClient(config *goconf.ConfigFile, maxConcurrentRequestsPerHost int, version string, etcdClient *EtcdClient) (*BackendClient, error) { - backends, err := NewBackendConfiguration(config, etcdClient) +func NewBackendClient(log *zap.Logger, config *goconf.ConfigFile, maxConcurrentRequestsPerHost int, version string, etcdClient *EtcdClient) (*BackendClient, error) { + backends, err := NewBackendConfiguration(log, config, etcdClient) if err != nil { return nil, err } skipverify, _ := config.GetBool("backend", "skipverify") if skipverify { - log.Println("WARNING: Backend verification is disabled!") + log.Warn("Backend verification is disabled!") } pool, err := NewHttpClientPool(maxConcurrentRequestsPerHost, skipverify) @@ -69,12 +70,13 @@ func NewBackendClient(config *goconf.ConfigFile, maxConcurrentRequestsPerHost in return nil, err } - capabilities, err := NewCapabilities(version, pool) + capabilities, err := NewCapabilities(log, version, pool) if err != nil { return nil, err } return &BackendClient{ + log: log, version: version, backends: backends, @@ -135,20 +137,30 @@ func (b *BackendClient) PerformJSONRequest(ctx context.Context, u *url.URL, requ c, pool, err := b.pool.Get(ctx, u) if err != nil { - log.Printf("Could not get client for host %s: %s", u.Host, err) + b.log.Error("Could not get client", + zap.String("host", u.Host), + zap.Error(err), + ) return err } defer pool.Put(c) data, err := json.Marshal(request) if err != nil { - log.Printf("Could not marshal request %+v: %s", request, err) + b.log.Error("Could not marshal request", + zap.Any("request", request), + zap.Error(err), + ) return err } req, err := http.NewRequestWithContext(ctx, "POST", requestUrl.String(), bytes.NewReader(data)) if err != nil { - log.Printf("Could not create request to %s: %s", requestUrl, err) + b.log.Error("Could not create request", + zap.Stringer("url", requestUrl), + zap.ByteString("body", data), + zap.Error(err), + ) return err } req.Header.Set("Content-Type", "application/json") @@ -162,22 +174,34 @@ func (b *BackendClient) PerformJSONRequest(ctx context.Context, u *url.URL, requ // Add checksum so the backend can validate the request. AddBackendChecksum(req, data, secret) + log := b.log.With( + zap.Stringer("url", req.URL), + ) + resp, err := c.Do(req) if err != nil { - log.Printf("Could not send request %s to %s: %s", string(data), req.URL, err) + log.Error("Could not send request", + zap.ByteString("body", data), + zap.Error(err), + ) return err } defer resp.Body.Close() ct := resp.Header.Get("Content-Type") if !strings.HasPrefix(ct, "application/json") { - log.Printf("Received unsupported content-type from %s: %s (%s)", req.URL, ct, resp.Status) + log.Error("Received unsupported content-type", + zap.String("contenttype", ct), + zap.String("status", resp.Status), + ) return ErrUnsupportedContentType } body, err := io.ReadAll(resp.Body) if err != nil { - log.Printf("Could not read response body from %s: %s", req.URL, err) + log.Error("Could not read response body", + zap.Error(err), + ) return err } @@ -192,25 +216,38 @@ func (b *BackendClient) PerformJSONRequest(ctx context.Context, u *url.URL, requ // } var ocs OcsResponse if err := json.Unmarshal(body, &ocs); err != nil { - log.Printf("Could not decode OCS response %s from %s: %s", string(body), req.URL, err) + log.Error("Could not decode OCS response", + zap.ByteString("response", body), + zap.Error(err), + ) return err } else if ocs.Ocs == nil || len(ocs.Ocs.Data) == 0 { - log.Printf("Incomplete OCS response %s from %s", string(body), req.URL) + log.Error("Incomplete OCS response", + zap.ByteString("response", body), + ) return ErrIncompleteResponse } switch ocs.Ocs.Meta.StatusCode { case http.StatusTooManyRequests: - log.Printf("Throttled OCS response %s from %s", string(body), req.URL) + log.Error("Throttled OCS response", + zap.ByteString("response", body), + ) return ErrThrottledResponse } if err := json.Unmarshal(ocs.Ocs.Data, response); err != nil { - log.Printf("Could not decode OCS response body %s from %s: %s", string(ocs.Ocs.Data), req.URL, err) + log.Error("Could not decode OCS response body", + zap.ByteString("response", body), + zap.Error(err), + ) return err } } else if err := json.Unmarshal(body, response); err != nil { - log.Printf("Could not decode response body %s from %s: %s", string(body), req.URL, err) + log.Error("Could not decode response body", + zap.ByteString("response", body), + zap.Error(err), + ) return err } return nil diff --git a/backend_client_test.go b/backend_client_test.go index d0a4d779..b35a6d8b 100644 --- a/backend_client_test.go +++ b/backend_client_test.go @@ -67,7 +67,7 @@ func returnOCS(t *testing.T, w http.ResponseWriter, body []byte) { func TestPostOnRedirect(t *testing.T) { t.Parallel() - CatchLogForTest(t) + log := GetLoggerForTest(t) require := require.New(t) r := mux.NewRouter() r.HandleFunc("/ocs/v2.php/one", func(w http.ResponseWriter, r *http.Request) { @@ -96,7 +96,7 @@ func TestPostOnRedirect(t *testing.T) { if u.Scheme == "http" { config.AddOption("backend", "allowhttp", "true") } - client, err := NewBackendClient(config, 1, "0.0", nil) + client, err := NewBackendClient(log, config, 1, "0.0", nil) require.NoError(err) ctx := context.Background() @@ -114,7 +114,7 @@ func TestPostOnRedirect(t *testing.T) { func TestPostOnRedirectDifferentHost(t *testing.T) { t.Parallel() - CatchLogForTest(t) + log := GetLoggerForTest(t) require := require.New(t) r := mux.NewRouter() r.HandleFunc("/ocs/v2.php/one", func(w http.ResponseWriter, r *http.Request) { @@ -132,7 +132,7 @@ func TestPostOnRedirectDifferentHost(t *testing.T) { if u.Scheme == "http" { config.AddOption("backend", "allowhttp", "true") } - client, err := NewBackendClient(config, 1, "0.0", nil) + client, err := NewBackendClient(log, config, 1, "0.0", nil) require.NoError(err) ctx := context.Background() @@ -151,7 +151,7 @@ func TestPostOnRedirectDifferentHost(t *testing.T) { func TestPostOnRedirectStatusFound(t *testing.T) { t.Parallel() - CatchLogForTest(t) + log := GetLoggerForTest(t) require := require.New(t) assert := assert.New(t) r := mux.NewRouter() @@ -177,7 +177,7 @@ func TestPostOnRedirectStatusFound(t *testing.T) { if u.Scheme == "http" { config.AddOption("backend", "allowhttp", "true") } - client, err := NewBackendClient(config, 1, "0.0", nil) + client, err := NewBackendClient(log, config, 1, "0.0", nil) require.NoError(err) ctx := context.Background() @@ -193,7 +193,7 @@ func TestPostOnRedirectStatusFound(t *testing.T) { func TestHandleThrottled(t *testing.T) { t.Parallel() - CatchLogForTest(t) + log := GetLoggerForTest(t) require := require.New(t) assert := assert.New(t) r := mux.NewRouter() @@ -212,7 +212,7 @@ func TestHandleThrottled(t *testing.T) { if u.Scheme == "http" { config.AddOption("backend", "allowhttp", "true") } - client, err := NewBackendClient(config, 1, "0.0", nil) + client, err := NewBackendClient(log, config, 1, "0.0", nil) require.NoError(err) ctx := context.Background() diff --git a/backend_configuration.go b/backend_configuration.go index fa6b45b8..59184296 100644 --- a/backend_configuration.go +++ b/backend_configuration.go @@ -28,6 +28,7 @@ import ( "sync" "github.com/dlintw/goconf" + "go.uber.org/zap" ) const ( @@ -140,6 +141,7 @@ type BackendStorage interface { } type backendStorageCommon struct { + log *zap.Logger mu sync.RWMutex backends map[string][]*Backend } @@ -188,7 +190,7 @@ type BackendConfiguration struct { storage BackendStorage } -func NewBackendConfiguration(config *goconf.ConfigFile, etcdClient *EtcdClient) (*BackendConfiguration, error) { +func NewBackendConfiguration(log *zap.Logger, config *goconf.ConfigFile, etcdClient *EtcdClient) (*BackendConfiguration, error) { backendType, _ := config.GetString("backend", "backendtype") if backendType == "" { backendType = DefaultBackendType @@ -200,9 +202,9 @@ func NewBackendConfiguration(config *goconf.ConfigFile, etcdClient *EtcdClient) var err error switch backendType { case BackendTypeStatic: - storage, err = NewBackendStorageStatic(config) + storage, err = NewBackendStorageStatic(log, config) case BackendTypeEtcd: - storage, err = NewBackendStorageEtcd(config, etcdClient) + storage, err = NewBackendStorageEtcd(log, config, etcdClient) default: err = fmt.Errorf("unknown backend type: %s", backendType) } diff --git a/backend_configuration_test.go b/backend_configuration_test.go index e467cbd8..24b99fc8 100644 --- a/backend_configuration_test.go +++ b/backend_configuration_test.go @@ -85,7 +85,7 @@ func testBackends(t *testing.T, config *BackendConfiguration, valid_urls [][]str } func TestIsUrlAllowed_Compat(t *testing.T) { - CatchLogForTest(t) + log := GetLoggerForTest(t) // Old-style configuration valid_urls := []string{ "http://domain.invalid", @@ -100,13 +100,13 @@ func TestIsUrlAllowed_Compat(t *testing.T) { config.AddOption("backend", "allowed", "domain.invalid") config.AddOption("backend", "allowhttp", "true") config.AddOption("backend", "secret", string(testBackendSecret)) - cfg, err := NewBackendConfiguration(config, nil) + cfg, err := NewBackendConfiguration(log, config, nil) require.NoError(t, err) testUrls(t, cfg, valid_urls, invalid_urls) } func TestIsUrlAllowed_CompatForceHttps(t *testing.T) { - CatchLogForTest(t) + log := GetLoggerForTest(t) // Old-style configuration, force HTTPS valid_urls := []string{ "https://domain.invalid", @@ -120,13 +120,13 @@ func TestIsUrlAllowed_CompatForceHttps(t *testing.T) { config := goconf.NewConfigFile() config.AddOption("backend", "allowed", "domain.invalid") config.AddOption("backend", "secret", string(testBackendSecret)) - cfg, err := NewBackendConfiguration(config, nil) + cfg, err := NewBackendConfiguration(log, config, nil) require.NoError(t, err) testUrls(t, cfg, valid_urls, invalid_urls) } func TestIsUrlAllowed(t *testing.T) { - CatchLogForTest(t) + log := GetLoggerForTest(t) valid_urls := [][]string{ {"https://domain.invalid/foo", string(testBackendSecret) + "-foo"}, {"https://domain.invalid/foo/", string(testBackendSecret) + "-foo"}, @@ -164,13 +164,13 @@ func TestIsUrlAllowed(t *testing.T) { config.AddOption("baz", "secret", string(testBackendSecret)+"-baz") config.AddOption("lala", "url", "https://otherdomain.invalid/") config.AddOption("lala", "secret", string(testBackendSecret)+"-lala") - cfg, err := NewBackendConfiguration(config, nil) + cfg, err := NewBackendConfiguration(log, config, nil) require.NoError(t, err) testBackends(t, cfg, valid_urls, invalid_urls) } func TestIsUrlAllowed_EmptyAllowlist(t *testing.T) { - CatchLogForTest(t) + log := GetLoggerForTest(t) valid_urls := []string{} invalid_urls := []string{ "http://domain.invalid", @@ -180,13 +180,13 @@ func TestIsUrlAllowed_EmptyAllowlist(t *testing.T) { config := goconf.NewConfigFile() config.AddOption("backend", "allowed", "") config.AddOption("backend", "secret", string(testBackendSecret)) - cfg, err := NewBackendConfiguration(config, nil) + cfg, err := NewBackendConfiguration(log, config, nil) require.NoError(t, err) testUrls(t, cfg, valid_urls, invalid_urls) } func TestIsUrlAllowed_AllowAll(t *testing.T) { - CatchLogForTest(t) + log := GetLoggerForTest(t) valid_urls := []string{ "http://domain.invalid", "https://domain.invalid", @@ -199,7 +199,7 @@ func TestIsUrlAllowed_AllowAll(t *testing.T) { config.AddOption("backend", "allowall", "true") config.AddOption("backend", "allowed", "") config.AddOption("backend", "secret", string(testBackendSecret)) - cfg, err := NewBackendConfiguration(config, nil) + cfg, err := NewBackendConfiguration(log, config, nil) require.NoError(t, err) testUrls(t, cfg, valid_urls, invalid_urls) } @@ -210,7 +210,6 @@ type ParseBackendIdsTestcase struct { } func TestParseBackendIds(t *testing.T) { - CatchLogForTest(t) testcases := []ParseBackendIdsTestcase{ {"", nil}, {"backend1", []string{"backend1"}}, @@ -229,7 +228,7 @@ func TestParseBackendIds(t *testing.T) { } func TestBackendReloadNoChange(t *testing.T) { - CatchLogForTest(t) + log := GetLoggerForTest(t) require := require.New(t) current := testutil.ToFloat64(statsBackendsCurrent) original_config := goconf.NewConfigFile() @@ -239,7 +238,7 @@ func TestBackendReloadNoChange(t *testing.T) { original_config.AddOption("backend1", "secret", string(testBackendSecret)+"-backend1") original_config.AddOption("backend2", "url", "http://domain2.invalid") original_config.AddOption("backend2", "secret", string(testBackendSecret)+"-backend2") - o_cfg, err := NewBackendConfiguration(original_config, nil) + o_cfg, err := NewBackendConfiguration(log, original_config, nil) require.NoError(err) checkStatsValue(t, statsBackendsCurrent, current+2) @@ -250,7 +249,7 @@ func TestBackendReloadNoChange(t *testing.T) { new_config.AddOption("backend1", "secret", string(testBackendSecret)+"-backend1") new_config.AddOption("backend2", "url", "http://domain2.invalid") new_config.AddOption("backend2", "secret", string(testBackendSecret)+"-backend2") - n_cfg, err := NewBackendConfiguration(new_config, nil) + n_cfg, err := NewBackendConfiguration(log, new_config, nil) require.NoError(err) checkStatsValue(t, statsBackendsCurrent, current+4) @@ -262,7 +261,7 @@ func TestBackendReloadNoChange(t *testing.T) { } func TestBackendReloadChangeExistingURL(t *testing.T) { - CatchLogForTest(t) + log := GetLoggerForTest(t) require := require.New(t) current := testutil.ToFloat64(statsBackendsCurrent) original_config := goconf.NewConfigFile() @@ -272,7 +271,7 @@ func TestBackendReloadChangeExistingURL(t *testing.T) { original_config.AddOption("backend1", "secret", string(testBackendSecret)+"-backend1") original_config.AddOption("backend2", "url", "http://domain2.invalid") original_config.AddOption("backend2", "secret", string(testBackendSecret)+"-backend2") - o_cfg, err := NewBackendConfiguration(original_config, nil) + o_cfg, err := NewBackendConfiguration(log, original_config, nil) require.NoError(err) checkStatsValue(t, statsBackendsCurrent, current+2) @@ -284,7 +283,7 @@ func TestBackendReloadChangeExistingURL(t *testing.T) { new_config.AddOption("backend1", "sessionlimit", "10") new_config.AddOption("backend2", "url", "http://domain2.invalid") new_config.AddOption("backend2", "secret", string(testBackendSecret)+"-backend2") - n_cfg, err := NewBackendConfiguration(new_config, nil) + n_cfg, err := NewBackendConfiguration(log, new_config, nil) require.NoError(err) checkStatsValue(t, statsBackendsCurrent, current+4) @@ -300,7 +299,7 @@ func TestBackendReloadChangeExistingURL(t *testing.T) { } func TestBackendReloadChangeSecret(t *testing.T) { - CatchLogForTest(t) + log := GetLoggerForTest(t) require := require.New(t) current := testutil.ToFloat64(statsBackendsCurrent) original_config := goconf.NewConfigFile() @@ -310,7 +309,7 @@ func TestBackendReloadChangeSecret(t *testing.T) { original_config.AddOption("backend1", "secret", string(testBackendSecret)+"-backend1") original_config.AddOption("backend2", "url", "http://domain2.invalid") original_config.AddOption("backend2", "secret", string(testBackendSecret)+"-backend2") - o_cfg, err := NewBackendConfiguration(original_config, nil) + o_cfg, err := NewBackendConfiguration(log, original_config, nil) require.NoError(err) checkStatsValue(t, statsBackendsCurrent, current+2) @@ -321,7 +320,7 @@ func TestBackendReloadChangeSecret(t *testing.T) { new_config.AddOption("backend1", "secret", string(testBackendSecret)+"-backend3") new_config.AddOption("backend2", "url", "http://domain2.invalid") new_config.AddOption("backend2", "secret", string(testBackendSecret)+"-backend2") - n_cfg, err := NewBackendConfiguration(new_config, nil) + n_cfg, err := NewBackendConfiguration(log, new_config, nil) require.NoError(err) checkStatsValue(t, statsBackendsCurrent, current+4) @@ -336,7 +335,7 @@ func TestBackendReloadChangeSecret(t *testing.T) { } func TestBackendReloadAddBackend(t *testing.T) { - CatchLogForTest(t) + log := GetLoggerForTest(t) require := require.New(t) current := testutil.ToFloat64(statsBackendsCurrent) original_config := goconf.NewConfigFile() @@ -344,7 +343,7 @@ func TestBackendReloadAddBackend(t *testing.T) { original_config.AddOption("backend", "allowall", "false") original_config.AddOption("backend1", "url", "http://domain1.invalid") original_config.AddOption("backend1", "secret", string(testBackendSecret)+"-backend1") - o_cfg, err := NewBackendConfiguration(original_config, nil) + o_cfg, err := NewBackendConfiguration(log, original_config, nil) require.NoError(err) checkStatsValue(t, statsBackendsCurrent, current+1) @@ -356,7 +355,7 @@ func TestBackendReloadAddBackend(t *testing.T) { new_config.AddOption("backend2", "url", "http://domain2.invalid") new_config.AddOption("backend2", "secret", string(testBackendSecret)+"-backend2") new_config.AddOption("backend2", "sessionlimit", "10") - n_cfg, err := NewBackendConfiguration(new_config, nil) + n_cfg, err := NewBackendConfiguration(log, new_config, nil) require.NoError(err) checkStatsValue(t, statsBackendsCurrent, current+3) @@ -374,7 +373,7 @@ func TestBackendReloadAddBackend(t *testing.T) { } func TestBackendReloadRemoveHost(t *testing.T) { - CatchLogForTest(t) + log := GetLoggerForTest(t) require := require.New(t) current := testutil.ToFloat64(statsBackendsCurrent) original_config := goconf.NewConfigFile() @@ -384,7 +383,7 @@ func TestBackendReloadRemoveHost(t *testing.T) { original_config.AddOption("backend1", "secret", string(testBackendSecret)+"-backend1") original_config.AddOption("backend2", "url", "http://domain2.invalid") original_config.AddOption("backend2", "secret", string(testBackendSecret)+"-backend2") - o_cfg, err := NewBackendConfiguration(original_config, nil) + o_cfg, err := NewBackendConfiguration(log, original_config, nil) require.NoError(err) checkStatsValue(t, statsBackendsCurrent, current+2) @@ -393,7 +392,7 @@ func TestBackendReloadRemoveHost(t *testing.T) { new_config.AddOption("backend", "allowall", "false") new_config.AddOption("backend1", "url", "http://domain1.invalid") new_config.AddOption("backend1", "secret", string(testBackendSecret)+"-backend1") - n_cfg, err := NewBackendConfiguration(new_config, nil) + n_cfg, err := NewBackendConfiguration(log, new_config, nil) require.NoError(err) checkStatsValue(t, statsBackendsCurrent, current+3) @@ -409,7 +408,7 @@ func TestBackendReloadRemoveHost(t *testing.T) { } func TestBackendReloadRemoveBackendFromSharedHost(t *testing.T) { - CatchLogForTest(t) + log := GetLoggerForTest(t) require := require.New(t) current := testutil.ToFloat64(statsBackendsCurrent) original_config := goconf.NewConfigFile() @@ -419,7 +418,7 @@ func TestBackendReloadRemoveBackendFromSharedHost(t *testing.T) { original_config.AddOption("backend1", "secret", string(testBackendSecret)+"-backend1") original_config.AddOption("backend2", "url", "http://domain1.invalid/bar/") original_config.AddOption("backend2", "secret", string(testBackendSecret)+"-backend2") - o_cfg, err := NewBackendConfiguration(original_config, nil) + o_cfg, err := NewBackendConfiguration(log, original_config, nil) require.NoError(err) checkStatsValue(t, statsBackendsCurrent, current+2) @@ -428,7 +427,7 @@ func TestBackendReloadRemoveBackendFromSharedHost(t *testing.T) { new_config.AddOption("backend", "allowall", "false") new_config.AddOption("backend1", "url", "http://domain1.invalid/foo/") new_config.AddOption("backend1", "secret", string(testBackendSecret)+"-backend1") - n_cfg, err := NewBackendConfiguration(new_config, nil) + n_cfg, err := NewBackendConfiguration(log, new_config, nil) require.NoError(err) checkStatsValue(t, statsBackendsCurrent, current+3) @@ -463,7 +462,7 @@ func mustParse(s string) *url.URL { func TestBackendConfiguration_Etcd(t *testing.T) { t.Parallel() - CatchLogForTest(t) + log := GetLoggerForTest(t) require := require.New(t) assert := assert.New(t) etcd, client := NewEtcdClientForTest(t) @@ -478,7 +477,7 @@ func TestBackendConfiguration_Etcd(t *testing.T) { config.AddOption("backend", "backendtype", "etcd") config.AddOption("backend", "backendprefix", "/backends") - cfg, err := NewBackendConfiguration(config, client) + cfg, err := NewBackendConfiguration(log, config, client) require.NoError(err) defer cfg.Close() @@ -574,7 +573,7 @@ func TestBackendConfiguration_Etcd(t *testing.T) { func TestBackendCommonSecret(t *testing.T) { t.Parallel() - CatchLogForTest(t) + log := GetLoggerForTest(t) require := require.New(t) assert := assert.New(t) u1, err := url.Parse("http://domain1.invalid") @@ -587,7 +586,7 @@ func TestBackendCommonSecret(t *testing.T) { original_config.AddOption("backend1", "url", u1.String()) original_config.AddOption("backend2", "url", u2.String()) original_config.AddOption("backend2", "secret", string(testBackendSecret)+"-backend2") - cfg, err := NewBackendConfiguration(original_config, nil) + cfg, err := NewBackendConfiguration(log, original_config, nil) require.NoError(err) if b1 := cfg.GetBackend(u1); assert.NotNil(b1) { diff --git a/backend_server.go b/backend_server.go index 016f0eb1..5438766e 100644 --- a/backend_server.go +++ b/backend_server.go @@ -31,7 +31,6 @@ import ( "errors" "fmt" "io" - "log" "net" "net/http" "net/url" @@ -45,6 +44,7 @@ import ( "github.com/dlintw/goconf" "github.com/gorilla/mux" "github.com/prometheus/client_golang/prometheus/promhttp" + "go.uber.org/zap" ) const ( @@ -56,6 +56,7 @@ const ( ) type BackendServer struct { + log *zap.Logger hub *Hub events AsyncEvents roomSessions RoomSessions @@ -72,7 +73,7 @@ type BackendServer struct { invalidSecret []byte } -func NewBackendServer(config *goconf.ConfigFile, hub *Hub, version string) (*BackendServer, error) { +func NewBackendServer(log *zap.Logger, config *goconf.ConfigFile, hub *Hub, version string) (*BackendServer, error) { turnapikey, _ := config.GetString("turn", "apikey") turnsecret, _ := config.GetString("turn", "secret") turnservers, _ := config.GetString("turn", "servers") @@ -95,10 +96,12 @@ func NewBackendServer(config *goconf.ConfigFile, hub *Hub, version string) (*Bac return nil, fmt.Errorf("need a shared TURN secret if TURN servers are configured") } - log.Printf("Using configured TURN API key") - log.Printf("Using configured shared TURN secret") + log.Info("Using configured TURN API key") + log.Info("Using configured shared TURN secret") for _, s := range turnserverslist { - log.Printf("Adding \"%s\" as TURN server", s) + log.Info("Adding TURN server", + zap.String("server", s), + ) } } @@ -109,10 +112,14 @@ func NewBackendServer(config *goconf.ConfigFile, hub *Hub, version string) (*Bac } if !statsAllowedIps.Empty() { - log.Printf("Only allowing access to the stats endpoint from %s", statsAllowed) + log.Info("Access to the stats endpoint only allowed from ips", + zap.Stringer("ips", statsAllowedIps), + ) } else { - log.Printf("No IPs configured for the stats endpoint, only allowing access from 127.0.0.1") statsAllowedIps = DefaultAllowedIps() + log.Info("No IPs configured for the stats endpoint, only allowing access from default ips", + zap.Stringer("ips", statsAllowedIps), + ) } invalidSecret := make([]byte, 32) @@ -121,6 +128,7 @@ func NewBackendServer(config *goconf.ConfigFile, hub *Hub, version string) (*Bac } result := &BackendServer{ + log: log, hub: hub, events: hub.events, roomSessions: hub.roomSessions, @@ -143,14 +151,21 @@ func (b *BackendServer) Reload(config *goconf.ConfigFile) { statsAllowed, _ := config.GetString("stats", "allowed_ips") if statsAllowedIps, err := ParseAllowedIps(statsAllowed); err == nil { if !statsAllowedIps.Empty() { - log.Printf("Only allowing access to the stats endpoint from %s", statsAllowed) + b.log.Info("Access to the stats endpoint only allowed from ips", + zap.Stringer("ips", statsAllowedIps), + ) } else { - log.Printf("No IPs configured for the stats endpoint, only allowing access from 127.0.0.1") statsAllowedIps = DefaultAllowedIps() + b.log.Info("No IPs configured for the stats endpoint, only allowing access from default ips", + zap.Stringer("ips", statsAllowedIps), + ) } b.statsAllowedIps.Store(statsAllowedIps) } else { - log.Printf("Error parsing allowed stats ips from \"%s\": %s", statsAllowedIps, err) + b.log.Error("Error parsing allowed stats ips", + zap.String("allowed", statsAllowed), + zap.Error(err), + ) } } @@ -246,7 +261,9 @@ func (b *BackendServer) getTurnCredentials(w http.ResponseWriter, r *http.Reques data, err := json.Marshal(result) if err != nil { - log.Printf("Could not serialize TURN credentials: %s", err) + b.log.Error("Could not serialize TURN credentials", + zap.Error(err), + ) w.WriteHeader(http.StatusInternalServerError) io.WriteString(w, "Could not serialize credentials.") // nolint return @@ -273,7 +290,9 @@ func (b *BackendServer) parseRequestBody(f func(http.ResponseWriter, *http.Reque } ct := r.Header.Get("Content-Type") if !strings.HasPrefix(ct, "application/json") { - log.Printf("Received unsupported content-type: %s", ct) + b.log.Warn("Received unsupported content-type", + zap.String("contenttype", ct), + ) http.Error(w, "Unsupported Content-Type", http.StatusBadRequest) return } @@ -286,7 +305,9 @@ func (b *BackendServer) parseRequestBody(f func(http.ResponseWriter, *http.Reque body, err := io.ReadAll(r.Body) if err != nil { - log.Println("Error reading body: ", err) + b.log.Error("Error reading body", + zap.Error(err), + ) http.Error(w, "Could not read body", http.StatusBadRequest) return } @@ -310,9 +331,16 @@ func (b *BackendServer) sendRoomInvite(roomid string, backend *Backend, userids }, }, } + log := b.log.With( + zap.String("room", roomid), + zap.String("backend", backend.Id()), + ) for _, userid := range userids { if err := b.events.PublishUserMessage(userid, backend, msg); err != nil { - log.Printf("Could not publish room invite for user %s in backend %s: %s", userid, backend.Id(), err) + log.Error("Could not publish room invite", + zap.String("userid", userid), + zap.Error(err), + ) } } } @@ -334,9 +362,16 @@ func (b *BackendServer) sendRoomDisinvite(roomid string, backend *Backend, reaso }, }, } + log := b.log.With( + zap.String("room", roomid), + zap.String("backend", backend.Id()), + ) for _, userid := range userids { if err := b.events.PublishUserMessage(userid, backend, msg); err != nil { - log.Printf("Could not publish room disinvite for user %s in backend %s: %s", userid, backend.Id(), err) + log.Error("Could not publish room disinvite", + zap.String("userid", userid), + zap.Error(err), + ) } } @@ -354,10 +389,16 @@ func (b *BackendServer) sendRoomDisinvite(roomid string, backend *Backend, reaso go func(sessionid string) { defer wg.Done() if sid, err := b.lookupByRoomSessionId(ctx, sessionid, nil); err != nil { - log.Printf("Could not lookup by room session %s: %s", sessionid, err) + log.Error("Could not lookup by room session", + zap.String("roomsessionid", sessionid), + zap.Error(err), + ) } else if sid != "" { if err := b.events.PublishSessionMessage(sid, backend, msg); err != nil { - log.Printf("Could not publish room disinvite for session %s: %s", sid, err) + log.Error("Could not publish room disinvite", + zap.String("sessionid", sid), + zap.Error(err), + ) } } }(sessionid) @@ -380,6 +421,10 @@ func (b *BackendServer) sendRoomUpdate(roomid string, backend *Backend, notified }, }, } + log := b.log.With( + zap.String("room", roomid), + zap.String("backend", backend.Id()), + ) notified := make(map[string]bool) for _, userid := range notified_userids { notified[userid] = true @@ -391,14 +436,19 @@ func (b *BackendServer) sendRoomUpdate(roomid string, backend *Backend, notified } if err := b.events.PublishUserMessage(userid, backend, msg); err != nil { - log.Printf("Could not publish room update for user %s in backend %s: %s", userid, backend.Id(), err) + log.Error("Could not publish room update", + zap.String("userid", userid), + zap.Error(err), + ) } } } func (b *BackendServer) lookupByRoomSessionId(ctx context.Context, roomSessionId string, cache *ConcurrentStringStringMap) (string, error) { if roomSessionId == sessionIdNotInMeeting { - log.Printf("Trying to lookup empty room session id: %s", roomSessionId) + b.log.Debug("Trying to lookup empty room session id", + zap.String("roomsessionid", roomSessionId), + ) return "", nil } @@ -435,13 +485,17 @@ func (b *BackendServer) fixupUserSessions(ctx context.Context, cache *Concurrent roomSessionId, ok := roomSessionIdOb.(string) if !ok { - log.Printf("User %+v has invalid room session id, ignoring", user) + b.log.Warn("User has invalid room session id, ignoring", + zap.Any("user", user), + ) delete(user, "sessionId") continue } if roomSessionId == sessionIdNotInMeeting { - log.Printf("User %+v is not in the meeting, ignoring", user) + b.log.Warn("User is not in the meeting, ignoring", + zap.Any("user", user), + ) delete(user, "sessionId") continue } @@ -450,7 +504,10 @@ func (b *BackendServer) fixupUserSessions(ctx context.Context, cache *Concurrent go func(roomSessionId string, u map[string]interface{}) { defer wg.Done() if sessionId, err := b.lookupByRoomSessionId(ctx, roomSessionId, cache); err != nil { - log.Printf("Could not lookup by room session %s: %s", roomSessionId, err) + b.log.Error("Could not lookup by room session", + zap.String("roomsessionid", roomSessionId), + zap.Error(err), + ) delete(u, "sessionId") } else if sessionId != "" { u["sessionId"] = sessionId @@ -518,16 +575,26 @@ loop: } sessionId := user["sessionId"].(string) + log := b.log.With( + zap.String("sessionid", sessionId), + ) permissionsList, ok := permissionsInterface.([]interface{}) if !ok { - log.Printf("Received invalid permissions %+v (%s) for session %s", permissionsInterface, reflect.TypeOf(permissionsInterface), sessionId) + log.Warn("Received invalid permissions for session, ignoring", + zap.Any("permissions", permissionsInterface), + zap.Any("type", reflect.TypeOf(permissionsInterface)), + ) continue } var permissions []Permission for idx, ob := range permissionsList { permission, ok := ob.(string) if !ok { - log.Printf("Received invalid permission at position %d %+v (%s) for session %s", idx, ob, reflect.TypeOf(ob), sessionId) + log.Warn("Received invalid permission at position for session, ignoring", + zap.Int("index", idx), + zap.Any("permission", ob), + zap.Any("type", reflect.TypeOf(ob)), + ) continue loop } permissions = append(permissions, Permission(permission)) @@ -541,7 +608,10 @@ loop: Permissions: permissions, } if err := b.events.PublishSessionMessage(sessionId, backend, message); err != nil { - log.Printf("Could not send permissions update (%+v) to session %s: %s", permissions, sessionId, err) + log.Error("Could not send permissions update", + zap.Any("permissions", permissions), + zap.Error(err), + ) } }(sessionId, permissions) } @@ -593,7 +663,10 @@ func (b *BackendServer) sendRoomSwitchTo(roomid string, backend *Backend, reques go func(roomSessionId string) { defer wg.Done() if sessionId, err := b.lookupByRoomSessionId(ctx, roomSessionId, nil); err != nil { - log.Printf("Could not lookup by room session %s: %s", roomSessionId, err) + b.log.Error("Could not lookup by room session", + zap.String("roomsessionid", roomSessionId), + zap.Error(err), + ) } else if sessionId != "" { mu.Lock() defer mu.Unlock() @@ -631,7 +704,10 @@ func (b *BackendServer) sendRoomSwitchTo(roomid string, backend *Backend, reques go func(roomSessionId string, details json.RawMessage) { defer wg.Done() if sessionId, err := b.lookupByRoomSessionId(ctx, roomSessionId, nil); err != nil { - log.Printf("Could not lookup by room session %s: %s", roomSessionId, err) + b.log.Error("Could not lookup by room session", + zap.String("roomsessionid", roomSessionId), + zap.Error(err), + ) } else if sessionId != "" { mu.Lock() defer mu.Unlock() @@ -762,7 +838,9 @@ func (b *BackendServer) startDialout(roomid string, backend *Backend, backendUrl return returnDialoutError(http.StatusBadGateway, dialout.Error) case "status": if dialout.Status.Status != DialoutStatusAccepted { - log.Printf("Received unsupported dialout status when triggering dialout: %+v", dialout) + b.log.Warn("Received unsupported dialout status when triggering dialout", + zap.Any("status", dialout), + ) return returnDialoutError(http.StatusBadGateway, NewError("unsupported_status", "Unsupported dialout status received.")) } @@ -774,7 +852,9 @@ func (b *BackendServer) startDialout(roomid string, backend *Backend, backendUrl }, nil } - log.Printf("Received unsupported dialout type when triggering dialout: %+v", dialout) + b.log.Warn("Received unsupported dialout type when triggering dialout", + zap.Any("status", dialout), + ) return returnDialoutError(http.StatusBadGateway, NewError("unsupported_type", "Unsupported dialout type received.")) } @@ -784,7 +864,9 @@ func (b *BackendServer) roomHandler(w http.ResponseWriter, r *http.Request, body http.Error(w, "Too many requests", http.StatusTooManyRequests) return } else if err != nil { - log.Printf("Error checking for bruteforce: %s", err) + b.log.Error("Error checking for bruteforce", + zap.Error(err), + ) http.Error(w, "Could not check for bruteforce", http.StatusInternalServerError) return } @@ -837,7 +919,10 @@ func (b *BackendServer) roomHandler(w http.ResponseWriter, r *http.Request, body var request BackendServerRoomRequest if err := json.Unmarshal(body, &request); err != nil { - log.Printf("Error decoding body %s: %s", string(body), err) + b.log.Error("Error decoding body", + zap.Binary("body", body), + zap.Error(err), + ) http.Error(w, "Could not read body", http.StatusBadRequest) return } @@ -882,7 +967,11 @@ func (b *BackendServer) roomHandler(w http.ResponseWriter, r *http.Request, body } if err != nil { - log.Printf("Error processing %s for room %s: %s", string(body), roomid, err) + b.log.Error("Error processing room request", + zap.ByteString("request", body), + zap.String("room", roomid), + zap.Error(err), + ) http.Error(w, "Error while processing", http.StatusInternalServerError) return } @@ -898,7 +987,10 @@ func (b *BackendServer) roomHandler(w http.ResponseWriter, r *http.Request, body } responseData, err = json.Marshal(response) if err != nil { - log.Printf("Could not serialize backend response %+v: %s", response, err) + b.log.Error("Could not serialize backend response", + zap.Any("response", response), + zap.Error(err), + ) responseStatus = http.StatusInternalServerError responseData = []byte("{\"error\":\"could_not_serialize\"}") } @@ -936,7 +1028,10 @@ func (b *BackendServer) statsHandler(w http.ResponseWriter, r *http.Request) { stats := b.hub.GetStats() statsData, err := json.MarshalIndent(stats, "", " ") if err != nil { - log.Printf("Could not serialize stats %+v: %s", stats, err) + b.log.Error("Could not serialize stats", + zap.Any("stats", stats), + zap.Error(err), + ) http.Error(w, "Internal server error", http.StatusInternalServerError) return } diff --git a/backend_server_test.go b/backend_server_test.go index 858b7f3c..62236a1f 100644 --- a/backend_server_test.go +++ b/backend_server_test.go @@ -68,6 +68,7 @@ func CreateBackendServerForTestWithTurn(t *testing.T) (*goconf.ConfigFile, *Back func CreateBackendServerForTestFromConfig(t *testing.T, config *goconf.ConfigFile) (*goconf.ConfigFile, *BackendServer, AsyncEvents, *Hub, *mux.Router, *httptest.Server) { require := require.New(t) + log := GetLoggerForTest(t) r := mux.NewRouter() registerBackendHandler(t, r) @@ -97,9 +98,9 @@ func CreateBackendServerForTestFromConfig(t *testing.T, config *goconf.ConfigFil config.AddOption("clients", "internalsecret", string(testInternalSecret)) config.AddOption("geoip", "url", "none") events := getAsyncEventsForTest(t) - hub, err := NewHub(config, events, nil, nil, nil, r, "no-version") + hub, err := NewHub(log, config, events, nil, nil, nil, r, "no-version") require.NoError(err) - b, err := NewBackendServer(config, hub, "no-version") + b, err := NewBackendServer(log, config, hub, "no-version") require.NoError(err) require.NoError(b.Start(r)) @@ -121,6 +122,7 @@ func CreateBackendServerWithClusteringForTest(t *testing.T) (*BackendServer, *Ba func CreateBackendServerWithClusteringForTestFromConfig(t *testing.T, config1 *goconf.ConfigFile, config2 *goconf.ConfigFile) (*BackendServer, *BackendServer, *Hub, *Hub, *httptest.Server, *httptest.Server) { require := require.New(t) + log := GetLoggerForTest(t) r1 := mux.NewRouter() registerBackendHandler(t, r1) @@ -156,13 +158,13 @@ func CreateBackendServerWithClusteringForTestFromConfig(t *testing.T, config1 *g config1.AddOption("clients", "internalsecret", string(testInternalSecret)) config1.AddOption("geoip", "url", "none") - events1, err := NewAsyncEvents(nats) + events1, err := NewAsyncEvents(log, nats) require.NoError(err) t.Cleanup(func() { events1.Close() }) client1, _ := NewGrpcClientsForTest(t, addr2) - hub1, err := NewHub(config1, events1, grpcServer1, client1, nil, r1, "no-version") + hub1, err := NewHub(log, config1, events1, grpcServer1, client1, nil, r1, "no-version") require.NoError(err) if config2 == nil { @@ -179,19 +181,19 @@ func CreateBackendServerWithClusteringForTestFromConfig(t *testing.T, config1 *g config2.AddOption("sessions", "blockkey", "09876543210987654321098765432109") config2.AddOption("clients", "internalsecret", string(testInternalSecret)) config2.AddOption("geoip", "url", "none") - events2, err := NewAsyncEvents(nats) + events2, err := NewAsyncEvents(log, nats) require.NoError(err) t.Cleanup(func() { events2.Close() }) client2, _ := NewGrpcClientsForTest(t, addr1) - hub2, err := NewHub(config2, events2, grpcServer2, client2, nil, r2, "no-version") + hub2, err := NewHub(log, config2, events2, grpcServer2, client2, nil, r2, "no-version") require.NoError(err) - b1, err := NewBackendServer(config1, hub1, "no-version") + b1, err := NewBackendServer(log, config1, hub1, "no-version") require.NoError(err) require.NoError(b1.Start(r1)) - b2, err := NewBackendServer(config2, hub2, "no-version") + b2, err := NewBackendServer(log, config2, hub2, "no-version") require.NoError(err) require.NoError(b2.Start(r2)) @@ -252,7 +254,6 @@ func expectRoomlistEvent(ch chan *AsyncMessage, msgType string) (*EventServerMes func TestBackendServer_NoAuth(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) _, _, _, _, _, server := CreateBackendServerForTest(t) @@ -274,7 +275,6 @@ func TestBackendServer_NoAuth(t *testing.T) { func TestBackendServer_InvalidAuth(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) _, _, _, _, _, server := CreateBackendServerForTest(t) @@ -298,7 +298,6 @@ func TestBackendServer_InvalidAuth(t *testing.T) { func TestBackendServer_OldCompatAuth(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) _, _, _, _, _, server := CreateBackendServerForTest(t) @@ -341,7 +340,6 @@ func TestBackendServer_OldCompatAuth(t *testing.T) { func TestBackendServer_InvalidBody(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) _, _, _, _, _, server := CreateBackendServerForTest(t) @@ -358,7 +356,6 @@ func TestBackendServer_InvalidBody(t *testing.T) { func TestBackendServer_UnsupportedRequest(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) _, _, _, _, _, server := CreateBackendServerForTest(t) @@ -379,7 +376,6 @@ func TestBackendServer_UnsupportedRequest(t *testing.T) { } func TestBackendServer_RoomInvite(t *testing.T) { - CatchLogForTest(t) for _, backend := range eventBackendsForTest { t.Run(backend, func(t *testing.T) { t.Parallel() @@ -447,7 +443,6 @@ func RunTestBackendServer_RoomInvite(t *testing.T) { } func TestBackendServer_RoomDisinvite(t *testing.T) { - CatchLogForTest(t) for _, backend := range eventBackendsForTest { t.Run(backend, func(t *testing.T) { t.Parallel() @@ -538,7 +533,6 @@ func RunTestBackendServer_RoomDisinvite(t *testing.T) { func TestBackendServer_RoomDisinviteDifferentRooms(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) _, _, _, hub, _, server := CreateBackendServerForTest(t) @@ -629,7 +623,6 @@ func TestBackendServer_RoomDisinviteDifferentRooms(t *testing.T) { } func TestBackendServer_RoomUpdate(t *testing.T) { - CatchLogForTest(t) for _, backend := range eventBackendsForTest { t.Run(backend, func(t *testing.T) { t.Parallel() @@ -699,7 +692,6 @@ func RunTestBackendServer_RoomUpdate(t *testing.T) { } func TestBackendServer_RoomDelete(t *testing.T) { - CatchLogForTest(t) for _, backend := range eventBackendsForTest { t.Run(backend, func(t *testing.T) { t.Parallel() @@ -766,7 +758,6 @@ func RunTestBackendServer_RoomDelete(t *testing.T) { } func TestBackendServer_ParticipantsUpdatePermissions(t *testing.T) { - CatchLogForTest(t) for _, subtest := range clusteredTests { t.Run(subtest, func(t *testing.T) { t.Parallel() @@ -874,7 +865,6 @@ func TestBackendServer_ParticipantsUpdatePermissions(t *testing.T) { func TestBackendServer_ParticipantsUpdateEmptyPermissions(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) _, _, _, hub, _, server := CreateBackendServerForTest(t) @@ -943,7 +933,6 @@ func TestBackendServer_ParticipantsUpdateEmptyPermissions(t *testing.T) { func TestBackendServer_ParticipantsUpdateTimeout(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) _, _, _, hub, _, server := CreateBackendServerForTest(t) @@ -1119,7 +1108,6 @@ func TestBackendServer_ParticipantsUpdateTimeout(t *testing.T) { } func TestBackendServer_InCallAll(t *testing.T) { - CatchLogForTest(t) for _, subtest := range clusteredTests { t.Run(subtest, func(t *testing.T) { t.Parallel() @@ -1318,7 +1306,6 @@ func TestBackendServer_InCallAll(t *testing.T) { func TestBackendServer_RoomMessage(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) _, _, _, hub, _, server := CreateBackendServerForTest(t) @@ -1367,7 +1354,6 @@ func TestBackendServer_RoomMessage(t *testing.T) { func TestBackendServer_TurnCredentials(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) _, _, _, _, _, server := CreateBackendServerForTestWithTurn(t) @@ -1397,7 +1383,6 @@ func TestBackendServer_TurnCredentials(t *testing.T) { } func TestBackendServer_StatsAllowedIps(t *testing.T) { - CatchLogForTest(t) config := goconf.NewConfigFile() config.AddOption("app", "trustedproxies", "1.2.3.4") config.AddOption("stats", "allowed_ips", "127.0.0.1, 192.168.0.1, 192.168.1.1/24") @@ -1495,7 +1480,6 @@ func Test_IsNumeric(t *testing.T) { func TestBackendServer_DialoutNoSipBridge(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) _, _, _, hub, _, server := CreateBackendServerForTest(t) @@ -1539,7 +1523,6 @@ func TestBackendServer_DialoutNoSipBridge(t *testing.T) { func TestBackendServer_DialoutAccepted(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) _, _, _, hub, _, server := CreateBackendServerForTest(t) @@ -1626,7 +1609,6 @@ func TestBackendServer_DialoutAccepted(t *testing.T) { func TestBackendServer_DialoutAcceptedCompat(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) _, _, _, hub, _, server := CreateBackendServerForTest(t) @@ -1713,7 +1695,6 @@ func TestBackendServer_DialoutAcceptedCompat(t *testing.T) { func TestBackendServer_DialoutRejected(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) _, _, _, hub, _, server := CreateBackendServerForTest(t) diff --git a/backend_storage_etcd.go b/backend_storage_etcd.go index ce82beff..8b411fd1 100644 --- a/backend_storage_etcd.go +++ b/backend_storage_etcd.go @@ -26,12 +26,12 @@ import ( "encoding/json" "errors" "fmt" - "log" "net/url" "time" "github.com/dlintw/goconf" clientv3 "go.etcd.io/etcd/client/v3" + "go.uber.org/zap" ) type backendStorageEtcd struct { @@ -49,7 +49,7 @@ type backendStorageEtcd struct { closeFunc context.CancelFunc } -func NewBackendStorageEtcd(config *goconf.ConfigFile, etcdClient *EtcdClient) (BackendStorage, error) { +func NewBackendStorageEtcd(log *zap.Logger, config *goconf.ConfigFile, etcdClient *EtcdClient) (BackendStorage, error) { if etcdClient == nil || !etcdClient.IsConfigured() { return nil, fmt.Errorf("no etcd endpoints configured") } @@ -63,6 +63,7 @@ func NewBackendStorageEtcd(config *goconf.ConfigFile, etcdClient *EtcdClient) (B closeCtx, closeFunc := context.WithCancel(context.Background()) result := &backendStorageEtcd{ backendStorageCommon: backendStorageCommon{ + log: log, backends: make(map[string][]*Backend), }, etcdClient: etcdClient, @@ -119,9 +120,14 @@ func (s *backendStorageEtcd) EtcdClientCreated(client *EtcdClient) { if errors.Is(err, context.Canceled) { return } else if errors.Is(err, context.DeadlineExceeded) { - log.Printf("Timeout getting initial list of backends, retry in %s", backoff.NextWait()) + s.log.Warn("Timeout getting initial list of backends, retry", + zap.Duration("wait", backoff.NextWait()), + ) } else { - log.Printf("Could not get initial list of backends, retry in %s: %s", backoff.NextWait(), err) + s.log.Error("Could not get initial list of backends, retry", + zap.Duration("wait", backoff.NextWait()), + zap.Error(err), + ) } backoff.Wait(s.closeCtx) @@ -139,7 +145,11 @@ func (s *backendStorageEtcd) EtcdClientCreated(client *EtcdClient) { for s.closeCtx.Err() == nil { var err error if nextRevision, err = client.Watch(s.closeCtx, s.keyPrefix, nextRevision, s, clientv3.WithPrefix()); err != nil { - log.Printf("Error processing watch for %s (%s), retry in %s", s.keyPrefix, err, backoff.NextWait()) + s.log.Error("Error processing watch, retry", + zap.String("prefix", s.keyPrefix), + zap.Duration("wait", backoff.NextWait()), + zap.Error(err), + ) backoff.Wait(s.closeCtx) continue } @@ -148,7 +158,10 @@ func (s *backendStorageEtcd) EtcdClientCreated(client *EtcdClient) { backoff.Reset() prevRevision = nextRevision } else { - log.Printf("Processing watch for %s interrupted, retry in %s", s.keyPrefix, backoff.NextWait()) + s.log.Warn("Processing watch interrupted, retry", + zap.String("prefix", s.keyPrefix), + zap.Duration("wait", backoff.NextWait()), + ) backoff.Wait(s.closeCtx) } } @@ -168,13 +181,22 @@ func (s *backendStorageEtcd) getBackends(ctx context.Context, client *EtcdClient } func (s *backendStorageEtcd) EtcdKeyUpdated(client *EtcdClient, key string, data []byte, prevValue []byte) { + log := s.log.With( + zap.String("key", key), + ) var info BackendInformationEtcd if err := json.Unmarshal(data, &info); err != nil { - log.Printf("Could not decode backend information %s: %s", string(data), err) + log.Error("Could not decode backend information", + zap.ByteString("data", data), + zap.Error(err), + ) return } if err := info.CheckValid(); err != nil { - log.Printf("Received invalid backend information %s: %s", string(data), err) + log.Warn("Received invalid backend information", + zap.ByteString("data", data), + zap.Error(err), + ) return } @@ -200,7 +222,9 @@ func (s *backendStorageEtcd) EtcdKeyUpdated(client *EtcdClient, key string, data entries, found := s.backends[host] if !found { // Simple case, first backend for this host - log.Printf("Added backend %s (from %s)", info.Url, key) + log.Info("Added backend", + zap.String("url", info.Url), + ) s.backends[host] = []*Backend{backend} updateBackendStats(backend) statsBackendsCurrent.Inc() @@ -212,7 +236,9 @@ func (s *backendStorageEtcd) EtcdKeyUpdated(client *EtcdClient, key string, data replaced := false for idx, entry := range entries { if entry.id == key { - log.Printf("Updated backend %s (from %s)", info.Url, key) + log.Info("Updated backend", + zap.String("url", info.Url), + ) updateBackendStats(backend) entries[idx] = backend replaced = true @@ -222,7 +248,9 @@ func (s *backendStorageEtcd) EtcdKeyUpdated(client *EtcdClient, key string, data if !replaced { // New backend, add to list. - log.Printf("Added backend %s (from %s)", info.Url, key) + log.Info("Added backend", + zap.String("url", info.Url), + ) s.backends[host] = append(entries, backend) updateBackendStats(backend) statsBackendsCurrent.Inc() @@ -246,7 +274,10 @@ func (s *backendStorageEtcd) EtcdKeyDeleted(client *EtcdClient, key string, prev return } - log.Printf("Removing backend %s (from %s)", info.Url, key) + s.log.Info("Removing backend", + zap.String("key", key), + zap.String("url", info.Url), + ) newEntries := make([]*Backend, 0, len(entries)-1) for _, entry := range entries { if entry.id == key { diff --git a/backend_storage_etcd_test.go b/backend_storage_etcd_test.go index d9bd77c8..29cbb7ec 100644 --- a/backend_storage_etcd_test.go +++ b/backend_storage_etcd_test.go @@ -53,7 +53,7 @@ func (tl *testListener) EtcdClientCreated(client *EtcdClient) { } func Test_BackendStorageEtcdNoLeak(t *testing.T) { - CatchLogForTest(t) + log := GetLoggerForTest(t) ensureNoGoroutinesLeak(t, func(t *testing.T) { etcd, client := NewEtcdClientForTest(t) tl := &testListener{ @@ -67,7 +67,7 @@ func Test_BackendStorageEtcdNoLeak(t *testing.T) { config.AddOption("backend", "backendtype", "etcd") config.AddOption("backend", "backendprefix", "/backends") - cfg, err := NewBackendConfiguration(config, client) + cfg, err := NewBackendConfiguration(log, config, client) require.NoError(t, err) <-tl.closed diff --git a/backend_storage_static.go b/backend_storage_static.go index 4a60c3f3..bb33dade 100644 --- a/backend_storage_static.go +++ b/backend_storage_static.go @@ -22,12 +22,12 @@ package signaling import ( - "log" "net/url" "reflect" "strings" "github.com/dlintw/goconf" + "go.uber.org/zap" ) type backendStorageStatic struct { @@ -39,7 +39,7 @@ type backendStorageStatic struct { compatBackend *Backend } -func NewBackendStorageStatic(config *goconf.ConfigFile) (BackendStorage, error) { +func NewBackendStorageStatic(log *zap.Logger, config *goconf.ConfigFile) (BackendStorage, error) { allowAll, _ := config.GetBool("backend", "allowall") allowHttp, _ := config.GetBool("backend", "allowhttp") commonSecret, _ := config.GetString("backend", "secret") @@ -51,7 +51,7 @@ func NewBackendStorageStatic(config *goconf.ConfigFile) (BackendStorage, error) var compatBackend *Backend numBackends := 0 if allowAll { - log.Println("WARNING: All backend hostnames are allowed, only use for development!") + log.Warn("All backend hostnames are allowed, only use for development!") compatBackend = &Backend{ id: "compat", secret: []byte(commonSecret), @@ -62,15 +62,20 @@ func NewBackendStorageStatic(config *goconf.ConfigFile) (BackendStorage, error) sessionLimit: uint64(sessionLimit), } if sessionLimit > 0 { - log.Printf("Allow a maximum of %d sessions", sessionLimit) + log.Info("Allow a maximum of sessions", + zap.Int("limit", sessionLimit), + ) } updateBackendStats(compatBackend) numBackends++ } else if backendIds, _ := config.GetString("backend", "backends"); backendIds != "" { - for host, configuredBackends := range getConfiguredHosts(backendIds, config, commonSecret) { + for host, configuredBackends := range getConfiguredHosts(log, backendIds, config, commonSecret) { backends[host] = append(backends[host], configuredBackends...) for _, be := range configuredBackends { - log.Printf("Backend %s added for %s", be.id, be.url) + log.Info("Backend added", + zap.String("id", be.id), + zap.String("url", be.url), + ) updateBackendStats(be) } numBackends += len(configuredBackends) @@ -81,7 +86,9 @@ func NewBackendStorageStatic(config *goconf.ConfigFile) (BackendStorage, error) for _, u := range strings.Split(allowedUrls, ",") { u = strings.TrimSpace(u) if idx := strings.IndexByte(u, '/'); idx != -1 { - log.Printf("WARNING: Removing path from allowed hostname \"%s\", check your configuration!", u) + log.Warn("Removing path from allowed hostname, check your configuration!", + zap.String("hostname", u), + ) u = u[:idx] } if u != "" { @@ -90,7 +97,7 @@ func NewBackendStorageStatic(config *goconf.ConfigFile) (BackendStorage, error) } if len(allowMap) == 0 { - log.Println("WARNING: No backend hostnames are allowed, check your configuration!") + log.Warn("No backend hostnames are allowed, check your configuration!") } else { compatBackend = &Backend{ id: "compat", @@ -107,11 +114,15 @@ func NewBackendStorageStatic(config *goconf.ConfigFile) (BackendStorage, error) backends[host] = []*Backend{compatBackend} } if len(hosts) > 1 { - log.Println("WARNING: Using deprecated backend configuration. Please migrate the \"allowed\" setting to the new \"backends\" configuration.") + log.Warn("Using deprecated backend configuration. Please migrate the \"allowed\" setting to the new \"backends\" configuration.") } - log.Printf("Allowed backend hostnames: %s", hosts) + log.Info("Allowed backend hostnames", + zap.Any("hosts", hosts), + ) if sessionLimit > 0 { - log.Printf("Allow a maximum of %d sessions", sessionLimit) + log.Info("Allow a maximum of sessions", + zap.Int("limit", sessionLimit), + ) } updateBackendStats(compatBackend) numBackends++ @@ -119,12 +130,13 @@ func NewBackendStorageStatic(config *goconf.ConfigFile) (BackendStorage, error) } if numBackends == 0 { - log.Printf("WARNING: No backends configured, client connections will not be possible.") + log.Warn("No backends configured, client connections will not be possible.") } statsBackendsCurrent.Add(float64(numBackends)) return &backendStorageStatic{ backendStorageCommon: backendStorageCommon{ + log: log, backends: backends, }, @@ -140,7 +152,10 @@ func (s *backendStorageStatic) Close() { func (s *backendStorageStatic) RemoveBackendsForHost(host string) { if oldBackends := s.backends[host]; len(oldBackends) > 0 { for _, backend := range oldBackends { - log.Printf("Backend %s removed for %s", backend.id, backend.url) + s.log.Info("Backend removed", + zap.String("id", backend.id), + zap.String("url", backend.url), + ) deleteBackendStats(backend) } statsBackendsCurrent.Sub(float64(len(oldBackends))) @@ -161,7 +176,10 @@ func (s *backendStorageStatic) UpsertHost(host string, backends []*Backend) { found = true s.backends[host][existingIndex] = newBackend backends = append(backends[:index], backends[index+1:]...) - log.Printf("Backend %s updated for %s", newBackend.id, newBackend.url) + s.log.Info("Backend updated", + zap.String("id", newBackend.id), + zap.String("url", newBackend.url), + ) updateBackendStats(newBackend) break } @@ -169,7 +187,10 @@ func (s *backendStorageStatic) UpsertHost(host string, backends []*Backend) { } if !found { removed := s.backends[host][existingIndex] - log.Printf("Backend %s removed for %s", removed.id, removed.url) + s.log.Info("Backend removed", + zap.String("id", removed.id), + zap.String("url", removed.url), + ) s.backends[host] = append(s.backends[host][:existingIndex], s.backends[host][existingIndex+1:]...) deleteBackendStats(removed) statsBackendsCurrent.Dec() @@ -178,7 +199,10 @@ func (s *backendStorageStatic) UpsertHost(host string, backends []*Backend) { s.backends[host] = append(s.backends[host], backends...) for _, added := range backends { - log.Printf("Backend %s added for %s", added.id, added.url) + s.log.Info("Backend added", + zap.String("id", added.id), + zap.String("url", added.url), + ) updateBackendStats(added) } statsBackendsCurrent.Add(float64(len(backends))) @@ -203,12 +227,14 @@ func getConfiguredBackendIDs(backendIds string) (ids []string) { return ids } -func getConfiguredHosts(backendIds string, config *goconf.ConfigFile, commonSecret string) (hosts map[string][]*Backend) { +func getConfiguredHosts(log *zap.Logger, backendIds string, config *goconf.ConfigFile, commonSecret string) (hosts map[string][]*Backend) { hosts = make(map[string][]*Backend) for _, id := range getConfiguredBackendIDs(backendIds) { u, _ := config.GetString(id, "url") if u == "" { - log.Printf("Backend %s is missing or incomplete, skipping", id) + log.Warn("Backend is missing or incomplete, skipping", + zap.String("id", id), + ) continue } @@ -217,7 +243,11 @@ func getConfiguredHosts(backendIds string, config *goconf.ConfigFile, commonSecr } parsed, err := url.Parse(u) if err != nil { - log.Printf("Backend %s has an invalid url %s configured (%s), skipping", id, u, err) + log.Warn("Backend has an invalid url configured, skipping", + zap.String("id", id), + zap.String("url", u), + zap.Error(err), + ) continue } @@ -228,11 +258,15 @@ func getConfiguredHosts(backendIds string, config *goconf.ConfigFile, commonSecr secret, _ := config.GetString(id, "secret") if secret == "" && commonSecret != "" { - log.Printf("Backend %s has no own shared secret set, using common shared secret", id) + log.Info("Backend has no own shared secret set, using common shared secret", + zap.String("id", id), + ) secret = commonSecret } if u == "" || secret == "" { - log.Printf("Backend %s is missing or incomplete, skipping", id) + log.Warn("Backend is missing or incomplete, skipping", + zap.String("id", id), + ) continue } @@ -241,7 +275,10 @@ func getConfiguredHosts(backendIds string, config *goconf.ConfigFile, commonSecr sessionLimit = 0 } if sessionLimit > 0 { - log.Printf("Backend %s allows a maximum of %d sessions", id, sessionLimit) + log.Info("Backend allows a maximum of sessions", + zap.String("id", id), + zap.Int("limit", sessionLimit), + ) } maxStreamBitrate, err := config.GetInt(id, "maxstreambitrate") @@ -276,14 +313,14 @@ func (s *backendStorageStatic) Reload(config *goconf.ConfigFile) { defer s.mu.Unlock() if s.compatBackend != nil { - log.Println("Old-style configuration active, reload is not supported") + s.log.Info("Old-style configuration active, reload is not supported") return } commonSecret, _ := config.GetString("backend", "secret") if backendIds, _ := config.GetString("backend", "backends"); backendIds != "" { - configuredHosts := getConfiguredHosts(backendIds, config, commonSecret) + configuredHosts := getConfiguredHosts(s.log, backendIds, config, commonSecret) // remove backends that are no longer configured for hostname := range s.backends { diff --git a/capabilities.go b/capabilities.go index 45b7b546..5881500e 100644 --- a/capabilities.go +++ b/capabilities.go @@ -26,7 +26,6 @@ import ( "encoding/json" "errors" "io" - "log" "net/http" "net/url" "strings" @@ -34,6 +33,7 @@ import ( "time" "github.com/marcw/cachecontrol" + "go.uber.org/zap" ) const ( @@ -103,13 +103,17 @@ func (e *capabilitiesEntry) errorIfMustRevalidate(err error) error { return err } -func (e *capabilitiesEntry) update(response *http.Response, now time.Time) error { +func (e *capabilitiesEntry) update(log *zap.Logger, response *http.Response, now time.Time) error { e.mu.Lock() defer e.mu.Unlock() url := response.Request.URL e.etag = response.Header.Get("ETag") + log = log.With( + zap.Stringer("url", url), + ) + var maxAge time.Duration if cacheControl := response.Header.Get("Cache-Control"); cacheControl != "" { cc := cachecontrol.Parse(cacheControl) @@ -126,55 +130,81 @@ func (e *capabilitiesEntry) update(response *http.Response, now time.Time) error e.nextUpdate = now.Add(maxAge) if response.StatusCode == http.StatusNotModified { - log.Printf("Capabilities %+v from %s have not changed", e.capabilities, url) + log.Debug("Capabilities have not changed", + zap.Any("capabilities", e.capabilities), + ) return nil } else if response.StatusCode != http.StatusOK { - log.Printf("Received unexpected HTTP status from %s: %s", url, response.Status) + log.Error("Received unexpected HTTP status", + zap.String("status", response.Status), + ) return e.errorIfMustRevalidate(ErrUnexpectedHttpStatus) } ct := response.Header.Get("Content-Type") if !strings.HasPrefix(ct, "application/json") { - log.Printf("Received unsupported content-type from %s: %s (%s)", url, ct, response.Status) + log.Error("Received unsupported content-type", + zap.String("contenttype", ct), + zap.String("status", response.Status), + ) return e.errorIfMustRevalidate(ErrUnsupportedContentType) } body, err := io.ReadAll(response.Body) if err != nil { - log.Printf("Could not read response body from %s: %s", url, err) + log.Error("Could not read response body", + zap.Error(err), + ) return e.errorIfMustRevalidate(err) } var ocs OcsResponse if err := json.Unmarshal(body, &ocs); err != nil { - log.Printf("Could not decode OCS response %s from %s: %s", string(body), url, err) + log.Error("Could not decode OCS response", + zap.ByteString("body", body), + zap.Error(err), + ) return e.errorIfMustRevalidate(err) } else if ocs.Ocs == nil || len(ocs.Ocs.Data) == 0 { - log.Printf("Incomplete OCS response %s from %s", string(body), url) + log.Error("Incomplete OCS response", + zap.ByteString("body", body), + ) return e.errorIfMustRevalidate(ErrIncompleteResponse) } var capaResponse CapabilitiesResponse if err := json.Unmarshal(ocs.Ocs.Data, &capaResponse); err != nil { - log.Printf("Could not decode OCS response body %s from %s: %s", string(ocs.Ocs.Data), url, err) + log.Error("Could not decode OCS response body", + zap.ByteString("body", ocs.Ocs.Data), + zap.Error(err), + ) return e.errorIfMustRevalidate(err) } capaObj, found := capaResponse.Capabilities[AppNameSpreed] if !found || len(capaObj) == 0 { - log.Printf("No capabilities received for app spreed from %s: %+v", url, capaResponse) + log.Warn("No capabilities received for app", + zap.String("app", AppNameSpreed), + zap.Any("capabilities", capaResponse), + ) e.capabilities = nil return nil } var capa map[string]interface{} if err := json.Unmarshal(capaObj, &capa); err != nil { - log.Printf("Unsupported capabilities received for app spreed from %s: %+v", url, capaResponse) + log.Warn("Unsupported capabilities received for app", + zap.String("app", AppNameSpreed), + zap.Any("capabilities", capaResponse), + zap.Error(err), + ) e.capabilities = nil return nil } - log.Printf("Received capabilities %+v from %s", capa, url) + log.Info("Received capabilities", + zap.Any("capabilities", capa), + ) e.capabilities = capa return nil } @@ -187,7 +217,8 @@ func (e *capabilitiesEntry) GetCapabilities() map[string]interface{} { } type Capabilities struct { - mu sync.RWMutex + log *zap.Logger + mu sync.RWMutex // Can be overwritten by tests. getNow func() time.Time @@ -198,8 +229,9 @@ type Capabilities struct { nextInvalidate map[string]time.Time } -func NewCapabilities(version string, pool *HttpClientPool) (*Capabilities, error) { +func NewCapabilities(log *zap.Logger, version string, pool *HttpClientPool) (*Capabilities, error) { result := &Capabilities{ + log: log, getNow: time.Now, version: version, @@ -289,18 +321,26 @@ func (c *Capabilities) loadCapabilities(ctx context.Context, u *url.URL) (map[st capUrl.Path = capUrl.Path[:pos+11] + "/cloud/capabilities" } - log.Printf("Capabilities expired for %s, updating", capUrl.String()) + c.log.Debug("Capabilities expired, updating", + zap.Stringer("url", &capUrl), + ) client, pool, err := c.pool.Get(ctx, &capUrl) if err != nil { - log.Printf("Could not get client for host %s: %s", capUrl.Host, err) + c.log.Error("Could not get client", + zap.String("host", capUrl.Host), + zap.Error(err), + ) return nil, false, err } defer pool.Put(client) req, err := http.NewRequestWithContext(ctx, "GET", capUrl.String(), nil) if err != nil { - log.Printf("Could not create request to %s: %s", &capUrl, err) + c.log.Error("Could not create request", + zap.Stringer("url", &capUrl), + zap.Error(err), + ) return nil, false, err } req.Header.Set("Accept", "application/json") @@ -320,7 +360,7 @@ func (c *Capabilities) loadCapabilities(ctx context.Context, u *url.URL) (map[st entry = c.newCapabilitiesEntry(key) } - if err := entry.update(resp, c.getNow()); err != nil { + if err := entry.update(c.log, resp, c.getNow()); err != nil { return nil, false, err } @@ -330,7 +370,10 @@ func (c *Capabilities) loadCapabilities(ctx context.Context, u *url.URL) (map[st func (c *Capabilities) HasCapabilityFeature(ctx context.Context, u *url.URL, feature string) bool { caps, _, err := c.loadCapabilities(ctx, u) if err != nil { - log.Printf("Could not get capabilities for %s: %s", u, err) + c.log.Error("Could not get capabilities", + zap.Stringer("url", u), + zap.Error(err), + ) return false } @@ -341,7 +384,10 @@ func (c *Capabilities) HasCapabilityFeature(ctx context.Context, u *url.URL, fea features, ok := featuresInterface.([]interface{}) if !ok { - log.Printf("Invalid features list received for %s: %+v", u, featuresInterface) + c.log.Error("Invalid features list received", + zap.Stringer("url", u), + zap.Any("list", featuresInterface), + ) return false } @@ -356,7 +402,10 @@ func (c *Capabilities) HasCapabilityFeature(ctx context.Context, u *url.URL, fea func (c *Capabilities) getConfigGroup(ctx context.Context, u *url.URL, group string) (map[string]interface{}, bool, bool) { caps, cached, err := c.loadCapabilities(ctx, u) if err != nil { - log.Printf("Could not get capabilities for %s: %s", u, err) + c.log.Error("Could not get capabilities", + zap.Stringer("url", u), + zap.Error(err), + ) return nil, cached, false } @@ -367,7 +416,10 @@ func (c *Capabilities) getConfigGroup(ctx context.Context, u *url.URL, group str config, ok := configInterface.(map[string]interface{}) if !ok { - log.Printf("Invalid config mapping received from %s: %+v", u, configInterface) + c.log.Error("Invalid config mapping received", + zap.Stringer("url", u), + zap.Any("mapping", configInterface), + ) return nil, cached, false } @@ -378,7 +430,11 @@ func (c *Capabilities) getConfigGroup(ctx context.Context, u *url.URL, group str groupConfig, ok := groupInterface.(map[string]interface{}) if !ok { - log.Printf("Invalid group mapping \"%s\" received from %s: %+v", group, u, groupInterface) + c.log.Error("Invalid group mapping received", + zap.String("group", group), + zap.Stringer("url", u), + zap.Any("mapping", groupInterface), + ) return nil, cached, false } @@ -404,7 +460,11 @@ func (c *Capabilities) GetIntegerConfig(ctx context.Context, u *url.URL, group, case float64: return int(value), cached, true default: - log.Printf("Invalid config value for \"%s\" received from %s: %+v", key, u, value) + c.log.Error("Invalid config value received", + zap.String("key", key), + zap.Stringer("url", u), + zap.Any("value", value), + ) } return 0, cached, false @@ -425,7 +485,11 @@ func (c *Capabilities) GetStringConfig(ctx context.Context, u *url.URL, group, k case string: return value, cached, true default: - log.Printf("Invalid config value for \"%s\" received from %s: %+v", key, u, value) + c.log.Error("Invalid config value received", + zap.String("key", key), + zap.Stringer("url", u), + zap.Any("value", value), + ) } return "", cached, false diff --git a/capabilities_test.go b/capabilities_test.go index fddefd0a..65d2a11f 100644 --- a/capabilities_test.go +++ b/capabilities_test.go @@ -43,9 +43,10 @@ import ( func NewCapabilitiesForTestWithCallback(t *testing.T, callback func(*CapabilitiesResponse, http.ResponseWriter) error) (*url.URL, *Capabilities) { require := require.New(t) + log := GetLoggerForTest(t) pool, err := NewHttpClientPool(1, false) require.NoError(err) - capabilities, err := NewCapabilities("0.0", pool) + capabilities, err := NewCapabilities(log, "0.0", pool) require.NoError(err) r := mux.NewRouter() @@ -171,7 +172,6 @@ func SetCapabilitiesGetNow(t *testing.T, capabilities *Capabilities, f func() ti func TestCapabilities(t *testing.T) { t.Parallel() - CatchLogForTest(t) assert := assert.New(t) url, capabilities := NewCapabilitiesForTest(t) @@ -214,7 +214,6 @@ func TestCapabilities(t *testing.T) { func TestInvalidateCapabilities(t *testing.T) { t.Parallel() - CatchLogForTest(t) assert := assert.New(t) var called atomic.Uint32 url, capabilities := NewCapabilitiesForTestWithCallback(t, func(cr *CapabilitiesResponse, w http.ResponseWriter) error { @@ -274,7 +273,6 @@ func TestInvalidateCapabilities(t *testing.T) { func TestCapabilitiesNoCache(t *testing.T) { t.Parallel() - CatchLogForTest(t) assert := assert.New(t) var called atomic.Uint32 url, capabilities := NewCapabilitiesForTestWithCallback(t, func(cr *CapabilitiesResponse, w http.ResponseWriter) error { @@ -318,7 +316,6 @@ func TestCapabilitiesNoCache(t *testing.T) { func TestCapabilitiesShortCache(t *testing.T) { t.Parallel() - CatchLogForTest(t) assert := assert.New(t) var called atomic.Uint32 url, capabilities := NewCapabilitiesForTestWithCallback(t, func(cr *CapabilitiesResponse, w http.ResponseWriter) error { @@ -372,7 +369,6 @@ func TestCapabilitiesShortCache(t *testing.T) { func TestCapabilitiesNoCacheETag(t *testing.T) { t.Parallel() - CatchLogForTest(t) assert := assert.New(t) var called atomic.Uint32 url, capabilities := NewCapabilitiesForTestWithCallback(t, func(cr *CapabilitiesResponse, w http.ResponseWriter) error { @@ -413,7 +409,6 @@ func TestCapabilitiesNoCacheETag(t *testing.T) { func TestCapabilitiesCacheNoMustRevalidate(t *testing.T) { t.Parallel() - CatchLogForTest(t) assert := assert.New(t) var called atomic.Uint32 url, capabilities := NewCapabilitiesForTestWithCallback(t, func(cr *CapabilitiesResponse, w http.ResponseWriter) error { @@ -453,7 +448,6 @@ func TestCapabilitiesCacheNoMustRevalidate(t *testing.T) { func TestCapabilitiesNoCacheNoMustRevalidate(t *testing.T) { t.Parallel() - CatchLogForTest(t) assert := assert.New(t) var called atomic.Uint32 url, capabilities := NewCapabilitiesForTestWithCallback(t, func(cr *CapabilitiesResponse, w http.ResponseWriter) error { @@ -493,7 +487,6 @@ func TestCapabilitiesNoCacheNoMustRevalidate(t *testing.T) { func TestCapabilitiesNoCacheMustRevalidate(t *testing.T) { t.Parallel() - CatchLogForTest(t) assert := assert.New(t) var called atomic.Uint32 url, capabilities := NewCapabilitiesForTestWithCallback(t, func(cr *CapabilitiesResponse, w http.ResponseWriter) error { diff --git a/certificate_reloader.go b/certificate_reloader.go index 3e23c965..42388413 100644 --- a/certificate_reloader.go +++ b/certificate_reloader.go @@ -25,12 +25,15 @@ import ( "crypto/tls" "crypto/x509" "fmt" - "log" "os" "sync/atomic" + + "go.uber.org/zap" ) type CertificateReloader struct { + log *zap.Logger + certFile string certWatcher *FileWatcher @@ -42,22 +45,23 @@ type CertificateReloader struct { reloadCounter atomic.Uint64 } -func NewCertificateReloader(certFile string, keyFile string) (*CertificateReloader, error) { +func NewCertificateReloader(log *zap.Logger, certFile string, keyFile string) (*CertificateReloader, error) { pair, err := tls.LoadX509KeyPair(certFile, keyFile) if err != nil { return nil, fmt.Errorf("could not load certificate / key: %w", err) } reloader := &CertificateReloader{ + log: log, certFile: certFile, keyFile: keyFile, } reloader.certificate.Store(&pair) - reloader.certWatcher, err = NewFileWatcher(certFile, reloader.reload) + reloader.certWatcher, err = NewFileWatcher(log, certFile, reloader.reload) if err != nil { return nil, err } - reloader.keyWatcher, err = NewFileWatcher(keyFile, reloader.reload) + reloader.keyWatcher, err = NewFileWatcher(log, keyFile, reloader.reload) if err != nil { reloader.certWatcher.Close() // nolint return nil, err @@ -72,10 +76,16 @@ func (r *CertificateReloader) Close() { } func (r *CertificateReloader) reload(filename string) { - log.Printf("reloading certificate from %s with %s", r.certFile, r.keyFile) + log := r.log.With( + zap.String("certificate", r.certFile), + zap.String("key", r.keyFile), + ) + log.Debug("reloading certificate") pair, err := tls.LoadX509KeyPair(r.certFile, r.keyFile) if err != nil { - log.Printf("could not load certificate / key: %s", err) + log.Error("could not load certificate / key", + zap.Error(err), + ) return } @@ -100,6 +110,7 @@ func (r *CertificateReloader) GetReloadCounter() uint64 { } type CertPoolReloader struct { + log *zap.Logger certFile string certWatcher *FileWatcher @@ -122,17 +133,18 @@ func loadCertPool(filename string) (*x509.CertPool, error) { return pool, nil } -func NewCertPoolReloader(certFile string) (*CertPoolReloader, error) { +func NewCertPoolReloader(log *zap.Logger, certFile string) (*CertPoolReloader, error) { pool, err := loadCertPool(certFile) if err != nil { return nil, err } reloader := &CertPoolReloader{ + log: log, certFile: certFile, } reloader.pool.Store(pool) - reloader.certWatcher, err = NewFileWatcher(certFile, reloader.reload) + reloader.certWatcher, err = NewFileWatcher(log, certFile, reloader.reload) if err != nil { return nil, err } @@ -145,10 +157,15 @@ func (r *CertPoolReloader) Close() { } func (r *CertPoolReloader) reload(filename string) { - log.Printf("reloading certificate pool from %s", r.certFile) + log := r.log.With( + zap.String("filename", r.certFile), + ) + log.Debug("reloading certificate pool") pool, err := loadCertPool(r.certFile) if err != nil { - log.Printf("could not load certificate pool: %s", err) + log.Error("could not load certificate pool", + zap.Error(err), + ) return } diff --git a/client.go b/client.go index c9f1de09..8b6a6673 100644 --- a/client.go +++ b/client.go @@ -26,7 +26,6 @@ import ( "context" "encoding/json" "errors" - "log" "net" "strconv" "strings" @@ -36,6 +35,7 @@ import ( "github.com/gorilla/websocket" "github.com/mailru/easyjson" + "go.uber.org/zap" ) const ( @@ -126,6 +126,7 @@ type ClientGeoIpHandler interface { type Client struct { ctx context.Context + log *zap.Logger conn *websocket.Conn addr string agent string @@ -147,7 +148,7 @@ type Client struct { messageChan chan *bytes.Buffer } -func NewClient(ctx context.Context, conn *websocket.Conn, remoteAddress string, agent string, handler ClientHandler) (*Client, error) { +func NewClient(ctx context.Context, log *zap.Logger, conn *websocket.Conn, remoteAddress string, agent string, handler ClientHandler) (*Client, error) { remoteAddress = strings.TrimSpace(remoteAddress) if remoteAddress == "" { remoteAddress = "unknown remote address" @@ -162,11 +163,12 @@ func NewClient(ctx context.Context, conn *websocket.Conn, remoteAddress string, agent: agent, logRTT: true, } - client.SetConn(conn, remoteAddress, handler) + client.SetConn(log, conn, remoteAddress, handler) return client, nil } -func (c *Client) SetConn(conn *websocket.Conn, remoteAddress string, handler ClientHandler) { +func (c *Client) SetConn(log *zap.Logger, conn *websocket.Conn, remoteAddress string, handler ClientHandler) { + c.log = log c.conn = conn c.addr = remoteAddress c.SetHandler(handler) @@ -330,11 +332,15 @@ func (c *Client) ReadPump() { go c.processMessages() addr := c.RemoteAddr() + log := c.log.With( + zap.String("addr", addr), + ) + c.mu.Lock() conn := c.conn c.mu.Unlock() if conn == nil { - log.Printf("Connection from %s closed while starting readPump", addr) + log.Debug("Connection closed while starting readPump") return } @@ -349,11 +355,17 @@ func (c *Client) ReadPump() { rtt := now.Sub(time.Unix(0, ts)) if c.logRTT { rtt_ms := rtt.Nanoseconds() / time.Millisecond.Nanoseconds() + pongLog := log if sessionId := c.GetSessionId(); sessionId != "" { - log.Printf("Client %s has RTT of %d ms (%s)", sessionId, rtt_ms, rtt) - } else { - log.Printf("Client from %s has RTT of %d ms (%s)", addr, rtt_ms, rtt) + pongLog = pongLog.With( + zap.String("sessionid", sessionId), + ) } + + pongLog.Info("Client has RTT", + zap.Int64("rttms", rtt_ms), + zap.Duration("rtt", rtt), + ) } c.getHandler().OnRTTReceived(c, rtt) } @@ -371,21 +383,29 @@ func (c *Client) ReadPump() { websocket.CloseNormalClosure, websocket.CloseGoingAway, websocket.CloseNoStatusReceived) { + errLog := log if sessionId := c.GetSessionId(); sessionId != "" { - log.Printf("Error reading from client %s: %v", sessionId, err) - } else { - log.Printf("Error reading from %s: %v", addr, err) + errLog = errLog.With( + zap.String("sessionid", sessionId), + ) } + errLog.Error("Error reading from client", + zap.Error(err), + ) } break } if messageType != websocket.TextMessage { + errLog := log if sessionId := c.GetSessionId(); sessionId != "" { - log.Printf("Unsupported message type %v from client %s", messageType, sessionId) - } else { - log.Printf("Unsupported message type %v from %s", messageType, addr) + errLog = errLog.With( + zap.String("sessionid", sessionId), + ) } + errLog.Debug("Unsupported message type", + zap.Int("type", messageType), + ) c.SendError(InvalidFormat) continue } @@ -394,11 +414,15 @@ func (c *Client) ReadPump() { decodeBuffer.Reset() if _, err := decodeBuffer.ReadFrom(reader); err != nil { bufferPool.Put(decodeBuffer) + errLog := log if sessionId := c.GetSessionId(); sessionId != "" { - log.Printf("Error reading message from client %s: %v", sessionId, err) - } else { - log.Printf("Error reading message from %s: %v", addr, err) + errLog = errLog.With( + zap.String("sessionid", sessionId), + ) } + errLog.Error("Error reading message", + zap.Error(err), + ) break } @@ -448,11 +472,19 @@ func (c *Client) writeInternal(message json.Marshaler) bool { return false } + log := c.log.With( + zap.String("addr", c.RemoteAddr()), + ) if sessionId := c.GetSessionId(); sessionId != "" { - log.Printf("Could not send message %+v to client %s: %v", message, sessionId, err) - } else { - log.Printf("Could not send message %+v to %s: %v", message, c.RemoteAddr(), err) + log = log.With( + zap.String("sessionid", sessionId), + ) } + + log.Error("Could not send message", + zap.Any("message", message), + zap.Error(err), + ) closeData = websocket.FormatCloseMessage(websocket.CloseInternalServerErr, "") goto close } @@ -461,11 +493,18 @@ func (c *Client) writeInternal(message json.Marshaler) bool { close: c.conn.SetWriteDeadline(time.Now().Add(writeWait)) // nolint if err := c.conn.WriteMessage(websocket.CloseMessage, closeData); err != nil { + log := c.log.With( + zap.String("addr", c.RemoteAddr()), + ) if sessionId := c.GetSessionId(); sessionId != "" { - log.Printf("Could not send close message to client %s: %v", sessionId, err) - } else { - log.Printf("Could not send close message to %s: %v", c.RemoteAddr(), err) + log = log.With( + zap.String("sessionid", sessionId), + ) } + + log.Error("Could not send close message", + zap.Error(err), + ) } return false } @@ -488,11 +527,17 @@ func (c *Client) writeError(e error) bool { // nolint closeData := websocket.FormatCloseMessage(websocket.CloseInternalServerErr, e.Error()) c.conn.SetWriteDeadline(time.Now().Add(writeWait)) // nolint if err := c.conn.WriteMessage(websocket.CloseMessage, closeData); err != nil { + log := c.log.With( + zap.String("addr", c.RemoteAddr()), + ) if sessionId := c.GetSessionId(); sessionId != "" { - log.Printf("Could not send close message to client %s: %v", sessionId, err) - } else { - log.Printf("Could not send close message to %s: %v", c.RemoteAddr(), err) + log = log.With( + zap.String("sessionid", sessionId), + ) } + log.Error("Could not send close message", + zap.Error(err), + ) } return false } @@ -536,11 +581,17 @@ func (c *Client) sendPing() bool { msg := strconv.FormatInt(now, 10) c.conn.SetWriteDeadline(time.Now().Add(writeWait)) // nolint if err := c.conn.WriteMessage(websocket.PingMessage, []byte(msg)); err != nil { + log := c.log.With( + zap.String("addr", c.RemoteAddr()), + ) if sessionId := c.GetSessionId(); sessionId != "" { - log.Printf("Could not send ping to client %s: %v", sessionId, err) - } else { - log.Printf("Could not send ping to %s: %v", c.RemoteAddr(), err) + log = log.With( + zap.String("sessionid", sessionId), + ) } + log.Error("Could not send ping", + zap.Error(err), + ) return false } diff --git a/clientsession.go b/clientsession.go index 9b5b9dab..54401926 100644 --- a/clientsession.go +++ b/clientsession.go @@ -25,7 +25,6 @@ import ( "context" "encoding/json" "fmt" - "log" "net/url" "strings" "sync" @@ -33,6 +32,7 @@ import ( "time" "github.com/pion/sdp/v3" + "go.uber.org/zap" ) var ( @@ -50,6 +50,7 @@ const ( type ResponseHandlerFunc func(message *ClientMessage) bool type ClientSession struct { + log *zap.Logger hub *Hub events AsyncEvents privateId string @@ -101,9 +102,12 @@ type ClientSession struct { responseHandlers map[string]ResponseHandlerFunc } -func NewClientSession(hub *Hub, privateId string, publicId string, data *SessionIdData, backend *Backend, hello *HelloClientMessage, auth *BackendClientAuthResponse) (*ClientSession, error) { +func NewClientSession(log *zap.Logger, hub *Hub, privateId string, publicId string, data *SessionIdData, backend *Backend, hello *HelloClientMessage, auth *BackendClientAuthResponse) (*ClientSession, error) { ctx, closeFunc := context.WithCancel(context.Background()) s := &ClientSession{ + log: log.With( + zap.String("sessionid", publicId), + ), hub: hub, events: hub.events, privateId: privateId, @@ -292,7 +296,9 @@ func (s *ClientSession) SetPermissions(permissions []Permission) { s.permissions = p s.supportsPermissions = true - log.Printf("Permissions of session %s changed: %s", s.PublicId(), permissions) + s.log.Info("Permissions of session changed", + zap.Any("permissions", permissions), + ) } func (s *ClientSession) Backend() *Backend { @@ -454,19 +460,31 @@ func (s *ClientSession) UpdateRoomSessionId(roomSessionId string) error { if roomSessionId != "" { if room := s.GetRoom(); room != nil { - log.Printf("Session %s updated room session id to %s in room %s", s.PublicId(), roomSessionId, room.Id()) + s.log.Info("Session updated room session id", + zap.String("roomsessionid", roomSessionId), + zap.String("roomid", room.Id()), + ) } else if client := s.GetFederationClient(); client != nil { - log.Printf("Session %s updated room session id to %s in federated room %s", s.PublicId(), roomSessionId, client.RemoteRoomId()) + s.log.Info("Session updated room session id", + zap.String("roomsessionid", roomSessionId), + zap.String("remoteroomid", client.RemoteRoomId()), + ) } else { - log.Printf("Session %s updated room session id to %s in unknown room", s.PublicId(), roomSessionId) + s.log.Info("Session updated room session id", + zap.String("roomsessionid", roomSessionId), + ) } } else { if room := s.GetRoom(); room != nil { - log.Printf("Session %s cleared room session id in room %s", s.PublicId(), room.Id()) + s.log.Info("Session cleared room session id", + zap.String("roomid", room.Id()), + ) } else if client := s.GetFederationClient(); client != nil { - log.Printf("Session %s cleared room session id in federated room %s", s.PublicId(), client.RemoteRoomId()) + s.log.Info("Session cleared room session id", + zap.String("remoteroomid", client.RemoteRoomId()), + ) } else { - log.Printf("Session %s cleared room session id in unknown room", s.PublicId()) + s.log.Info("Session cleared room session id") } } @@ -488,7 +506,10 @@ func (s *ClientSession) SubscribeRoomEvents(roomid string, roomSessionId string) return err } } - log.Printf("Session %s joined room %s with room session id %s", s.PublicId(), roomid, roomSessionId) + s.log.Info("Session joined room", + zap.String("roomid", roomid), + zap.String("roomsessionid", roomSessionId), + ) s.roomSessionId = roomSessionId return nil } @@ -502,7 +523,9 @@ func (s *ClientSession) LeaveCall() { return } - log.Printf("Session %s left call %s", s.PublicId(), room.Id()) + s.log.Info("Session left call", + zap.String("roomid", room.Id()), + ) s.releaseMcuObjects() } @@ -514,7 +537,10 @@ func (s *ClientSession) LeaveRoomWithMessage(notify bool, message *ClientMessage if prev := s.federation.Swap(nil); prev != nil { // Session was connected to a federation room. if err := prev.Leave(message); err != nil { - log.Printf("Error leaving room for session %s on federation client %s: %s", s.PublicId(), prev.URL(), err) + s.log.Error("Error leaving room on federation client", + zap.String("url", prev.URL()), + zap.Error(err), + ) prev.Close() } return nil @@ -558,15 +584,23 @@ func (s *ClientSession) doUnsubscribeRoomEvents(notify bool) { if notify && room != nil && s.roomSessionId != "" && !strings.HasPrefix(s.roomSessionId, FederatedRoomSessionIdPrefix) { // Notify go func(sid string) { + log := s.log.With( + zap.String("roomsessionid", sid), + zap.String("roomid", room.Id()), + ) ctx := context.Background() request := NewBackendClientRoomRequest(room.Id(), s.userId, sid) request.Room.UpdateFromSession(s) request.Room.Action = "leave" var response map[string]interface{} if err := s.hub.backend.PerformJSONRequest(ctx, s.ParsedBackendUrl(), request, &response); err != nil { - log.Printf("Could not notify about room session %s left room %s: %s", sid, room.Id(), err) + log.Error("Could not notify about room session left room", + zap.Error(err), + ) } else { - log.Printf("Removed room session %s: %+v", sid, response) + log.Info("Removed room session", + zap.Any("response", response), + ) } }(s.roomSessionId) } @@ -584,7 +618,7 @@ func (s *ClientSession) clearClientLocked(client HandlerClient) { if s.client == nil { return } else if client != nil && s.client != client { - log.Printf("Trying to clear other client in session %s", s.PublicId()) + s.log.Warn("Trying to clear other client in session") return } @@ -637,7 +671,10 @@ func (s *ClientSession) sendOffer(client McuClient, sender string, streamType St } offer_data, err := json.Marshal(offer_message) if err != nil { - log.Println("Could not serialize offer", offer_message, err) + s.log.Error("Could not serialize offer", + zap.Any("offer", offer_message), + zap.Error(err), + ) return } response_message := &ServerMessage{ @@ -667,7 +704,10 @@ func (s *ClientSession) sendCandidate(client McuClient, sender string, streamTyp } candidate_data, err := json.Marshal(candidate_message) if err != nil { - log.Println("Could not serialize candidate", candidate_message, err) + s.log.Error("Could not serialize candidate", + zap.Any("candidate", candidate_message), + zap.Error(err), + ) return } response_message := &ServerMessage{ @@ -755,7 +795,10 @@ func (s *ClientSession) OnIceCandidate(client McuClient, candidate interface{}) } } - log.Printf("Session %s received candidate %+v for unknown client %s", s.PublicId(), candidate, client.Id()) + s.log.Warn("Session received candidate for unknown client", + zap.Any("candidate", candidate), + zap.String("clientid", client.Id()), + ) } func (s *ClientSession) OnIceCompleted(client McuClient) { @@ -935,7 +978,10 @@ func (s *ClientSession) GetOrCreatePublisher(ctx context.Context, mcu Mcu, strea } else { s.publishers[streamType] = publisher } - log.Printf("Publishing %s as %s for session %s", streamType, publisher.Id(), s.PublicId()) + s.log.Info("Publishing for session", + zap.Any("streamtype", streamType), + zap.String("publisherid", publisher.Id()), + ) s.publisherWaiters.Wakeup() } else { publisher.SetMedia(mediaTypes) @@ -1013,7 +1059,11 @@ func (s *ClientSession) GetOrCreateSubscriber(ctx context.Context, mcu Mcu, id s } else { s.subscribers[getStreamId(id, streamType)] = subscriber } - log.Printf("Subscribing %s from %s as %s in session %s", streamType, id, subscriber.Id(), s.PublicId()) + s.log.Info("Subscribing in session", + zap.Any("streamtype", streamType), + zap.String("publisherid", id), + zap.String("subscriberid", subscriber.Id()), + ) } return subscriber, nil @@ -1051,7 +1101,9 @@ func (s *ClientSession) processAsyncMessage(message *AsyncMessage) { if (publisher.HasMedia(MediaTypeAudio) && !s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_AUDIO)) || (publisher.HasMedia(MediaTypeVideo) && !s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_VIDEO)) { delete(s.publishers, StreamTypeVideo) - log.Printf("Session %s is no longer allowed to publish media, closing publisher %s", s.PublicId(), publisher.Id()) + s.log.Info("Session is no longer allowed to publish media, closing publisher", + zap.String("publisherid", publisher.Id()), + ) go func() { publisher.Close(context.Background()) }() @@ -1062,7 +1114,9 @@ func (s *ClientSession) processAsyncMessage(message *AsyncMessage) { if !s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_SCREEN) { if publisher, found := s.publishers[StreamTypeScreen]; found { delete(s.publishers, StreamTypeScreen) - log.Printf("Session %s is no longer allowed to publish screen, closing publisher %s", s.PublicId(), publisher.Id()) + s.log.Info("Session is no longer allowed to publish screen, closing publisher", + zap.String("publisherid", publisher.Id()), + ) go func() { publisher.Close(context.Background()) }() @@ -1073,7 +1127,9 @@ func (s *ClientSession) processAsyncMessage(message *AsyncMessage) { return case "message": if message.Message.Type == "bye" && message.Message.Bye.Reason == "room_session_reconnected" { - log.Printf("Closing session %s because same room session %s connected", s.PublicId(), s.RoomSessionId()) + s.log.Info("Closing session because same room session connected", + zap.String("roomsessionid", s.RoomSessionId()), + ) s.LeaveRoom(false) defer s.closeAndWait(false) } @@ -1085,7 +1141,10 @@ func (s *ClientSession) processAsyncMessage(message *AsyncMessage) { mc, err := s.GetOrCreateSubscriber(ctx, s.hub.mcu, message.SendOffer.SessionId, StreamType(message.SendOffer.Data.RoomType)) if err != nil { - log.Printf("Could not create MCU subscriber for session %s to process sendoffer in %s: %s", message.SendOffer.SessionId, s.PublicId(), err) + s.log.Error("Could not create MCU subscriber to process sendoffer", + zap.String("publisherid", message.SendOffer.SessionId), + zap.Error(err), + ) if err := s.events.PublishSessionMessage(message.SendOffer.SessionId, s.backend, &AsyncMessage{ Type: "message", Message: &ServerMessage{ @@ -1094,11 +1153,16 @@ func (s *ClientSession) processAsyncMessage(message *AsyncMessage) { Error: NewError("client_not_found", "No MCU client found to send message to."), }, }); err != nil { - log.Printf("Error sending sendoffer error response to %s: %s", message.SendOffer.SessionId, err) + s.log.Error("Error sending sendoffer error response", + zap.String("recipient", message.SendOffer.SessionId), + zap.Error(err), + ) } return } else if mc == nil { - log.Printf("No MCU subscriber found for session %s to process sendoffer in %s", message.SendOffer.SessionId, s.PublicId()) + s.log.Warn("No MCU subscriber found for session to process sendoffer", + zap.String("publisher", message.SendOffer.SessionId), + ) if err := s.events.PublishSessionMessage(message.SendOffer.SessionId, s.backend, &AsyncMessage{ Type: "message", Message: &ServerMessage{ @@ -1107,14 +1171,21 @@ func (s *ClientSession) processAsyncMessage(message *AsyncMessage) { Error: NewError("client_not_found", "No MCU client found to send message to."), }, }); err != nil { - log.Printf("Error sending sendoffer error response to %s: %s", message.SendOffer.SessionId, err) + s.log.Error("Error sending sendoffer error response", + zap.String("recipient", message.SendOffer.SessionId), + zap.Error(err), + ) } return } mc.SendMessage(s.Context(), nil, message.SendOffer.Data, func(err error, response map[string]interface{}) { if err != nil { - log.Printf("Could not send MCU message %+v for session %s to %s: %s", message.SendOffer.Data, message.SendOffer.SessionId, s.PublicId(), err) + s.log.Error("Could not send MCU message for session", + zap.Any("message", message.SendOffer.Data), + zap.String("recipient", message.SendOffer.SessionId), + zap.Error(err), + ) if err := s.events.PublishSessionMessage(message.SendOffer.SessionId, s.backend, &AsyncMessage{ Type: "message", Message: &ServerMessage{ @@ -1123,7 +1194,10 @@ func (s *ClientSession) processAsyncMessage(message *AsyncMessage) { Error: NewError("processing_failed", "Processing of the message failed, please check server logs."), }, }); err != nil { - log.Printf("Error sending sendoffer error response to %s: %s", message.SendOffer.SessionId, err) + s.log.Error("Error sending sendoffer error response", + zap.String("recipient", message.SendOffer.SessionId), + zap.Error(err), + ) } return } else if response == nil { @@ -1163,7 +1237,9 @@ func (s *ClientSession) storePendingMessage(message *ServerMessage) { } s.pendingClientMessages = append(s.pendingClientMessages, message) if len(s.pendingClientMessages) >= warnPendingMessagesCount { - log.Printf("Session %s has %d pending messages", s.PublicId(), len(s.pendingClientMessages)) + s.log.Warn("Session has pending messages", + zap.Int("count", len(s.pendingClientMessages)), + ) } } @@ -1218,7 +1294,9 @@ func (s *ClientSession) filterDuplicateJoin(entries []*EventServerMessageSession result := make([]*EventServerMessageSessionEntry, 0, len(entries)) for _, e := range entries { if s.seenJoinedEvents[e.SessionId] { - log.Printf("Session %s got duplicate joined event for %s, ignoring", s.publicId, e.SessionId) + s.log.Debug("Session got duplicate joined event, ignoring", + zap.String("other", e.SessionId), + ) continue } @@ -1348,7 +1426,9 @@ func (s *ClientSession) filterAsyncMessage(msg *AsyncMessage) *ServerMessage { switch msg.Type { case "message": if msg.Message == nil { - log.Printf("Received asynchronous message without payload: %+v", msg) + s.log.Warn("Received asynchronous message without payload", + zap.Any("message", msg), + ) return nil } @@ -1372,7 +1452,11 @@ func (s *ClientSession) filterAsyncMessage(msg *AsyncMessage) *ServerMessage { // Can happen mostly during tests where an older room async message // could be received by a subscriber that joined after it was sent. if joined := s.getRoomJoinTime(); joined.IsZero() || msg.SendTime.Before(joined) { - log.Printf("Message %+v was sent on %s before room was joined on %s, ignoring", msg.Message, msg.SendTime, joined) + s.log.Debug("Message was sent before room was joined, ignoring", + zap.Any("message", msg.Message), + zap.Time("sent", msg.SendTime), + zap.Time("joined", joined), + ) return nil } } @@ -1380,7 +1464,10 @@ func (s *ClientSession) filterAsyncMessage(msg *AsyncMessage) *ServerMessage { return msg.Message default: - log.Printf("Received async message with unsupported type %s: %+v", msg.Type, msg) + s.log.Warn("Received async message with unsupported type", + zap.String("type", msg.Type), + zap.Any("message", msg), + ) return nil } } @@ -1402,7 +1489,9 @@ func (s *ClientSession) NotifySessionResumed(client HandlerClient) { s.hasPendingParticipantsUpdate = false s.mu.Unlock() - log.Printf("Send %d pending messages to session %s", len(messages), s.PublicId()) + s.log.Debug("Send pending messages to session", + zap.Int("count", len(messages)), + ) // Send through session to handle connection interruptions. s.SendMessages(messages) diff --git a/clientsession_test.go b/clientsession_test.go index 5840c040..aaf504ce 100644 --- a/clientsession_test.go +++ b/clientsession_test.go @@ -129,7 +129,6 @@ func Test_permissionsEqual(t *testing.T) { func TestBandwidth_Client(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -137,7 +136,7 @@ func TestBandwidth_Client(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), testTimeout) defer cancel() - mcu, err := NewTestMCU() + mcu, err := NewTestMCU(t) require.NoError(err) require.NoError(mcu.Start(ctx)) defer mcu.Stop() @@ -185,7 +184,6 @@ func TestBandwidth_Client(t *testing.T) { func TestBandwidth_Backend(t *testing.T) { t.Parallel() - CatchLogForTest(t) hub, _, _, server := CreateHubWithMultipleBackendsForTest(t) u, err := url.Parse(server.URL + "/one") @@ -199,7 +197,7 @@ func TestBandwidth_Backend(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), testTimeout) defer cancel() - mcu, err := NewTestMCU() + mcu, err := NewTestMCU(t) require.NoError(t, err) require.NoError(t, mcu.Start(ctx)) defer mcu.Stop() diff --git a/deferred_executor.go b/deferred_executor.go index a6f46c79..da498453 100644 --- a/deferred_executor.go +++ b/deferred_executor.go @@ -22,26 +22,28 @@ package signaling import ( - "log" "reflect" "runtime" - "runtime/debug" "sync" + + "go.uber.org/zap" ) // DeferredExecutor will asynchronously execute functions while maintaining // their order. type DeferredExecutor struct { + log *zap.Logger queue chan func() closed chan struct{} closeOnce sync.Once } -func NewDeferredExecutor(queueSize int) *DeferredExecutor { +func NewDeferredExecutor(log *zap.Logger, queueSize int) *DeferredExecutor { if queueSize < 0 { queueSize = 0 } result := &DeferredExecutor{ + log: log, queue: make(chan func(), queueSize), closed: make(chan struct{}), } @@ -68,9 +70,12 @@ func getFunctionName(i interface{}) string { func (e *DeferredExecutor) Execute(f func()) { defer func() { - if e := recover(); e != nil { - log.Printf("Could not defer function %v: %+v", getFunctionName(f), e) - log.Printf("Called from %s", string(debug.Stack())) + if err := recover(); err != nil { + e.log.Error("Could not defer", + zap.String("function", getFunctionName(f)), + zap.Any("error", err), + zap.StackSkip("stack", 1), + ) } }() diff --git a/deferred_executor_test.go b/deferred_executor_test.go index 8fa96ce4..63a90844 100644 --- a/deferred_executor_test.go +++ b/deferred_executor_test.go @@ -28,8 +28,13 @@ import ( "github.com/stretchr/testify/assert" ) +func NewDeferredExecutorForTest(t *testing.T, queueSize int) *DeferredExecutor { + log := GetLoggerForTest(t) + return NewDeferredExecutor(log, queueSize) +} + func TestDeferredExecutor_MultiClose(t *testing.T) { - e := NewDeferredExecutor(0) + e := NewDeferredExecutorForTest(t, 0) defer e.waitForStop() e.Close() @@ -38,7 +43,7 @@ func TestDeferredExecutor_MultiClose(t *testing.T) { func TestDeferredExecutor_QueueSize(t *testing.T) { t.Parallel() - e := NewDeferredExecutor(0) + e := NewDeferredExecutorForTest(t, 0) defer e.waitForStop() defer e.Close() @@ -59,7 +64,7 @@ func TestDeferredExecutor_QueueSize(t *testing.T) { } func TestDeferredExecutor_Order(t *testing.T) { - e := NewDeferredExecutor(64) + e := NewDeferredExecutorForTest(t, 64) defer e.waitForStop() defer e.Close() @@ -86,7 +91,7 @@ func TestDeferredExecutor_Order(t *testing.T) { } func TestDeferredExecutor_CloseFromFunc(t *testing.T) { - e := NewDeferredExecutor(64) + e := NewDeferredExecutorForTest(t, 64) defer e.waitForStop() done := make(chan struct{}) @@ -99,8 +104,7 @@ func TestDeferredExecutor_CloseFromFunc(t *testing.T) { } func TestDeferredExecutor_DeferAfterClose(t *testing.T) { - CatchLogForTest(t) - e := NewDeferredExecutor(64) + e := NewDeferredExecutorForTest(t, 64) defer e.waitForStop() e.Close() @@ -111,7 +115,7 @@ func TestDeferredExecutor_DeferAfterClose(t *testing.T) { } func TestDeferredExecutor_WaitForStopTwice(t *testing.T) { - e := NewDeferredExecutor(64) + e := NewDeferredExecutorForTest(t, 64) defer e.waitForStop() e.Close() diff --git a/dns_monitor.go b/dns_monitor.go index 072e01cc..dbd7350b 100644 --- a/dns_monitor.go +++ b/dns_monitor.go @@ -23,13 +23,14 @@ package signaling import ( "context" - "log" "net" "net/url" "strings" "sync" "sync/atomic" "time" + + "go.uber.org/zap" ) var ( @@ -140,6 +141,7 @@ func (e *dnsMonitorEntry) runCallbacks(all []net.IP, add []net.IP, keep []net.IP } type DnsMonitor struct { + log *zap.Logger interval time.Duration stopCtx context.Context @@ -156,13 +158,14 @@ type DnsMonitor struct { checkHostnames func() } -func NewDnsMonitor(interval time.Duration) (*DnsMonitor, error) { +func NewDnsMonitor(log *zap.Logger, interval time.Duration) (*DnsMonitor, error) { if interval < 0 { interval = defaultDnsMonitorInterval } stopCtx, stopFunc := context.WithCancel(context.Background()) monitor := &DnsMonitor{ + log: log, interval: interval, stopCtx: stopCtx, @@ -335,7 +338,10 @@ func (m *DnsMonitor) checkHostname(entry *dnsMonitorEntry) { ips, err := lookupDnsMonitorIP(entry.hostname) if err != nil { - log.Printf("Could not lookup %s: %s", entry.hostname, err) + m.log.Error("Could not lookup", + zap.String("hostname", entry.hostname), + zap.Error(err), + ) return } diff --git a/dns_monitor_test.go b/dns_monitor_test.go index ee107722..446a4292 100644 --- a/dns_monitor_test.go +++ b/dns_monitor_test.go @@ -86,8 +86,9 @@ func (m *mockDnsLookup) lookup(host string) ([]net.IP, error) { func newDnsMonitorForTest(t *testing.T, interval time.Duration) *DnsMonitor { t.Helper() require := require.New(t) + log := GetLoggerForTest(t) - monitor, err := NewDnsMonitor(interval) + monitor, err := NewDnsMonitor(log, interval) require.NoError(err) t.Cleanup(func() { diff --git a/etcd_client.go b/etcd_client.go index ea1b64d6..c8acd674 100644 --- a/etcd_client.go +++ b/etcd_client.go @@ -25,7 +25,6 @@ import ( "context" "errors" "fmt" - "log" "strings" "sync" "sync/atomic" @@ -50,6 +49,7 @@ type EtcdClientWatcher interface { } type EtcdClient struct { + log *zap.Logger compatSection string mu sync.Mutex @@ -57,8 +57,9 @@ type EtcdClient struct { listeners map[EtcdClientListener]bool } -func NewEtcdClient(config *goconf.ConfigFile, compatSection string) (*EtcdClient, error) { +func NewEtcdClient(log *zap.Logger, config *goconf.ConfigFile, compatSection string) (*EtcdClient, error) { result := &EtcdClient{ + log: log, compatSection: compatSection, } if err := result.load(config, false); err != nil { @@ -73,7 +74,10 @@ func (c *EtcdClient) getConfigStringWithFallback(config *goconf.ConfigFile, opti if value == "" && c.compatSection != "" { value, _ = config.GetString(c.compatSection, option) if value != "" { - log.Printf("WARNING: Configuring etcd option \"%s\" in section \"%s\" is deprecated, use section \"etcd\" instead", option, c.compatSection) + c.log.Warn("Configured etcd option is deprecated, use section \"etcd\" instead", + zap.String("option", option), + zap.String("section", c.compatSection), + ) } } @@ -106,7 +110,7 @@ func (c *EtcdClient) load(config *goconf.ConfigFile, ignoreErrors bool) error { return nil } - log.Printf("No etcd endpoints configured, not changing client") + c.log.Info("No etcd endpoints configured, not changing client") } else { cfg := clientv3.Config{ Endpoints: endpoints, @@ -141,7 +145,9 @@ func (c *EtcdClient) load(config *goconf.ConfigFile, ignoreErrors bool) error { return fmt.Errorf("Could not setup etcd TLS configuration: %w", err) } - log.Printf("Could not setup TLS configuration, will be disabled (%s)", err) + c.log.Warn("Could not setup TLS configuration, will be disabled", + zap.Error(err), + ) } else { cfg.TLS = tlsConfig } @@ -153,14 +159,19 @@ func (c *EtcdClient) load(config *goconf.ConfigFile, ignoreErrors bool) error { return err } - log.Printf("Could not create new client from etd endpoints %+v: %s", endpoints, err) + c.log.Error("Could not create new client from etd endpoints", + zap.Any("endpoints", endpoints), + zap.Error(err), + ) } else { prev := c.getEtcdClient() if prev != nil { prev.Close() } c.client.Store(client) - log.Printf("Using etcd endpoints %+v", endpoints) + c.log.Info("Using etcd endpoints", + zap.Any("endpoints", endpoints), + ) c.notifyListeners() } } @@ -241,16 +252,23 @@ func (c *EtcdClient) WaitForConnection(ctx context.Context) error { if errors.Is(err, context.Canceled) { return err } else if errors.Is(err, context.DeadlineExceeded) { - log.Printf("Timeout waiting for etcd client to connect to the cluster, retry in %s", backoff.NextWait()) + c.log.Error("Timeout waiting for etcd client to connect to the cluster, retry", + zap.Duration("wait", backoff.NextWait()), + ) } else { - log.Printf("Could not sync etcd client with the cluster, retry in %s: %s", backoff.NextWait(), err) + c.log.Error("Could not sync etcd client with the cluster, retry", + zap.Duration("wait", backoff.NextWait()), + zap.Error(err), + ) } backoff.Wait(ctx) continue } - log.Printf("Client synced, using endpoints %+v", c.getEtcdClient().Endpoints()) + c.log.Info("Client synced, using endpoints", + zap.Any("endpoints", c.getEtcdClient().Endpoints()), + ) return nil } } @@ -260,10 +278,15 @@ func (c *EtcdClient) Get(ctx context.Context, key string, opts ...clientv3.OpOpt } func (c *EtcdClient) Watch(ctx context.Context, key string, nextRevision int64, watcher EtcdClientWatcher, opts ...clientv3.OpOption) (int64, error) { - log.Printf("Wait for leader and start watching on %s (rev=%d)", key, nextRevision) + c.log.Debug("Wait for leader and start watching", + zap.String("key", key), + zap.Int64("rev", nextRevision), + ) opts = append(opts, clientv3.WithRev(nextRevision), clientv3.WithPrevKV()) ch := c.getEtcdClient().Watch(clientv3.WithRequireLeader(ctx), key, opts...) - log.Printf("Watch created for %s", key) + c.log.Debug("Watch created", + zap.String("key", key), + ) watcher.EtcdWatchCreated(c, key) for response := range ch { if err := response.Err(); err != nil { @@ -286,7 +309,11 @@ func (c *EtcdClient) Watch(ctx context.Context, key string, nextRevision int64, } watcher.EtcdKeyDeleted(c, string(ev.Kv.Key), prevValue) default: - log.Printf("Unsupported watch event %s %q -> %q", ev.Type, ev.Kv.Key, ev.Kv.Value) + c.log.Error("Unsupported watch event", + zap.Any("type", ev.Type), + zap.ByteString("key", ev.Kv.Key), + zap.ByteString("value", ev.Kv.Value), + ) } } } diff --git a/etcd_client_test.go b/etcd_client_test.go index 7986e2ec..2aa438a7 100644 --- a/etcd_client_test.go +++ b/etcd_client_test.go @@ -113,13 +113,14 @@ func NewEtcdForTest(t *testing.T) *embed.Etcd { } func NewEtcdClientForTest(t *testing.T) (*embed.Etcd, *EtcdClient) { + log := GetLoggerForTest(t) etcd := NewEtcdForTest(t) config := goconf.NewConfigFile() config.AddOption("etcd", "endpoints", etcd.Config().ListenClientUrls[0].String()) config.AddOption("etcd", "loglevel", "error") - client, err := NewEtcdClient(config, "") + client, err := NewEtcdClient(log, config, "") require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, client.Close()) @@ -143,7 +144,6 @@ func DeleteEtcdValue(etcd *embed.Etcd, key string) { func Test_EtcdClient_Get(t *testing.T) { t.Parallel() - CatchLogForTest(t) assert := assert.New(t) etcd, client := NewEtcdClientForTest(t) @@ -163,7 +163,6 @@ func Test_EtcdClient_Get(t *testing.T) { func Test_EtcdClient_GetPrefix(t *testing.T) { t.Parallel() - CatchLogForTest(t) assert := assert.New(t) etcd, client := NewEtcdClientForTest(t) @@ -274,7 +273,6 @@ func (l *EtcdClientTestListener) EtcdKeyDeleted(client *EtcdClient, key string, func Test_EtcdClient_Watch(t *testing.T) { t.Parallel() - CatchLogForTest(t) assert := assert.New(t) etcd, client := NewEtcdClientForTest(t) diff --git a/federation.go b/federation.go index 3aab6e5d..8a29d6c3 100644 --- a/federation.go +++ b/federation.go @@ -27,7 +27,6 @@ import ( "encoding/json" "errors" "fmt" - "log" "net" "strconv" "strings" @@ -37,6 +36,7 @@ import ( "github.com/gorilla/websocket" easyjson "github.com/mailru/easyjson" + "go.uber.org/zap" ) const ( @@ -68,6 +68,7 @@ func getCloudUrl(s string) string { } type FederationClient struct { + log *zap.Logger hub *Hub session *ClientSession message atomic.Pointer[ClientMessage] @@ -97,7 +98,7 @@ type FederationClient struct { closeOnLeave atomic.Bool } -func NewFederationClient(ctx context.Context, hub *Hub, session *ClientSession, message *ClientMessage) (*FederationClient, error) { +func NewFederationClient(ctx context.Context, log *zap.Logger, hub *Hub, session *ClientSession, message *ClientMessage) (*FederationClient, error) { if message.Type != "room" || message.Room == nil || message.Room.Federation == nil { return nil, fmt.Errorf("expected federation room message, got %+v", message) } @@ -125,6 +126,10 @@ func NewFederationClient(ctx context.Context, hub *Hub, session *ClientSession, } result := &FederationClient{ + log: log.With( + zap.String("url", url), + zap.String("sessionid", session.PublicId()), + ), hub: hub, session: session, @@ -173,7 +178,7 @@ func (c *FederationClient) CanReuse(federation *RoomFederationMessage) bool { } func (c *FederationClient) connect(ctx context.Context) error { - log.Printf("Creating federation connection to %s for %s", c.URL(), c.session.PublicId()) + c.log.Info("Creating federation connection") conn, response, err := c.dialer.DialContext(ctx, c.url, nil) if err != nil { return err @@ -190,13 +195,15 @@ func (c *FederationClient) connect(ctx context.Context) error { } if !supportsFederation { if err := conn.Close(); err != nil { - log.Printf("Error closing federation connection to %s: %s", c.URL(), err) + c.log.Error("Error closing federation connection", + zap.Error(err), + ) } return ErrFederationNotSupported } - log.Printf("Federation connection established to %s for %s", c.URL(), c.session.PublicId()) + c.log.Info("Federation connection established") c.mu.Lock() defer c.mu.Unlock() @@ -268,18 +275,24 @@ func (c *FederationClient) closeConnection(withBye bool) { if err := c.sendMessageLocked(&ClientMessage{ Type: "bye", }); err != nil && !isClosedError(err) { - log.Printf("Error sending bye on federation connection to %s: %s", c.URL(), err) + c.log.Error("Error sending bye on federation connection", + zap.Error(err), + ) } } closeMessage := websocket.FormatCloseMessage(websocket.CloseNormalClosure, "") deadline := time.Now().Add(writeWait) if err := c.conn.WriteControl(websocket.CloseMessage, closeMessage, deadline); err != nil && !isClosedError(err) { - log.Printf("Error sending close message on federation connection to %s: %s", c.URL(), err) + c.log.Error("Error sending close message on federation connection", + zap.Error(err), + ) } if err := c.conn.Close(); err != nil && !isClosedError(err) { - log.Printf("Error closing federation connection to %s: %s", c.URL(), err) + c.log.Error("Error closing federation connection", + zap.Error(err), + ) } c.conn = nil @@ -330,7 +343,9 @@ func (c *FederationClient) reconnect() { defer cancel() if err := c.connect(ctx); err != nil { - log.Printf("Error connecting to federation server %s for %s: %s", c.URL(), c.session.PublicId(), err) + c.log.Error("Error connecting to federation server", + zap.Error(err), + ) c.scheduleReconnect() return } @@ -354,7 +369,9 @@ func (c *FederationClient) readPump(conn *websocket.Conn) { } if !websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseNoStatusReceived) { - log.Printf("Error reading from %s for %s: %s", c.URL(), c.session.PublicId(), err) + c.log.Error("Error reading from federated connection", + zap.Error(err), + ) } c.scheduleReconnect() @@ -367,7 +384,10 @@ func (c *FederationClient) readPump(conn *websocket.Conn) { var msg ServerMessage if err := json.Unmarshal(data, &msg); err != nil { - log.Printf("Error unmarshalling %s from %s: %s", string(data), c.URL(), err) + c.log.Error("Error unmarshalling federated message", + zap.ByteString("data", data), + zap.Error(err), + ) continue } @@ -396,7 +416,9 @@ func (c *FederationClient) sendPing() { msg := strconv.FormatInt(now, 10) c.conn.SetWriteDeadline(time.Now().Add(writeWait)) // nolint if err := c.conn.WriteMessage(websocket.PingMessage, []byte(msg)); err != nil { - log.Printf("Could not send ping to federated client %s for %s: %v", c.URL(), c.session.PublicId(), err) + c.log.Error("Could not send ping to federated client", + zap.Error(err), + ) c.scheduleReconnectLocked() } } @@ -479,7 +501,9 @@ func (c *FederationClient) processWelcome(msg *ServerMessage) { Token: c.federation.Load().Token, } if err := c.sendHello(federationParams); err != nil { - log.Printf("Error sending hello message to %s for %s: %s", c.URL(), c.session.PublicId(), err) + c.log.Error("Error sending hello message", + zap.Error(err), + ) c.closeWithError(err) } } @@ -491,7 +515,10 @@ func (c *FederationClient) processHello(msg *ServerMessage) { defer c.helloMu.Unlock() if msg.Id != c.helloMsgId { - log.Printf("Received hello response %+v for unknown request, expected %s", msg, c.helloMsgId) + c.log.Warn("Received hello response for unknown request", + zap.Stringer("response", msg), + zap.String("expected", c.helloMsgId), + ) if err := c.sendHelloLocked(c.helloAuth); err != nil { c.closeWithError(err) } @@ -510,12 +537,16 @@ func (c *FederationClient) processHello(msg *ServerMessage) { c.closeWithError(err) } default: - log.Printf("Received hello error from federated client for %s to %s: %+v", c.session.PublicId(), c.URL(), msg) + c.log.Error("Received hello error from federated client", + zap.Stringer("error", msg), + ) c.closeWithError(msg.Error) } return } else if msg.Type != "hello" { - log.Printf("Received unknown hello response from federated client for %s to %s: %+v", c.session.PublicId(), c.URL(), msg) + c.log.Error("Received unknown hello response from federated client", + zap.Stringer("response", msg), + ) if err := c.sendHelloLocked(c.helloAuth); err != nil { c.closeWithError(err) } @@ -556,7 +587,9 @@ func (c *FederationClient) processHello(msg *ServerMessage) { messages := c.pendingMessages c.pendingMessages = nil - log.Printf("Sending %d pending messages to %s for %s", count, c.URL(), c.session.PublicId()) + c.log.Debug("Send pending messages", + zap.Int("count", count), + ) c.helloMu.Unlock() defer c.helloMu.Lock() @@ -565,7 +598,10 @@ func (c *FederationClient) processHello(msg *ServerMessage) { defer c.mu.Unlock() for _, msg := range messages { if err := c.sendMessageLocked(msg); err != nil { - log.Printf("Error sending pending message %+v on federation connection to %s: %s", msg, c.URL(), err) + c.log.Error("Error sending pending message on federation connection", + zap.Stringer("message", msg), + zap.Error(err), + ) break } } @@ -840,7 +876,9 @@ func (c *FederationClient) deferMessage(message *ClientMessage) { c.pendingMessages = append(c.pendingMessages, message) if len(c.pendingMessages) >= warnPendingMessagesCount { - log.Printf("Session %s has %d pending federated messages", c.session.PublicId(), len(c.pendingMessages)) + c.log.Debug("Session has pending federated messages", + zap.Int("count", len(c.pendingMessages)), + ) } } @@ -872,7 +910,10 @@ func (c *FederationClient) sendMessageLocked(message *ClientMessage) error { return err } - log.Printf("Could not send message %+v for %s to federated client %s: %v", message, c.session.PublicId(), c.URL(), err) + c.log.Error("Could not send message to federated client", + zap.Stringer("message", message), + zap.Error(err), + ) c.deferMessage(message) c.scheduleReconnectLocked() } diff --git a/federation_test.go b/federation_test.go index ea4a5044..781f5ded 100644 --- a/federation_test.go +++ b/federation_test.go @@ -33,8 +33,6 @@ import ( ) func Test_FederationInvalidToken(t *testing.T) { - CatchLogForTest(t) - assert := assert.New(t) require := require.New(t) @@ -73,8 +71,6 @@ func Test_FederationInvalidToken(t *testing.T) { } func Test_Federation(t *testing.T) { - CatchLogForTest(t) - assert := assert.New(t) require := require.New(t) @@ -492,8 +488,6 @@ func Test_Federation(t *testing.T) { } func Test_FederationJoinRoomTwice(t *testing.T) { - CatchLogForTest(t) - assert := assert.New(t) require := require.New(t) @@ -603,8 +597,6 @@ func Test_FederationJoinRoomTwice(t *testing.T) { } func Test_FederationChangeRoom(t *testing.T) { - CatchLogForTest(t) - assert := assert.New(t) require := require.New(t) @@ -716,8 +708,6 @@ func Test_FederationChangeRoom(t *testing.T) { } func Test_FederationMedia(t *testing.T) { - CatchLogForTest(t) - assert := assert.New(t) require := require.New(t) @@ -726,14 +716,14 @@ func Test_FederationMedia(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), testTimeout) defer cancel() - mcu1, err := NewTestMCU() + mcu1, err := NewTestMCU(t) require.NoError(err) require.NoError(mcu1.Start(ctx)) defer mcu1.Stop() hub1.SetMcu(mcu1) - mcu2, err := NewTestMCU() + mcu2, err := NewTestMCU(t) require.NoError(err) require.NoError(mcu2.Start(ctx)) defer mcu2.Stop() @@ -827,8 +817,6 @@ func Test_FederationMedia(t *testing.T) { } func Test_FederationResume(t *testing.T) { - CatchLogForTest(t) - assert := assert.New(t) require := require.New(t) @@ -960,8 +948,6 @@ func Test_FederationResume(t *testing.T) { } func Test_FederationResumeNewSession(t *testing.T) { - CatchLogForTest(t) - assert := assert.New(t) require := require.New(t) diff --git a/file_watcher.go b/file_watcher.go index 6d3a9237..02924b49 100644 --- a/file_watcher.go +++ b/file_watcher.go @@ -24,7 +24,6 @@ package signaling import ( "context" "errors" - "log" "os" "path" "path/filepath" @@ -34,6 +33,7 @@ import ( "time" "github.com/fsnotify/fsnotify" + "go.uber.org/zap" ) const ( @@ -51,6 +51,7 @@ func init() { type FileWatcherCallback func(filename string) type FileWatcher struct { + log *zap.Logger filename string target string callback FileWatcherCallback @@ -60,7 +61,7 @@ type FileWatcher struct { closeFunc context.CancelFunc } -func NewFileWatcher(filename string, callback FileWatcherCallback) (*FileWatcher, error) { +func NewFileWatcher(log *zap.Logger, filename string, callback FileWatcherCallback) (*FileWatcher, error) { realFilename, err := filepath.EvalSymlinks(filename) if err != nil { return nil, err @@ -84,6 +85,7 @@ func NewFileWatcher(filename string, callback FileWatcherCallback) (*FileWatcher closeCtx, closeFunc := context.WithCancel(context.Background()) w := &FileWatcher{ + log: log, filename: filename, target: realFilename, callback: callback, @@ -141,7 +143,10 @@ func (f *FileWatcher) run() { if stat, err := os.Lstat(event.Name); err != nil { if !errors.Is(err, os.ErrNotExist) { - log.Printf("Could not lstat %s: %s", event.Name, err) + f.log.Error("Could not lstat", + zap.String("filename", event.Name), + zap.Error(err), + ) } } else if stat.Mode()&os.ModeSymlink != 0 { target, err := filepath.EvalSymlinks(event.Name) @@ -160,7 +165,10 @@ func (f *FileWatcher) run() { return } - log.Printf("Error watching %s: %s", f.filename, err) + f.log.Error("Error watching", + zap.String("filename", f.filename), + zap.Error(err), + ) case <-f.closeCtx.Done(): return } diff --git a/file_watcher_test.go b/file_watcher_test.go index 14dec157..6b32abbd 100644 --- a/file_watcher_test.go +++ b/file_watcher_test.go @@ -37,8 +37,9 @@ var ( func TestFileWatcher_NotExist(t *testing.T) { assert := assert.New(t) + log := GetLoggerForTest(t) tmpdir := t.TempDir() - if w, err := NewFileWatcher(path.Join(tmpdir, "test.txt"), func(filename string) {}); !assert.ErrorIs(err, os.ErrNotExist) { + if w, err := NewFileWatcher(log, path.Join(tmpdir, "test.txt"), func(filename string) {}); !assert.ErrorIs(err, os.ErrNotExist) { if w != nil { assert.NoError(w.Close()) } @@ -49,12 +50,13 @@ func TestFileWatcher_File(t *testing.T) { ensureNoGoroutinesLeak(t, func(t *testing.T) { require := require.New(t) assert := assert.New(t) + log := GetLoggerForTest(t) tmpdir := t.TempDir() filename := path.Join(tmpdir, "test.txt") require.NoError(os.WriteFile(filename, []byte("Hello world!"), 0644)) modified := make(chan struct{}) - w, err := NewFileWatcher(filename, func(filename string) { + w, err := NewFileWatcher(log, filename, func(filename string) { modified <- struct{}{} }) require.NoError(err) @@ -90,11 +92,12 @@ func TestFileWatcher_Rename(t *testing.T) { require := require.New(t) assert := assert.New(t) tmpdir := t.TempDir() + log := GetLoggerForTest(t) filename := path.Join(tmpdir, "test.txt") require.NoError(os.WriteFile(filename, []byte("Hello world!"), 0644)) modified := make(chan struct{}) - w, err := NewFileWatcher(filename, func(filename string) { + w, err := NewFileWatcher(log, filename, func(filename string) { modified <- struct{}{} }) require.NoError(err) @@ -129,6 +132,7 @@ func TestFileWatcher_Symlink(t *testing.T) { require := require.New(t) assert := assert.New(t) tmpdir := t.TempDir() + log := GetLoggerForTest(t) sourceFilename := path.Join(tmpdir, "test1.txt") require.NoError(os.WriteFile(sourceFilename, []byte("Hello world!"), 0644)) @@ -136,7 +140,7 @@ func TestFileWatcher_Symlink(t *testing.T) { require.NoError(os.Symlink(sourceFilename, filename)) modified := make(chan struct{}) - w, err := NewFileWatcher(filename, func(filename string) { + w, err := NewFileWatcher(log, filename, func(filename string) { modified <- struct{}{} }) require.NoError(err) @@ -159,6 +163,7 @@ func TestFileWatcher_ChangeSymlinkTarget(t *testing.T) { require := require.New(t) assert := assert.New(t) tmpdir := t.TempDir() + log := GetLoggerForTest(t) sourceFilename1 := path.Join(tmpdir, "test1.txt") require.NoError(os.WriteFile(sourceFilename1, []byte("Hello world!"), 0644)) @@ -169,7 +174,7 @@ func TestFileWatcher_ChangeSymlinkTarget(t *testing.T) { require.NoError(os.Symlink(sourceFilename1, filename)) modified := make(chan struct{}) - w, err := NewFileWatcher(filename, func(filename string) { + w, err := NewFileWatcher(log, filename, func(filename string) { modified <- struct{}{} }) require.NoError(err) @@ -194,6 +199,7 @@ func TestFileWatcher_OtherSymlink(t *testing.T) { require := require.New(t) assert := assert.New(t) tmpdir := t.TempDir() + log := GetLoggerForTest(t) sourceFilename1 := path.Join(tmpdir, "test1.txt") require.NoError(os.WriteFile(sourceFilename1, []byte("Hello world!"), 0644)) @@ -204,7 +210,7 @@ func TestFileWatcher_OtherSymlink(t *testing.T) { require.NoError(os.Symlink(sourceFilename1, filename)) modified := make(chan struct{}) - w, err := NewFileWatcher(filename, func(filename string) { + w, err := NewFileWatcher(log, filename, func(filename string) { modified <- struct{}{} }) require.NoError(err) @@ -226,6 +232,7 @@ func TestFileWatcher_RenameSymlinkTarget(t *testing.T) { require := require.New(t) assert := assert.New(t) tmpdir := t.TempDir() + log := GetLoggerForTest(t) sourceFilename1 := path.Join(tmpdir, "test1.txt") require.NoError(os.WriteFile(sourceFilename1, []byte("Hello world!"), 0644)) @@ -233,7 +240,7 @@ func TestFileWatcher_RenameSymlinkTarget(t *testing.T) { require.NoError(os.Symlink(sourceFilename1, filename)) modified := make(chan struct{}) - w, err := NewFileWatcher(filename, func(filename string) { + w, err := NewFileWatcher(log, filename, func(filename string) { modified <- struct{}{} }) require.NoError(err) diff --git a/geoip.go b/geoip.go index ec461e55..f39da57e 100644 --- a/geoip.go +++ b/geoip.go @@ -26,7 +26,6 @@ import ( "compress/gzip" "fmt" "io" - "log" "net" "net/http" "net/url" @@ -37,6 +36,7 @@ import ( "github.com/dlintw/goconf" "github.com/oschwald/maxminddb-golang" + "go.uber.org/zap" ) var ( @@ -56,6 +56,7 @@ func GetGeoIpDownloadUrl(license string) string { } type GeoLookup struct { + log *zap.Logger url string isFile bool client http.Client @@ -67,15 +68,21 @@ type GeoLookup struct { reader *maxminddb.Reader } -func NewGeoLookupFromUrl(url string) (*GeoLookup, error) { +func NewGeoLookupFromUrl(log *zap.Logger, url string) (*GeoLookup, error) { geoip := &GeoLookup{ + log: log.With( + zap.String("url", url), + ), url: url, } return geoip, nil } -func NewGeoLookupFromFile(filename string) (*GeoLookup, error) { +func NewGeoLookupFromFile(log *zap.Logger, filename string) (*GeoLookup, error) { geoip := &GeoLookup{ + log: log.With( + zap.String("filename", filename), + ), url: filename, isFile: true, } @@ -123,7 +130,10 @@ func (g *GeoLookup) updateFile() error { } metadata := reader.Metadata - log.Printf("Using %s GeoIP database from %s (built on %s)", metadata.DatabaseType, g.url, time.Unix(int64(metadata.BuildEpoch), 0).UTC()) + g.log.Info("Using GeoIP database", + zap.String("type", metadata.DatabaseType), + zap.Time("built", time.Unix(int64(metadata.BuildEpoch), 0).UTC()), + ) g.mu.Lock() if g.reader != nil { @@ -150,7 +160,7 @@ func (g *GeoLookup) updateUrl() error { defer response.Body.Close() if response.StatusCode == http.StatusNotModified { - log.Printf("GeoIP database at %s has not changed", g.url) + g.log.Debug("GeoIP database has not changed") return nil } else if response.StatusCode/100 != 2 { return fmt.Errorf("downloading %s returned an error: %s", g.url, response.Status) @@ -208,7 +218,10 @@ func (g *GeoLookup) updateUrl() error { } metadata := reader.Metadata - log.Printf("Using %s GeoIP database from %s (built on %s)", metadata.DatabaseType, g.url, time.Unix(int64(metadata.BuildEpoch), 0).UTC()) + g.log.Info("Using GeoIP database", + zap.String("type", metadata.DatabaseType), + zap.Time("built", time.Unix(int64(metadata.BuildEpoch), 0).UTC()), + ) g.mu.Lock() if g.reader != nil { @@ -278,7 +291,7 @@ func IsValidContinent(continent string) bool { } } -func LoadGeoIPOverrides(config *goconf.ConfigFile, ignoreErrors bool) (map[*net.IPNet]string, error) { +func LoadGeoIPOverrides(log *zap.Logger, config *goconf.ConfigFile, ignoreErrors bool) (map[*net.IPNet]string, error) { options, _ := GetStringOptions(config, "geoip-overrides", true) if len(options) == 0 { return nil, nil @@ -293,7 +306,10 @@ func LoadGeoIPOverrides(config *goconf.ConfigFile, ignoreErrors bool) (map[*net. _, ipNet, err = net.ParseCIDR(option) if err != nil { if ignoreErrors { - log.Printf("could not parse CIDR %s (%s), skipping", option, err) + log.Error("could not parse CIDR, skipping", + zap.String("cidr", option), + zap.Error(err), + ) continue } @@ -303,7 +319,9 @@ func LoadGeoIPOverrides(config *goconf.ConfigFile, ignoreErrors bool) (map[*net. ip = net.ParseIP(option) if ip == nil { if ignoreErrors { - log.Printf("could not parse IP %s, skipping", option) + log.Error("could not parse IP, skipping", + zap.String("ip", option), + ) continue } @@ -324,14 +342,22 @@ func LoadGeoIPOverrides(config *goconf.ConfigFile, ignoreErrors bool) (map[*net. value = strings.ToUpper(strings.TrimSpace(value)) if value == "" { - log.Printf("IP %s doesn't have a country assigned, skipping", option) + log.Warn("IP doesn't have a country assigned, skipping", + zap.String("ip", option), + ) continue } else if !IsValidCountry(value) { - log.Printf("Country %s for IP %s is invalid, skipping", value, option) + log.Warn("Country for IP is invalid, skipping", + zap.String("country", value), + zap.String("ip", option), + ) continue } - log.Printf("Using country %s for %s", value, ipNet) + log.Info("Using country for IP", + zap.String("country", value), + zap.Stringer("ip", ipNet), + ) geoipOverrides[ipNet] = value } diff --git a/geoip_test.go b/geoip_test.go index 4d1a1e14..9ea1b2a7 100644 --- a/geoip_test.go +++ b/geoip_test.go @@ -78,9 +78,9 @@ func GetGeoIpUrlForTest(t *testing.T) string { } func TestGeoLookup(t *testing.T) { - CatchLogForTest(t) require := require.New(t) - reader, err := NewGeoLookupFromUrl(GetGeoIpUrlForTest(t)) + log := GetLoggerForTest(t) + reader, err := NewGeoLookupFromUrl(log, GetGeoIpUrlForTest(t)) require.NoError(err) defer reader.Close() @@ -90,9 +90,9 @@ func TestGeoLookup(t *testing.T) { } func TestGeoLookupCaching(t *testing.T) { - CatchLogForTest(t) require := require.New(t) - reader, err := NewGeoLookupFromUrl(GetGeoIpUrlForTest(t)) + log := GetLoggerForTest(t) + reader, err := NewGeoLookupFromUrl(log, GetGeoIpUrlForTest(t)) require.NoError(err) defer reader.Close() @@ -130,15 +130,15 @@ func TestGeoLookupContinent(t *testing.T) { } func TestGeoLookupCloseEmpty(t *testing.T) { - CatchLogForTest(t) - reader, err := NewGeoLookupFromUrl("ignore-url") + log := GetLoggerForTest(t) + reader, err := NewGeoLookupFromUrl(log, "ignore-url") require.NoError(t, err) reader.Close() } func TestGeoLookupFromFile(t *testing.T) { - CatchLogForTest(t) require := require.New(t) + log := GetLoggerForTest(t) geoIpUrl := GetGeoIpUrlForTest(t) resp, err := http.Get(geoIpUrl) @@ -192,7 +192,7 @@ func TestGeoLookupFromFile(t *testing.T) { require.True(foundDatabase, "Did not find GeoIP database in download from %s", geoIpUrl) - reader, err := NewGeoLookupFromFile(tmpfile.Name()) + reader, err := NewGeoLookupFromFile(log, tmpfile.Name()) require.NoError(err) defer reader.Close() diff --git a/grpc_client.go b/grpc_client.go index 0774ed70..7932d9ce 100644 --- a/grpc_client.go +++ b/grpc_client.go @@ -27,7 +27,6 @@ import ( "errors" "fmt" "io" - "log" "net" "net/url" "strings" @@ -37,6 +36,7 @@ import ( "github.com/dlintw/goconf" clientv3 "go.etcd.io/etcd/client/v3" + "go.uber.org/zap" "google.golang.org/grpc" codes "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" @@ -79,6 +79,7 @@ func newGrpcClientImpl(conn grpc.ClientConnInterface) *grpcClientImpl { } type GrpcClient struct { + log *zap.Logger ip net.IP target string conn *grpc.ClientConn @@ -125,7 +126,7 @@ func (r *customIpResolver) Close() { // Noop } -func NewGrpcClient(target string, ip net.IP, opts ...grpc.DialOption) (*GrpcClient, error) { +func NewGrpcClient(log *zap.Logger, target string, ip net.IP, opts ...grpc.DialOption) (*GrpcClient, error) { var conn *grpc.ClientConn var err error if ip != nil { @@ -160,6 +161,9 @@ func NewGrpcClient(target string, ip net.IP, opts ...grpc.DialOption) (*GrpcClie if ip != nil { result.target += " (" + ip.String() + ")" } + result.log = log.With( + zap.String("target", result.Target()), + ) return result, nil } @@ -191,8 +195,9 @@ func (c *GrpcClient) GetServerId(ctx context.Context) (string, error) { func (c *GrpcClient) LookupResumeId(ctx context.Context, resumeId string) (*LookupResumeIdReply, error) { statsGrpcClientCalls.WithLabelValues("LookupResumeId").Inc() - // TODO: Remove debug logging - log.Printf("Lookup resume id %s on %s", resumeId, c.Target()) + c.log.Debug("Lookup resume id", + zap.String("resumeid", resumeId), + ) response, err := c.impl.LookupResumeId(ctx, &LookupResumeIdRequest{ ResumeId: resumeId, }, grpc.WaitForReady(true)) @@ -211,8 +216,9 @@ func (c *GrpcClient) LookupResumeId(ctx context.Context, resumeId string) (*Look func (c *GrpcClient) LookupSessionId(ctx context.Context, roomSessionId string, disconnectReason string) (string, error) { statsGrpcClientCalls.WithLabelValues("LookupSessionId").Inc() - // TODO: Remove debug logging - log.Printf("Lookup room session %s on %s", roomSessionId, c.Target()) + c.log.Debug("Lookup room session", + zap.String("roomsessionid", roomSessionId), + ) response, err := c.impl.LookupSessionId(ctx, &LookupSessionIdRequest{ RoomSessionId: roomSessionId, DisconnectReason: disconnectReason, @@ -233,8 +239,10 @@ func (c *GrpcClient) LookupSessionId(ctx context.Context, roomSessionId string, func (c *GrpcClient) IsSessionInCall(ctx context.Context, sessionId string, room *Room) (bool, error) { statsGrpcClientCalls.WithLabelValues("IsSessionInCall").Inc() - // TODO: Remove debug logging - log.Printf("Check if session %s is in call %s on %s", sessionId, room.Id(), c.Target()) + c.log.Debug("Check if session is in call", + zap.String("sessionid", sessionId), + zap.String("roomid", room.Id()), + ) response, err := c.impl.IsSessionInCall(ctx, &IsSessionInCallRequest{ SessionId: sessionId, RoomId: room.Id(), @@ -251,8 +259,10 @@ func (c *GrpcClient) IsSessionInCall(ctx context.Context, sessionId string, room func (c *GrpcClient) GetPublisherId(ctx context.Context, sessionId string, streamType StreamType) (string, string, net.IP, error) { statsGrpcClientCalls.WithLabelValues("GetPublisherId").Inc() - // TODO: Remove debug logging - log.Printf("Get %s publisher id %s on %s", streamType, sessionId, c.Target()) + c.log.Debug("Get publisher id", + zap.Any("streamtype", streamType), + zap.String("sessionid", sessionId), + ) response, err := c.impl.GetPublisherId(ctx, &GetPublisherIdRequest{ SessionId: sessionId, StreamType: string(streamType), @@ -268,8 +278,9 @@ func (c *GrpcClient) GetPublisherId(ctx context.Context, sessionId string, strea func (c *GrpcClient) GetSessionCount(ctx context.Context, u *url.URL) (uint32, error) { statsGrpcClientCalls.WithLabelValues("GetSessionCount").Inc() - // TODO: Remove debug logging - log.Printf("Get session count for %s on %s", u, c.Target()) + c.log.Debug("Get session count", + zap.Stringer("url", u), + ) response, err := c.impl.GetSessionCount(ctx, &GetSessionCountRequest{ Url: u.String(), }, grpc.WaitForReady(true)) @@ -292,6 +303,7 @@ type ProxySessionReceiver interface { } type SessionProxy struct { + log *zap.Logger sessionId string receiver ProxySessionReceiver @@ -304,7 +316,9 @@ func (p *SessionProxy) recvPump() { defer func() { p.receiver.OnProxyClose(closeError) if err := p.Close(); err != nil { - log.Printf("Error closing proxy for session %s: %s", p.sessionId, err) + p.log.Error("Error closing proxy", + zap.Error(err), + ) } }() @@ -315,13 +329,18 @@ func (p *SessionProxy) recvPump() { break } - log.Printf("Error receiving message from proxy for session %s: %s", p.sessionId, err) + p.log.Error("Error receiving message from proxy", + zap.Error(err), + ) closeError = err break } if err := p.receiver.OnProxyMessage(msg); err != nil { - log.Printf("Error processing message %+v from proxy for session %s: %s", msg, p.sessionId, err) + p.log.Error("Error processing message from proxy", + zap.Stringer("message", msg), + zap.Error(err), + ) } } } @@ -352,6 +371,7 @@ func (c *GrpcClient) ProxySession(ctx context.Context, sessionId string, receive } proxy := &SessionProxy{ + log: c.log, sessionId: sessionId, receiver: receiver, @@ -368,7 +388,8 @@ type grpcClientsList struct { } type GrpcClients struct { - mu sync.RWMutex + log *zap.Logger + mu sync.RWMutex clientsMap map[string]*grpcClientsList clients []*GrpcClient @@ -391,10 +412,11 @@ type GrpcClients struct { closeFunc context.CancelFunc } -func NewGrpcClients(config *goconf.ConfigFile, etcdClient *EtcdClient, dnsMonitor *DnsMonitor) (*GrpcClients, error) { +func NewGrpcClients(log *zap.Logger, config *goconf.ConfigFile, etcdClient *EtcdClient, dnsMonitor *DnsMonitor) (*GrpcClients, error) { initializedCtx, initializedFunc := context.WithCancel(context.Background()) closeCtx, closeFunc := context.WithCancel(context.Background()) result := &GrpcClients{ + log: log, dnsMonitor: dnsMonitor, etcdClient: etcdClient, initializedCtx: initializedCtx, @@ -409,7 +431,7 @@ func NewGrpcClients(config *goconf.ConfigFile, etcdClient *EtcdClient, dnsMonito } func (c *GrpcClients) load(config *goconf.ConfigFile, fromReload bool) error { - creds, err := NewReloadableCredentials(config, false) + creds, err := NewReloadableCredentials(c.log, config, false) if err != nil { return err } @@ -447,7 +469,10 @@ func (c *GrpcClients) closeClient(client *GrpcClient) { } if err := client.Close(); err != nil { - log.Printf("Error closing client to %s: %s", client.Target(), err) + c.log.Error("Error closing client", + zap.String("target", client.Target()), + zap.Error(err), + ) } } @@ -499,18 +524,26 @@ loop: } if status.Code(err) != codes.Canceled { - log.Printf("Error checking GRPC server id of %s, retrying in %s: %s", client.Target(), backoff.NextWait(), err) + c.log.Error("Error checking GRPC server id, retrying", + zap.String("target", client.Target()), + zap.Duration("wait", backoff.NextWait()), + zap.Error(err), + ) } backoff.Wait(ctx) continue } if id == GrpcServerId { - log.Printf("GRPC target %s is this server, removing", client.Target()) + c.log.Info("GRPC target is this server, removing", + zap.String("target", client.Target()), + ) c.closeClient(client) client.SetSelf(true) } else { - log.Printf("Checked GRPC server id of %s", client.Target()) + c.log.Info("Checked GRPC server id", + zap.String("target", client.Target()), + ) } break loop } @@ -581,7 +614,7 @@ func (c *GrpcClients) loadTargetsStatic(config *goconf.ConfigFile, fromReload bo continue } - client, err := NewGrpcClient(target, nil, opts...) + client, err := NewGrpcClient(c.log, target, nil, opts...) if err != nil { for _, entry := range clientsMap { for _, client := range entry.clients { @@ -599,7 +632,9 @@ func (c *GrpcClients) loadTargetsStatic(config *goconf.ConfigFile, fromReload bo c.selfCheckWaitGroup.Add(1) go c.checkIsSelf(c.closeCtx, target, client) - log.Printf("Adding %s as GRPC target", client.Target()) + c.log.Info("Adding GRPC target", + zap.String("target", client.Target()), + ) entry, found := clientsMap[target] if !found { entry = &grpcClientsList{} @@ -612,7 +647,9 @@ func (c *GrpcClients) loadTargetsStatic(config *goconf.ConfigFile, fromReload bo for target := range removeTargets { if entry, found := clientsMap[target]; found { for _, client := range entry.clients { - log.Printf("Deleting GRPC target %s", client.Target()) + c.log.Info("Deleting GRPC target", + zap.String("target", client.Target()), + ) c.closeClient(client) } @@ -649,7 +686,9 @@ func (c *GrpcClients) onLookup(entry *DnsMonitorEntry, all []net.IP, added []net for _, client := range e.clients { if ip.Equal(client.ip) { mapModified = true - log.Printf("Removing connection to %s", client.Target()) + c.log.Info("Removing GRPC connection", + zap.String("target", client.Target()), + ) c.closeClient(client) c.wakeupForTesting() } @@ -665,16 +704,22 @@ func (c *GrpcClients) onLookup(entry *DnsMonitorEntry, all []net.IP, added []net } for _, ip := range added { - client, err := NewGrpcClient(target, ip, opts...) + client, err := NewGrpcClient(c.log, target, ip, opts...) if err != nil { - log.Printf("Error creating client to %s with IP %s: %s", target, ip.String(), err) + c.log.Error("Error creating GRPC client", + zap.String("target", target), + zap.Stringer("ip", ip), + zap.Error(err), + ) continue } c.selfCheckWaitGroup.Add(1) go c.checkIsSelf(c.closeCtx, target, client) - log.Printf("Adding %s as GRPC target", client.Target()) + c.log.Info("Adding GRPC target", + zap.String("target", client.Target()), + ) newClients = append(newClients, client) mapModified = true c.wakeupForTesting() @@ -727,9 +772,14 @@ func (c *GrpcClients) EtcdClientCreated(client *EtcdClient) { if errors.Is(err, context.Canceled) { return } else if errors.Is(err, context.DeadlineExceeded) { - log.Printf("Timeout getting initial list of GRPC targets, retry in %s", backoff.NextWait()) + c.log.Error("Timeout getting initial list of GRPC targets, retry", + zap.Duration("wait", backoff.NextWait()), + ) } else { - log.Printf("Could not get initial list of GRPC targets, retry in %s: %s", backoff.NextWait(), err) + c.log.Error("Could not get initial list of GRPC targets, retry", + zap.Duration("wait", backoff.NextWait()), + zap.Error(err), + ) } backoff.Wait(c.closeCtx) @@ -749,7 +799,11 @@ func (c *GrpcClients) EtcdClientCreated(client *EtcdClient) { for c.closeCtx.Err() == nil { var err error if nextRevision, err = client.Watch(c.closeCtx, c.targetPrefix, nextRevision, c, clientv3.WithPrefix()); err != nil { - log.Printf("Error processing watch for %s (%s), retry in %s", c.targetPrefix, err, backoff.NextWait()) + c.log.Error("Error processing watch, retry", + zap.String("prefix", c.targetPrefix), + zap.Duration("wait", backoff.NextWait()), + zap.Error(err), + ) backoff.Wait(c.closeCtx) continue } @@ -758,7 +812,10 @@ func (c *GrpcClients) EtcdClientCreated(client *EtcdClient) { backoff.Reset() prevRevision = nextRevision } else { - log.Printf("Processing watch for %s interrupted, retry in %s", c.targetPrefix, backoff.NextWait()) + c.log.Warn("Processing watch interrupted, retry", + zap.String("prefix", c.targetPrefix), + zap.Duration("wait", backoff.NextWait()), + ) backoff.Wait(c.closeCtx) } } @@ -778,11 +835,19 @@ func (c *GrpcClients) getGrpcTargets(ctx context.Context, client *EtcdClient, ta func (c *GrpcClients) EtcdKeyUpdated(client *EtcdClient, key string, data []byte, prevValue []byte) { var info GrpcTargetInformationEtcd if err := json.Unmarshal(data, &info); err != nil { - log.Printf("Could not decode GRPC target %s=%s: %s", key, string(data), err) + c.log.Error("Could not decode GRPC target", + zap.String("key", key), + zap.ByteString("data", data), + zap.Error(err), + ) return } if err := info.CheckValid(); err != nil { - log.Printf("Received invalid GRPC target %s=%s: %s", key, string(data), err) + c.log.Error("Received invalid GRPC target", + zap.String("key", key), + zap.ByteString("data", data), + zap.Error(err), + ) return } @@ -796,21 +861,29 @@ func (c *GrpcClients) EtcdKeyUpdated(client *EtcdClient, key string, data []byte } if _, found := c.clientsMap[info.Address]; found { - log.Printf("GRPC target %s already exists, ignoring %s", info.Address, key) + c.log.Warn("GRPC target already exists, ignoring", + zap.String("target", info.Address), + zap.String("key", key), + ) return } opts := c.dialOptions.Load().([]grpc.DialOption) - cl, err := NewGrpcClient(info.Address, nil, opts...) + cl, err := NewGrpcClient(c.log, info.Address, nil, opts...) if err != nil { - log.Printf("Could not create GRPC client for target %s: %s", info.Address, err) + c.log.Error("Could not create GRPC client", + zap.String("target", info.Address), + zap.Error(err), + ) return } c.selfCheckWaitGroup.Add(1) go c.checkIsSelf(c.closeCtx, info.Address, cl) - log.Printf("Adding %s as GRPC target", cl.Target()) + c.log.Info("Adding GRPC target", + zap.String("target", cl.Target()), + ) if c.clientsMap == nil { c.clientsMap = make(map[string]*grpcClientsList) @@ -834,7 +907,9 @@ func (c *GrpcClients) EtcdKeyDeleted(client *EtcdClient, key string, prevValue [ func (c *GrpcClients) removeEtcdClientLocked(key string) { info, found := c.targetInformation[key] if !found { - log.Printf("No connection found for %s, ignoring", key) + c.log.Debug("No connection found, ignoring", + zap.String("key", key), + ) c.wakeupForTesting() return } @@ -846,7 +921,10 @@ func (c *GrpcClients) removeEtcdClientLocked(key string) { } for _, client := range entry.clients { - log.Printf("Removing connection to %s (from %s)", client.Target(), key) + c.log.Info("Removing GRPC connection", + zap.String("target", client.Target()), + zap.String("key", key), + ) c.closeClient(client) } delete(c.clientsMap, info.Address) @@ -880,7 +958,9 @@ func (c *GrpcClients) wakeupForTesting() { func (c *GrpcClients) Reload(config *goconf.ConfigFile) { if err := c.load(config, true); err != nil { - log.Printf("Could not reload RPC clients: %s", err) + c.log.Error("Could not reload RPC clients", + zap.Error(err), + ) } } @@ -891,7 +971,10 @@ func (c *GrpcClients) Close() { for _, entry := range c.clientsMap { for _, client := range entry.clients { if err := client.Close(); err != nil { - log.Printf("Error closing client to %s: %s", client.Target(), err) + c.log.Error("Error closing GRPC client", + zap.String("target", client.Target()), + zap.Error(err), + ) } } diff --git a/grpc_client_test.go b/grpc_client_test.go index 3606aec2..63e7c78e 100644 --- a/grpc_client_test.go +++ b/grpc_client_test.go @@ -52,8 +52,9 @@ func (c *GrpcClients) getWakeupChannelForTesting() <-chan struct{} { } func NewGrpcClientsForTestWithConfig(t *testing.T, config *goconf.ConfigFile, etcdClient *EtcdClient) (*GrpcClients, *DnsMonitor) { + log := GetLoggerForTest(t) dnsMonitor := newDnsMonitorForTest(t, time.Hour) // will be updated manually - client, err := NewGrpcClients(config, etcdClient, dnsMonitor) + client, err := NewGrpcClients(log, config, etcdClient, dnsMonitor) require.NoError(t, err) t.Cleanup(func() { client.Close() @@ -77,7 +78,8 @@ func NewGrpcClientsWithEtcdForTest(t *testing.T, etcd *embed.Etcd) (*GrpcClients config.AddOption("grpc", "targettype", "etcd") config.AddOption("grpc", "targetprefix", "/grpctargets") - etcdClient, err := NewEtcdClient(config, "") + log := GetLoggerForTest(t) + etcdClient, err := NewEtcdClient(log, config, "") require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, etcdClient.Close()) @@ -108,7 +110,6 @@ func waitForEvent(ctx context.Context, t *testing.T, ch <-chan struct{}) { } func Test_GrpcClients_EtcdInitial(t *testing.T) { - CatchLogForTest(t) ensureNoGoroutinesLeak(t, func(t *testing.T) { _, addr1 := NewGrpcServerForTest(t) _, addr2 := NewGrpcServerForTest(t) @@ -130,7 +131,6 @@ func Test_GrpcClients_EtcdInitial(t *testing.T) { func Test_GrpcClients_EtcdUpdate(t *testing.T) { t.Parallel() - CatchLogForTest(t) assert := assert.New(t) etcd := NewEtcdForTest(t) client, _ := NewGrpcClientsWithEtcdForTest(t, etcd) @@ -176,7 +176,6 @@ func Test_GrpcClients_EtcdUpdate(t *testing.T) { func Test_GrpcClients_EtcdIgnoreSelf(t *testing.T) { t.Parallel() - CatchLogForTest(t) assert := assert.New(t) etcd := NewEtcdForTest(t) client, _ := NewGrpcClientsWithEtcdForTest(t, etcd) @@ -214,7 +213,6 @@ func Test_GrpcClients_EtcdIgnoreSelf(t *testing.T) { } func Test_GrpcClients_DnsDiscovery(t *testing.T) { - CatchLogForTest(t) ensureNoGoroutinesLeak(t, func(t *testing.T) { assert := assert.New(t) lookup := newMockDnsLookupForTest(t) @@ -262,7 +260,6 @@ func Test_GrpcClients_DnsDiscovery(t *testing.T) { func Test_GrpcClients_DnsDiscoveryInitialFailed(t *testing.T) { t.Parallel() - CatchLogForTest(t) assert := assert.New(t) lookup := newMockDnsLookupForTest(t) target := "testgrpc:12345" @@ -292,7 +289,6 @@ func Test_GrpcClients_DnsDiscoveryInitialFailed(t *testing.T) { } func Test_GrpcClients_Encryption(t *testing.T) { - CatchLogForTest(t) ensureNoGoroutinesLeak(t, func(t *testing.T) { require := require.New(t) serverKey, err := rsa.GenerateKey(rand.Reader, 1024) diff --git a/grpc_common.go b/grpc_common.go index b7df93ea..6a35094f 100644 --- a/grpc_common.go +++ b/grpc_common.go @@ -25,10 +25,10 @@ import ( "context" "crypto/tls" "fmt" - "log" "net" "github.com/dlintw/goconf" + "go.uber.org/zap" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" ) @@ -134,7 +134,7 @@ func (c *reloadableCredentials) Close() { } } -func NewReloadableCredentials(config *goconf.ConfigFile, server bool) (credentials.TransportCredentials, error) { +func NewReloadableCredentials(log *zap.Logger, config *goconf.ConfigFile, server bool) (credentials.TransportCredentials, error) { var prefix string var caPrefix string if server { @@ -153,7 +153,7 @@ func NewReloadableCredentials(config *goconf.ConfigFile, server bool) (credentia var loader *CertificateReloader var err error if certificateFile != "" && keyFile != "" { - loader, err = NewCertificateReloader(certificateFile, keyFile) + loader, err = NewCertificateReloader(log, certificateFile, keyFile) if err != nil { return nil, fmt.Errorf("invalid GRPC %s certificate / key in %s / %s: %w", prefix, certificateFile, keyFile, err) } @@ -161,7 +161,7 @@ func NewReloadableCredentials(config *goconf.ConfigFile, server bool) (credentia var pool *CertPoolReloader if caFile != "" { - pool, err = NewCertPoolReloader(caFile) + pool, err = NewCertPoolReloader(log, caFile) if err != nil { return nil, err } @@ -173,9 +173,9 @@ func NewReloadableCredentials(config *goconf.ConfigFile, server bool) (credentia if loader == nil && pool == nil { if server { - log.Printf("WARNING: No GRPC server certificate and/or key configured, running unencrypted") + log.Warn("No GRPC server certificate and/or key configured, running unencrypted") } else { - log.Printf("WARNING: No GRPC CA configured, expecting unencrypted connections") + log.Warn("No GRPC CA configured, expecting unencrypted connections") } return insecure.NewCredentials(), nil } diff --git a/grpc_remote_client.go b/grpc_remote_client.go index 8940fdec..18c9f20c 100644 --- a/grpc_remote_client.go +++ b/grpc_remote_client.go @@ -27,9 +27,9 @@ import ( "errors" "fmt" "io" - "log" "sync/atomic" + "go.uber.org/zap" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" @@ -49,6 +49,7 @@ func getMD(md metadata.MD, key string) string { // remoteGrpcClient is a remote client connecting from a GRPC proxy to a Hub. type remoteGrpcClient struct { + log *zap.Logger hub *Hub client RpcSessions_ProxySessionServer @@ -64,7 +65,7 @@ type remoteGrpcClient struct { messages chan WritableClientMessage } -func newRemoteGrpcClient(hub *Hub, request RpcSessions_ProxySessionServer) (*remoteGrpcClient, error) { +func newRemoteGrpcClient(log *zap.Logger, hub *Hub, request RpcSessions_ProxySessionServer) (*remoteGrpcClient, error) { md, found := metadata.FromIncomingContext(request.Context()) if !found { return nil, errors.New("no metadata provided") @@ -73,6 +74,7 @@ func newRemoteGrpcClient(hub *Hub, request RpcSessions_ProxySessionServer) (*rem closeCtx, closeFunc := context.WithCancelCause(context.Background()) result := &remoteGrpcClient{ + log: log, hub: hub, client: request, @@ -105,7 +107,10 @@ func (c *remoteGrpcClient) readPump() { } if status.Code(err) != codes.Canceled { - log.Printf("Error reading from remote client for session %s: %s", c.sessionId, err) + c.log.Error("Error reading from remote client for session", + zap.String("sessionid", c.sessionId), + zap.Error(err), + ) closeError = err } break @@ -193,7 +198,10 @@ func (c *remoteGrpcClient) SendMessage(message WritableClientMessage) bool { case c.messages <- message: return true default: - log.Printf("Message queue for remote client of session %s is full, not sending %+v", c.sessionId, message) + c.log.Warn("Message queue for remote client of session is full, not sending", + zap.String("sessionid", c.sessionId), + zap.Any("message", message), + ) return false } } @@ -215,7 +223,11 @@ func (c *remoteGrpcClient) run() error { case msg := <-c.messages: data, err := json.Marshal(msg) if err != nil { - log.Printf("Error marshalling %+v for remote client for session %s: %s", msg, c.sessionId, err) + c.log.Error("Error marshalling message for remote client for session", + zap.Any("message", msg), + zap.String("sessionid", c.sessionId), + zap.Error(err), + ) continue } diff --git a/grpc_server.go b/grpc_server.go index 0ccb1020..237fa032 100644 --- a/grpc_server.go +++ b/grpc_server.go @@ -27,12 +27,12 @@ import ( "encoding/hex" "errors" "fmt" - "log" "net" "net/url" "os" "github.com/dlintw/goconf" + "go.uber.org/zap" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" @@ -69,6 +69,7 @@ type GrpcServer struct { UnimplementedRpcMcuServer UnimplementedRpcSessionsServer + log *zap.Logger creds credentials.TransportCredentials conn *grpc.Server listener net.Listener @@ -77,7 +78,7 @@ type GrpcServer struct { hub GrpcServerHub } -func NewGrpcServer(config *goconf.ConfigFile) (*GrpcServer, error) { +func NewGrpcServer(log *zap.Logger, config *goconf.ConfigFile) (*GrpcServer, error) { var listener net.Listener if addr, _ := GetStringOptionWithEnv(config, "grpc", "listen"); addr != "" { var err error @@ -87,13 +88,14 @@ func NewGrpcServer(config *goconf.ConfigFile) (*GrpcServer, error) { } } - creds, err := NewReloadableCredentials(config, true) + creds, err := NewReloadableCredentials(log, config, true) if err != nil { return nil, err } conn := grpc.NewServer(grpc.Creds(creds)) result := &GrpcServer{ + log: log, creds: creds, conn: conn, listener: listener, @@ -123,8 +125,9 @@ func (s *GrpcServer) Close() { func (s *GrpcServer) LookupResumeId(ctx context.Context, request *LookupResumeIdRequest) (*LookupResumeIdReply, error) { statsGrpcServerCalls.WithLabelValues("LookupResumeId").Inc() - // TODO: Remove debug logging - log.Printf("Lookup session for resume id %s", request.ResumeId) + s.log.Debug("Lookup session for resume id", + zap.String("resumeid", request.ResumeId), + ) session := s.hub.GetSessionByResumeId(request.ResumeId) if session == nil { return nil, status.Error(codes.NotFound, "no such room session id") @@ -137,8 +140,9 @@ func (s *GrpcServer) LookupResumeId(ctx context.Context, request *LookupResumeId func (s *GrpcServer) LookupSessionId(ctx context.Context, request *LookupSessionIdRequest) (*LookupSessionIdReply, error) { statsGrpcServerCalls.WithLabelValues("LookupSessionId").Inc() - // TODO: Remove debug logging - log.Printf("Lookup session id for room session id %s", request.RoomSessionId) + s.log.Debug("Lookup session id for room session id", + zap.String("roomsessionid", request.RoomSessionId), + ) sid, err := s.hub.GetSessionIdByRoomSessionId(request.RoomSessionId) if errors.Is(err, ErrNoSuchRoomSession) { return nil, status.Error(codes.NotFound, "no such room session id") @@ -148,7 +152,10 @@ func (s *GrpcServer) LookupSessionId(ctx context.Context, request *LookupSession if sid != "" && request.DisconnectReason != "" { if session := s.hub.GetSessionByPublicId(sid); session != nil { - log.Printf("Closing session %s because same room session %s connected", session.PublicId(), request.RoomSessionId) + s.log.Info("Closing session because same room session connected", + zap.String("sessionid", session.PublicId()), + zap.String("roomsessionid", request.RoomSessionId), + ) session.LeaveRoom(false) switch sess := session.(type) { case *ClientSession: @@ -166,8 +173,11 @@ func (s *GrpcServer) LookupSessionId(ctx context.Context, request *LookupSession func (s *GrpcServer) IsSessionInCall(ctx context.Context, request *IsSessionInCallRequest) (*IsSessionInCallReply, error) { statsGrpcServerCalls.WithLabelValues("IsSessionInCall").Inc() - // TODO: Remove debug logging - log.Printf("Check if session %s is in call %s on %s", request.SessionId, request.RoomId, request.BackendUrl) + s.log.Debug("Check if session is in call", + zap.String("sessionid", request.SessionId), + zap.String("room", request.RoomId), + zap.String("backend", request.BackendUrl), + ) session := s.hub.GetSessionByPublicId(request.SessionId) if session == nil { return nil, status.Error(codes.NotFound, "no such session id") @@ -187,8 +197,10 @@ func (s *GrpcServer) IsSessionInCall(ctx context.Context, request *IsSessionInCa func (s *GrpcServer) GetPublisherId(ctx context.Context, request *GetPublisherIdRequest) (*GetPublisherIdReply, error) { statsGrpcServerCalls.WithLabelValues("GetPublisherId").Inc() - // TODO: Remove debug logging - log.Printf("Get %s publisher id for session %s", request.StreamType, request.SessionId) + s.log.Debug("Get publisher id for session", + zap.Any("streamtype", request.StreamType), + zap.String("sessionid", request.SessionId), + ) session := s.hub.GetSessionByPublicId(request.SessionId) if session == nil { return nil, status.Error(codes.NotFound, "no such session") @@ -246,7 +258,7 @@ func (s *GrpcServer) ProxySession(request RpcSessions_ProxySessionServer) error return status.Error(codes.Internal, "invalid hub type") } - client, err := newRemoteGrpcClient(hub, request) + client, err := newRemoteGrpcClient(s.log, hub, request) if err != nil { return err } diff --git a/grpc_server_test.go b/grpc_server_test.go index ffa6ceb9..d39ee9b6 100644 --- a/grpc_server_test.go +++ b/grpc_server_test.go @@ -62,11 +62,12 @@ func (s *GrpcServer) WaitForCertPoolReload(ctx context.Context) error { } func NewGrpcServerForTestWithConfig(t *testing.T, config *goconf.ConfigFile) (server *GrpcServer, addr string) { + log := GetLoggerForTest(t) for port := 50000; port < 50100; port++ { addr = net.JoinHostPort("127.0.0.1", strconv.Itoa(port)) config.AddOption("grpc", "listen", addr) var err error - server, err = NewGrpcServer(config) + server, err = NewGrpcServer(log, config) if isErrorAddressAlreadyInUse(err) { continue } @@ -96,7 +97,6 @@ func NewGrpcServerForTest(t *testing.T) (server *GrpcServer, addr string) { } func Test_GrpcServer_ReloadCerts(t *testing.T) { - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) key, err := rsa.GenerateKey(rand.Reader, 1024) @@ -167,8 +167,8 @@ func Test_GrpcServer_ReloadCerts(t *testing.T) { } func Test_GrpcServer_ReloadCA(t *testing.T) { - CatchLogForTest(t) require := require.New(t) + log := GetLoggerForTest(t) serverKey, err := rsa.GenerateKey(rand.Reader, 1024) require.NoError(err) clientKey, err := rsa.GenerateKey(rand.Reader, 1024) @@ -211,7 +211,7 @@ func Test_GrpcServer_ReloadCA(t *testing.T) { RootCAs: pool, Certificates: []tls.Certificate{pair1}, } - client1, err := NewGrpcClient(addr, nil, grpc.WithTransportCredentials(credentials.NewTLS(cfg1))) + client1, err := NewGrpcClient(log, addr, nil, grpc.WithTransportCredentials(credentials.NewTLS(cfg1))) require.NoError(err) defer client1.Close() // nolint @@ -237,7 +237,7 @@ func Test_GrpcServer_ReloadCA(t *testing.T) { RootCAs: pool, Certificates: []tls.Certificate{pair2}, } - client2, err := NewGrpcClient(addr, nil, grpc.WithTransportCredentials(credentials.NewTLS(cfg2))) + client2, err := NewGrpcClient(log, addr, nil, grpc.WithTransportCredentials(credentials.NewTLS(cfg2))) require.NoError(err) defer client2.Close() // nolint diff --git a/hub.go b/hub.go index 3808559a..f4d10fed 100644 --- a/hub.go +++ b/hub.go @@ -36,7 +36,6 @@ import ( "errors" "fmt" "hash/fnv" - "log" "net" "net/http" "net/url" @@ -51,6 +50,8 @@ import ( "github.com/gorilla/mux" "github.com/gorilla/securecookie" "github.com/gorilla/websocket" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" ) var ( @@ -124,6 +125,7 @@ func init() { } type Hub struct { + log *zap.Logger version string events AsyncEvents upgrader websocket.Upgrader @@ -188,13 +190,15 @@ type Hub struct { federationTimeout time.Duration } -func NewHub(config *goconf.ConfigFile, events AsyncEvents, rpcServer *GrpcServer, rpcClients *GrpcClients, etcdClient *EtcdClient, r *mux.Router, version string) (*Hub, error) { +func NewHub(log *zap.Logger, config *goconf.ConfigFile, events AsyncEvents, rpcServer *GrpcServer, rpcClients *GrpcClients, etcdClient *EtcdClient, r *mux.Router, version string) (*Hub, error) { hashKey, _ := config.GetString("sessions", "hashkey") switch len(hashKey) { case 32: case 64: default: - log.Printf("WARNING: The sessions hash key should be 32 or 64 bytes but is %d bytes", len(hashKey)) + log.Warn("The sessions hash key should be 32 or 64 bytes", + zap.Int("len", len(hashKey)), + ) } blockKey, _ := config.GetString("sessions", "blockkey") @@ -211,7 +215,7 @@ func NewHub(config *goconf.ConfigFile, events AsyncEvents, rpcServer *GrpcServer internalClientsSecret, _ := config.GetString("clients", "internalsecret") if internalClientsSecret == "" { - log.Println("WARNING: No shared secret has been set for internal clients.") + log.Warn("No shared secret has been set for internal clients.") } maxConcurrentRequestsPerHost, _ := config.GetInt("backend", "connectionsperhost") @@ -219,18 +223,22 @@ func NewHub(config *goconf.ConfigFile, events AsyncEvents, rpcServer *GrpcServer maxConcurrentRequestsPerHost = defaultMaxConcurrentRequestsPerHost } - backend, err := NewBackendClient(config, maxConcurrentRequestsPerHost, version, etcdClient) + backend, err := NewBackendClient(log, config, maxConcurrentRequestsPerHost, version, etcdClient) if err != nil { return nil, err } - log.Printf("Using a maximum of %d concurrent backend connections per host", maxConcurrentRequestsPerHost) + log.Info("Using a maximum of concurrent backend connections per host", + zap.Int("limit", maxConcurrentRequestsPerHost), + ) backendTimeoutSeconds, _ := config.GetInt("backend", "timeout") if backendTimeoutSeconds <= 0 { backendTimeoutSeconds = defaultBackendTimeoutSeconds } backendTimeout := time.Duration(backendTimeoutSeconds) * time.Second - log.Printf("Using a timeout of %s for backend connections", backendTimeout) + log.Info("Using a timeout for backend connections", + zap.Duration("timeout", backendTimeout), + ) mcuTimeoutSeconds, _ := config.GetInt("mcu", "timeout") if mcuTimeoutSeconds <= 0 { @@ -240,7 +248,7 @@ func NewHub(config *goconf.ConfigFile, events AsyncEvents, rpcServer *GrpcServer allowSubscribeAnyStream, _ := config.GetBool("app", "allowsubscribeany") if allowSubscribeAnyStream { - log.Printf("WARNING: Allow subscribing any streams, this is insecure and should only be enabled for testing") + log.Warn("Allow subscribing any streams, this is insecure and should only be enabled for testing") } trustedProxies, _ := config.GetString("app", "trustedproxies") @@ -251,7 +259,7 @@ func NewHub(config *goconf.ConfigFile, events AsyncEvents, rpcServer *GrpcServer skipFederationVerify, _ := config.GetBool("federation", "skipverify") if skipFederationVerify { - log.Println("WARNING: Federation target verification is disabled!") + log.Warn("Federation target verification is disabled!") } federationTimeoutSeconds, _ := config.GetInt("federation", "timeout") if federationTimeoutSeconds <= 0 { @@ -260,10 +268,14 @@ func NewHub(config *goconf.ConfigFile, events AsyncEvents, rpcServer *GrpcServer federationTimeout := time.Duration(federationTimeoutSeconds) * time.Second if !trustedProxiesIps.Empty() { - log.Printf("Trusted proxies: %s", trustedProxiesIps) + log.Info("Trusted proxies", + zap.Any("proxies", trustedProxiesIps), + ) } else { trustedProxiesIps = DefaultTrustedProxies - log.Printf("No trusted proxies configured, only allowing for %s", trustedProxiesIps) + log.Info("No trusted proxies configured, allowing for default list", + zap.Any("proxies", trustedProxiesIps), + ) } decodeCaches := make([]*LruCache, 0, numDecodeCaches) @@ -271,12 +283,12 @@ func NewHub(config *goconf.ConfigFile, events AsyncEvents, rpcServer *GrpcServer decodeCaches = append(decodeCaches, NewLruCache(decodeCacheSize)) } - roomSessions, err := NewBuiltinRoomSessions(rpcClients) + roomSessions, err := NewBuiltinRoomSessions(log, rpcClients) if err != nil { return nil, err } - roomPing, err := NewRoomPing(backend, backend.capabilities) + roomPing, err := NewRoomPing(log, backend, backend.capabilities) if err != nil { return nil, err } @@ -295,30 +307,35 @@ func NewHub(config *goconf.ConfigFile, events AsyncEvents, rpcServer *GrpcServer if geoipUrl != "" { if strings.HasPrefix(geoipUrl, "file://") { geoipUrl = geoipUrl[7:] - log.Printf("Using GeoIP database from %s", geoipUrl) - geoip, err = NewGeoLookupFromFile(geoipUrl) + log.Info("Using GeoIP database", + zap.String("filename", geoipUrl), + ) + geoip, err = NewGeoLookupFromFile(log, geoipUrl) } else { - log.Printf("Downloading GeoIP database from %s", geoipUrl) - geoip, err = NewGeoLookupFromUrl(geoipUrl) + log.Info("Downloading GeoIP database", + zap.String("url", geoipUrl), + ) + geoip, err = NewGeoLookupFromUrl(log, geoipUrl) } if err != nil { return nil, err } } else { - log.Printf("Not using GeoIP database") + log.Info("Not using GeoIP database") } - geoipOverrides, err := LoadGeoIPOverrides(config, false) + geoipOverrides, err := LoadGeoIPOverrides(log, config, false) if err != nil { return nil, err } - throttler, err := NewMemoryThrottler() + throttler, err := NewMemoryThrottler(log) if err != nil { return nil, err } hub := &Hub{ + log: log, version: version, events: events, upgrader: websocket.Upgrader{ @@ -410,7 +427,9 @@ func (h *Hub) SetMcu(mcu Mcu) { welcome.Welcome.RemoveFeature(ServerFeatureMcu, ServerFeatureSimulcast, ServerFeatureUpdateSdp) } else { - log.Printf("Using a timeout of %s for MCU requests", h.mcuTimeout) + h.log.Info("Using a timeout for MCU requests", + zap.Duration("timeout", h.mcuTimeout), + ) h.info.AddFeature(ServerFeatureMcu, ServerFeatureSimulcast, ServerFeatureUpdateSdp) h.infoInternal.AddFeature(ServerFeatureMcu, ServerFeatureSimulcast, ServerFeatureUpdateSdp) @@ -445,7 +464,9 @@ func (h *Hub) updateGeoDatabase() { defer h.geoipUpdating.Store(false) backoff, err := NewExponentialBackoff(time.Second, 5*time.Minute) if err != nil { - log.Printf("Could not create exponential backoff: %s", err) + h.log.Error("Could not create exponential backoff", + zap.Error(err), + ) return } @@ -455,7 +476,10 @@ func (h *Hub) updateGeoDatabase() { break } - log.Printf("Could not update GeoIP database, will retry in %s (%s)", backoff.NextWait(), err) + h.log.Error("Could not update GeoIP database, will retry", + zap.Duration("wait", backoff.NextWait()), + zap.Error(err), + ) backoff.Wait(context.Background()) } } @@ -507,17 +531,24 @@ func (h *Hub) Reload(config *goconf.ConfigFile) { trustedProxies, _ := config.GetString("app", "trustedproxies") if trustedProxiesIps, err := ParseAllowedIps(trustedProxies); err == nil { if !trustedProxiesIps.Empty() { - log.Printf("Trusted proxies: %s", trustedProxiesIps) + h.log.Info("Trusted proxies", + zap.Any("proxies", trustedProxiesIps), + ) } else { trustedProxiesIps = DefaultTrustedProxies - log.Printf("No trusted proxies configured, only allowing for %s", trustedProxiesIps) + h.log.Info("No trusted proxies configured, allowing for default list", + zap.Any("proxies", trustedProxiesIps), + ) } h.trustedProxies.Store(trustedProxiesIps) } else { - log.Printf("Error parsing trusted proxies from \"%s\": %s", trustedProxies, err) + h.log.Error("Error parsing trusted proxies", + zap.String("trusted", trustedProxies), + zap.Error(err), + ) } - geoipOverrides, _ := LoadGeoIPOverrides(config, true) + geoipOverrides, _ := LoadGeoIPOverrides(h.log, config, true) if len(geoipOverrides) > 0 { h.geoipOverrides.Store(&geoipOverrides) } else { @@ -677,7 +708,10 @@ func (h *Hub) checkExpiredSessions(now time.Time) { for session, expires := range h.expiredSessions { if now.After(expires) { h.mu.Unlock() - log.Printf("Closing expired session %s (private=%s)", session.PublicId(), session.PrivateId()) + h.log.Info("Closing expired session", + zap.String("sessionid", session.PublicId()), + zap.String("privateid", session.PrivateId()), + ) session.Close() h.mu.Lock() // Should already be deleted by the close code, but better be sure. @@ -856,7 +890,9 @@ func (h *Hub) processRegister(c HandlerClient, message *ClientMessage, backend * client, ok := c.(*Client) if !ok { - log.Printf("Can't register non-client %T", c) + h.log.Warn("Can't register non-client", + zap.Any("client", c), + ) client.SendMessage(message.NewWrappedErrorServerMessage(errors.New("can't register non-client"))) return } @@ -873,23 +909,41 @@ func (h *Hub) processRegister(c HandlerClient, message *ClientMessage, backend * return } + commonFields := []zapcore.Field{ + zap.String("backend", backend.Id()), + zap.String("addr", client.RemoteAddr()), + zap.String("country", client.Country()), + zap.String("useragent", client.UserAgent()), + zap.String("sessionid", publicSessionId), + zap.String("privateid", privateSessionId), + } userId := auth.Auth.UserId if userId != "" { - log.Printf("Register user %s@%s from %s in %s (%s) %s (private=%s)", userId, backend.Id(), client.RemoteAddr(), client.Country(), client.UserAgent(), publicSessionId, privateSessionId) + h.log.Info("Register user", + append(commonFields, zap.String("userid", userId))..., + ) } else if message.Hello.Auth.Type != HelloClientTypeClient { - log.Printf("Register %s@%s from %s in %s (%s) %s (private=%s)", message.Hello.Auth.Type, backend.Id(), client.RemoteAddr(), client.Country(), client.UserAgent(), publicSessionId, privateSessionId) + h.log.Info("Register", + append(commonFields, zap.String("type", message.Hello.Auth.Type))..., + ) } else { - log.Printf("Register anonymous@%s from %s in %s (%s) %s (private=%s)", backend.Id(), client.RemoteAddr(), client.Country(), client.UserAgent(), publicSessionId, privateSessionId) + h.log.Info("Register anonymous", + commonFields..., + ) } - session, err := NewClientSession(h, privateSessionId, publicSessionId, sessionIdData, backend, message.Hello, auth.Auth) + session, err := NewClientSession(h.log, h, privateSessionId, publicSessionId, sessionIdData, backend, message.Hello, auth.Auth) if err != nil { client.SendMessage(message.NewWrappedErrorServerMessage(err)) return } if err := backend.AddSession(session); err != nil { - log.Printf("Error adding session %s to backend %s: %s", session.PublicId(), backend.Id(), err) + h.log.Error("Error adding session to backend", + zap.String("sessionid", session.PublicId()), + zap.String("backend", backend.Id()), + zap.Error(err), + ) session.Close() client.SendMessage(message.NewWrappedErrorServerMessage(err)) return @@ -908,12 +962,20 @@ func (h *Hub) processRegister(c HandlerClient, message *ClientMessage, backend * count, err := c.GetSessionCount(ctx, backend.ParsedUrl()) if err != nil { - log.Printf("Received error while getting session count for %s from %s: %s", backend.Url(), c.Target(), err) + h.log.Error("Received error while getting session count", + zap.String("backend", backend.Url()), + zap.String("target", c.Target()), + zap.Error(err), + ) return } if count > 0 { - log.Printf("%d sessions connected for %s on %s", count, backend.Url(), c.Target()) + h.log.Debug("Clustered sessions connected", + zap.Uint32("count", count), + zap.String("backend", backend.Url()), + zap.String("target", c.Target()), + ) totalCount.Add(count) } }(client) @@ -921,7 +983,11 @@ func (h *Hub) processRegister(c HandlerClient, message *ClientMessage, backend * wg.Wait() if totalCount.Load() > limit { backend.RemoveSession(session) - log.Printf("Error adding session %s to backend %s: %s", session.PublicId(), backend.Id(), SessionLimitExceeded) + h.log.Error("Error adding session to backend", + zap.String("sessionid", session.PublicId()), + zap.String("backend", backend.Id()), + zap.Error(SessionLimitExceeded), + ) session.Close() client.SendMessage(message.NewWrappedErrorServerMessage(SessionLimitExceeded)) return @@ -973,7 +1039,10 @@ func (h *Hub) processUnregister(client HandlerClient) Session { } h.mu.Unlock() if session != nil { - log.Printf("Unregister %s (private=%s)", session.PublicId(), session.PrivateId()) + h.log.Info("Unregister session", + zap.String("sessionid", session.PublicId()), + zap.String("privateid", session.PrivateId()), + ) if c, ok := client.(*Client); ok { if cs, ok := session.(*ClientSession); ok { cs.ClearClient(c) @@ -987,27 +1056,39 @@ func (h *Hub) processUnregister(client HandlerClient) Session { func (h *Hub) processMessage(client HandlerClient, data []byte) { var message ClientMessage + decodeLog := h.log.With( + zap.String("addr", client.RemoteAddr()), + ) + session := client.GetSession() + if session != nil { + decodeLog = decodeLog.With( + zap.String("sessionid", session.PublicId()), + ) + } if err := message.UnmarshalJSON(data); err != nil { - if session := client.GetSession(); session != nil { - log.Printf("Error decoding message from client %s: %v", session.PublicId(), err) + decodeLog.Error("Error decoding message", + zap.Error(err), + ) + if session != nil { session.SendError(InvalidFormat) } else { - log.Printf("Error decoding message from %s: %v", client.RemoteAddr(), err) client.SendError(InvalidFormat) } return } if err := message.CheckValid(); err != nil { - if session := client.GetSession(); session != nil { - log.Printf("Invalid message %+v from client %s: %v", message, session.PublicId(), err) + decodeLog.Error("Invalid message received", + zap.Stringer("message", message), + zap.Error(err), + ) + if session != nil { if err, ok := err.(*Error); ok { session.SendMessage(message.NewErrorServerMessage(err)) } else { session.SendMessage(message.NewErrorServerMessage(InvalidFormat)) } } else { - log.Printf("Invalid message %+v from %s: %v", message, client.RemoteAddr(), err) if err, ok := err.(*Error); ok { client.SendMessage(message.NewErrorServerMessage(err)) } else { @@ -1018,8 +1099,6 @@ func (h *Hub) processMessage(client HandlerClient, data []byte) { } statsMessagesTotal.WithLabelValues(message.Type).Inc() - - session := client.GetSession() if session == nil { if message.Type != "hello" { client.SendMessage(message.NewErrorServerMessage(HelloExpected)) @@ -1056,9 +1135,13 @@ func (h *Hub) processMessage(client HandlerClient, data []byte) { case "bye": h.processByeMsg(client, &message) case "hello": - log.Printf("Ignore hello %+v for already authenticated connection %s", message.Hello, session.PublicId()) + h.log.Warn("Ignore hello for already authenticated connection", + zap.Any("hello", message.Hello), + ) default: - log.Printf("Ignore unknown message %+v from %s", message, session.PublicId()) + h.log.Warn("Ignore unknown message", + zap.Stringer("message", message), + ) } } @@ -1115,7 +1198,11 @@ func (h *Hub) tryProxyResume(c HandlerClient, resumeId string, message *ClientMe response, err := client.LookupResumeId(ctx, resumeId) if err != nil { - log.Printf("Could not lookup resume id %s on %s: %s", resumeId, client.Target(), err) + h.log.Error("Could not lookup resume id", + zap.String("resumeid", resumeId), + zap.String("target", client.Target()), + zap.Error(err), + ) return } @@ -1138,19 +1225,30 @@ func (h *Hub) tryProxyResume(c HandlerClient, resumeId string, message *ClientMe return false } - rs, err := NewRemoteSession(h, client, info.client, info.response.SessionId) + rs, err := NewRemoteSession(h.log, h, client, info.client, info.response.SessionId) if err != nil { - log.Printf("Could not create remote session %s on %s: %s", info.response.SessionId, info.client.Target(), err) + h.log.Error("Could not create remote session", + zap.String("sessionid", info.response.SessionId), + zap.String("target", info.client.Target()), + zap.Error(err), + ) return false } if err := rs.Start(message); err != nil { rs.Close() - log.Printf("Could not start remote session %s on %s: %s", info.response.SessionId, info.client.Target(), err) + h.log.Error("Could not start remote session", + zap.String("sessionid", info.response.SessionId), + zap.String("target", info.client.Target()), + zap.Error(err), + ) return false } - log.Printf("Proxy session %s to %s", info.response.SessionId, info.client.Target()) + h.log.Info("Proxy session created", + zap.String("sessionid", info.response.SessionId), + zap.String("target", info.client.Target()), + ) h.mu.Lock() defer h.mu.Unlock() h.remoteSessions[rs] = true @@ -1167,7 +1265,9 @@ func (h *Hub) processHello(client HandlerClient, message *ClientMessage) { client.SendMessage(message.NewErrorServerMessage(TooManyRequests)) return } else if err != nil { - log.Printf("Error checking for bruteforce: %s", err) + h.log.Error("Error checking for bruteforce", + zap.Error(err), + ) client.SendMessage(message.NewWrappedErrorServerMessage(err)) return } @@ -1202,7 +1302,10 @@ func (h *Hub) processHello(client HandlerClient, message *ClientMessage) { if !ok { // Should never happen as clients only can resume their own sessions. h.mu.Unlock() - log.Printf("Client resumed non-client session %s (private=%s)", session.PublicId(), session.PrivateId()) + h.log.Warn("Client resumed non-client session", + zap.String("sessionid", session.PublicId()), + zap.String("privateid", session.PrivateId()), + ) statsHubSessionResumeFailed.Inc() client.SendMessage(message.NewErrorServerMessage(NoSuchSession)) return @@ -1215,7 +1318,10 @@ func (h *Hub) processHello(client HandlerClient, message *ClientMessage) { } if prev := clientSession.SetClient(client); prev != nil { - log.Printf("Closing previous client from %s for session %s", prev.RemoteAddr(), session.PublicId()) + h.log.Info("Closing previous client", + zap.String("addr", prev.RemoteAddr()), + zap.String("sessionid", session.PublicId()), + ) prev.SendByeResponseWithReason(nil, "session_resumed") } @@ -1224,7 +1330,13 @@ func (h *Hub) processHello(client HandlerClient, message *ClientMessage) { delete(h.expectHelloClients, client) h.mu.Unlock() - log.Printf("Resume session from %s in %s (%s) %s (private=%s)", client.RemoteAddr(), client.Country(), client.UserAgent(), session.PublicId(), session.PrivateId()) + h.log.Info("Resume session", + zap.String("addr", client.RemoteAddr()), + zap.String("country", client.Country()), + zap.String("useragent", client.UserAgent()), + zap.String("sessionid", session.PublicId()), + zap.String("privateid", session.PrivateId()), + ) statsHubSessionsResumedTotal.WithLabelValues(clientSession.Backend().Id(), clientSession.ClientType()).Inc() h.sendHelloResponse(clientSession, message) @@ -1330,7 +1442,9 @@ func (h *Hub) processHelloV2(ctx context.Context, client HandlerClient, message return jwt.ParseEdPublicKeyFromPEM(data) } default: - log.Printf("Unexpected signing method: %v", token.Header["alg"]) + h.log.Warn("Unexpected signing method", + zap.Any("method", token.Header["alg"]), + ) return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) } @@ -1449,7 +1563,9 @@ func (h *Hub) processHelloInternal(client HandlerClient, message *ClientMessage) client.SendMessage(message.NewErrorServerMessage(TooManyRequests)) return } else if err != nil { - log.Printf("Error checking for bruteforce: %s", err) + h.log.Error("Error checking for bruteforce", + zap.Error(err), + ) client.SendMessage(message.NewWrappedErrorServerMessage(err)) return } @@ -1484,7 +1600,10 @@ func (h *Hub) disconnectByRoomSessionId(ctx context.Context, roomSessionId strin if err == ErrNoSuchRoomSession { return } else if err != nil { - log.Printf("Could not get session id for room session %s: %s", roomSessionId, err) + h.log.Error("Could not get session id for room session", + zap.String("roomsessionid", roomSessionId), + zap.Error(err), + ) return } @@ -1502,12 +1621,18 @@ func (h *Hub) disconnectByRoomSessionId(ctx context.Context, roomSessionId strin }, } if err := h.events.PublishSessionMessage(sessionId, backend, msg); err != nil { - log.Printf("Could not send reconnect bye to session %s: %s", sessionId, err) + h.log.Error("Could not send reconnect bye to session", + zap.String("sessionid", sessionId), + zap.Error(err), + ) } return } - log.Printf("Closing session %s because same room session %s connected", session.PublicId(), roomSessionId) + h.log.Info("Closing session because same room session connected", + zap.String("sessionid", session.PublicId()), + zap.String("roomsessionid", roomSessionId), + ) session.LeaveRoom(false) switch sess := session.(type) { case *ClientSession: @@ -1580,7 +1705,7 @@ func (h *Hub) processRoom(sess Session, message *ClientMessage) { } } if client == nil { - client, err = NewFederationClient(ctx, h, session, message) + client, err = NewFederationClient(ctx, h.log, h, session, message) } if err != nil { @@ -1623,7 +1748,12 @@ func (h *Hub) processRoom(sess Session, message *ClientMessage) { } } - log.Printf("Error creating federation client to %s for %s to join room %s: %s", federation.SignalingUrl, session.PublicId(), roomId, err) + h.log.Error("Error creating federation client to join room", + zap.String("url", federation.SignalingUrl), + zap.String("sessionid", session.PublicId()), + zap.String("roomid", roomId), + zap.Error(err), + ) session.SendMessage(message.NewErrorServerMessage( NewErrorDetail("federation_error", "Failed to create federation client.", details), )) @@ -1635,14 +1765,19 @@ func (h *Hub) processRoom(sess Session, message *ClientMessage) { roomSessionId := message.Room.SessionId if roomSessionId == "" { // TODO(jojo): Better make the session id required in the request. - log.Printf("User did not send a room session id, assuming session %s", session.PublicId()) roomSessionId = session.PublicId() + h.log.Warn("User did not send a room session id, assuming value", + zap.String("roomsessionid", roomSessionId), + ) } // Prefix room session id to allow using the same signaling server for two Nextcloud instances during development. // Otherwise the same room session id will be detected and the other session will be kicked. if err := session.UpdateRoomSessionId(FederatedRoomSessionIdPrefix + roomSessionId); err != nil { - log.Printf("Error updating room session id for session %s: %s", session.PublicId(), err) + h.log.Error("Error updating room session id for federated session", + zap.String("sessionid", session.PublicId()), + zap.Error(err), + ) } h.mu.Lock() @@ -1656,12 +1791,17 @@ func (h *Hub) processRoom(sess Session, message *ClientMessage) { roomSessionId := message.Room.SessionId if roomSessionId == "" { // TODO(jojo): Better make the session id required in the request. - log.Printf("User did not send a room session id, assuming session %s", session.PublicId()) roomSessionId = session.PublicId() + h.log.Warn("User did not send a room session id, assuming value", + zap.String("roomsessionid", roomSessionId), + ) } if err := session.UpdateRoomSessionId(roomSessionId); err != nil { - log.Printf("Error updating room session id for session %s: %s", session.PublicId(), err) + h.log.Error("Error updating room session id for session", + zap.String("sessionid", session.PublicId()), + zap.Error(err), + ) } session.SendMessage(message.NewErrorServerMessage( NewErrorDetail("already_joined", "Already joined this room.", &RoomErrorDetails{ @@ -1691,8 +1831,10 @@ func (h *Hub) processRoom(sess Session, message *ClientMessage) { sessionId := message.Room.SessionId if sessionId == "" { // TODO(jojo): Better make the session id required in the request. - log.Printf("User did not send a room session id, assuming session %s", session.PublicId()) sessionId = session.PublicId() + h.log.Warn("User did not send a room session id, assuming value", + zap.String("roomsessionid", sessionId), + ) } request := NewBackendClientRoomRequest(roomId, session.UserId(), sessionId) request.Room.UpdateFromSession(session) @@ -1784,7 +1926,11 @@ func (h *Hub) publishFederatedSessions() (int, *sync.WaitGroup) { defer cancel() if err := h.roomPing.SendPings(ctx, roomId, url, entries); err != nil { - log.Printf("Error pinging room %s for active entries %+v: %s", roomId, entries, err) + h.log.Error("Error pinging room for active entries", + zap.String("roomid", roomId), + zap.Any("entries", entries), + zap.Error(err), + ) } }(roomId, urls[u], e) } @@ -1813,7 +1959,7 @@ func (h *Hub) removeRoom(room *Room) { func (h *Hub) createRoom(id string, properties json.RawMessage, backend *Backend) (*Room, error) { // Note the write lock must be held. - room, err := NewRoom(id, properties, h, h.events, backend) + room, err := NewRoom(h.log, id, properties, h, h.events, backend) if err != nil { return nil, err } @@ -1899,7 +2045,11 @@ func (h *Hub) processMessageMsg(sess Session, message *ClientMessage) { var data MessageClientMessageData if err := json.Unmarshal(msg.Data, &data); err == nil { if err := data.CheckValid(); err != nil { - log.Printf("Invalid message %+v from client %s: %v", message, session.PublicId(), err) + h.log.Error("Invalid message received", + zap.Stringer("message", message), + zap.String("sessionid", session.PublicId()), + zap.Error(err), + ) if err, ok := err.(*Error); ok { session.SendMessage(message.NewErrorServerMessage(err)) } else { @@ -1947,7 +2097,9 @@ func (h *Hub) processMessageMsg(sess Session, message *ClientMessage) { return } - log.Printf("Closing screen publisher for %s", session.PublicId()) + h.log.Info("Closing screen publisher", + zap.String("sessionid", session.PublicId()), + ) ctx, cancel := context.WithTimeout(context.Background(), h.mcuTimeout) defer cancel() publisher.Close(ctx) @@ -2014,7 +2166,11 @@ func (h *Hub) processMessageMsg(sess Session, message *ClientMessage) { var data MessageClientMessageData if err := json.Unmarshal(msg.Data, &data); err == nil { if err := data.CheckValid(); err != nil { - log.Printf("Invalid message %+v from client %s: %v", message, session.PublicId(), err) + h.log.Error("Invalid message received", + zap.Stringer("message", message), + zap.String("sessionid", session.PublicId()), + zap.Error(err), + ) if err, ok := err.(*Error); ok { session.SendMessage(message.NewErrorServerMessage(err)) } else { @@ -2030,7 +2186,10 @@ func (h *Hub) processMessageMsg(sess Session, message *ClientMessage) { } } if subject == "" { - log.Printf("Unknown recipient in message %+v from %s", msg, session.PublicId()) + h.log.Warn("Unknown recipient in message", + zap.Any("message", msg), + zap.String("sessionid", session.PublicId()), + ) return } @@ -2050,7 +2209,11 @@ func (h *Hub) processMessageMsg(sess Session, message *ClientMessage) { // The recipient is connected to this instance, no need to go through asynchronous events. if clientData != nil && clientData.Type == "sendoffer" { if err := session.IsAllowedToSend(clientData); err != nil { - log.Printf("Session %s is not allowed to send offer for %s, ignoring (%s)", session.PublicId(), clientData.RoomType, err) + h.log.Info("Session is not allowed to send offer, ignoring", + zap.String("sessionid", session.PublicId()), + zap.String("streamtype", clientData.RoomType), + zap.Error(err), + ) sendNotAllowed(session, message, "Not allowed to send offer") return } @@ -2064,18 +2227,32 @@ func (h *Hub) processMessageMsg(sess Session, message *ClientMessage) { mc, err := recipient.GetOrCreateSubscriber(ctx, h.mcu, session.PublicId(), StreamType(clientData.RoomType)) if err != nil { - log.Printf("Could not create MCU subscriber for session %s to send %+v to %s: %s", session.PublicId(), clientData, recipient.PublicId(), err) + h.log.Error("Could not create MCU subscriber", + zap.String("sessionid", session.PublicId()), + zap.String("recipientsessionid", recipient.PublicId()), + zap.Any("message", clientData), + zap.Error(err), + ) sendMcuClientNotFound(session, message) return } else if mc == nil { - log.Printf("No MCU subscriber found for session %s to send %+v to %s", session.PublicId(), clientData, recipient.PublicId()) + h.log.Error("No MCU subscriber found", + zap.String("sessionid", session.PublicId()), + zap.String("recipientsessionid", recipient.PublicId()), + zap.Any("message", clientData), + ) sendMcuClientNotFound(session, message) return } mc.SendMessage(session.Context(), msg, clientData, func(err error, response map[string]interface{}) { if err != nil { - log.Printf("Could not send MCU message %+v for session %s to %s: %s", clientData, session.PublicId(), recipient.PublicId(), err) + h.log.Error("Could not send MCU message", + zap.String("sessionid", session.PublicId()), + zap.String("recipientsessionid", recipient.PublicId()), + zap.Any("message", clientData), + zap.Error(err), + ) sendMcuProcessingFailed(session, message) return } else if response == nil { @@ -2096,7 +2273,11 @@ func (h *Hub) processMessageMsg(sess Session, message *ClientMessage) { } else { if clientData != nil && clientData.Type == "sendoffer" { if err := session.IsAllowedToSend(clientData); err != nil { - log.Printf("Session %s is not allowed to send offer for %s, ignoring (%s)", session.PublicId(), clientData.RoomType, err) + h.log.Info("Session is not allowed to send offer, ignoring", + zap.String("sessionid", session.PublicId()), + zap.String("streamtype", clientData.RoomType), + zap.Error(err), + ) sendNotAllowed(session, message, "Not allowed to send offer") return } @@ -2110,7 +2291,11 @@ func (h *Hub) processMessageMsg(sess Session, message *ClientMessage) { }, } if err := h.events.PublishSessionMessage(recipientSessionId, session.Backend(), async); err != nil { - log.Printf("Error publishing message to remote session: %s", err) + h.log.Error("Error publishing message to remote session", + zap.String("recipientsessionid", recipientSessionId), + zap.String("backend", session.Backend().Id()), + zap.Error(err), + ) } return } @@ -2119,20 +2304,34 @@ func (h *Hub) processMessageMsg(sess Session, message *ClientMessage) { Type: "message", Message: response, } + commonFields := []zapcore.Field{ + zap.String("backend", session.Backend().Id()), + } var err error switch msg.Recipient.Type { case RecipientTypeSession: + commonFields = append(commonFields, + zap.String("recipientsessionid", recipientSessionId), + ) err = h.events.PublishSessionMessage(recipientSessionId, session.Backend(), async) case RecipientTypeUser: + commonFields = append(commonFields, + zap.String("recipientuserid", msg.Recipient.UserId), + ) err = h.events.PublishUserMessage(msg.Recipient.UserId, session.Backend(), async) case RecipientTypeRoom: + commonFields = append(commonFields, + zap.String("recipientroomid", room.Id()), + ) err = h.events.PublishRoomMessage(room.Id(), session.Backend(), async) default: err = fmt.Errorf("unsupported recipient type: %s", msg.Recipient.Type) } if err != nil { - log.Printf("Error publishing message to remote session: %s", err) + h.log.Error("Error publishing message to remote", + append(commonFields, zap.Error(err))..., + ) } } } @@ -2154,7 +2353,10 @@ func isAllowedToControl(session Session) bool { func (h *Hub) processControlMsg(session Session, message *ClientMessage) { msg := message.Control if !isAllowedToControl(session) { - log.Printf("Ignore control message %+v from %s", msg, session.PublicId()) + h.log.Info("Ignore control message", + zap.Any("message", msg), + zap.String("sessionid", session.PublicId()), + ) return } @@ -2220,7 +2422,10 @@ func (h *Hub) processControlMsg(session Session, message *ClientMessage) { } } if subject == "" { - log.Printf("Unknown recipient in message %+v from %s", msg, session.PublicId()) + h.log.Warn("Unknown recipient in message", + zap.Any("message", msg), + zap.String("sessionid", session.PublicId()), + ) return } @@ -2243,19 +2448,33 @@ func (h *Hub) processControlMsg(session Session, message *ClientMessage) { Type: "message", Message: response, } + commonFields := []zapcore.Field{ + zap.String("backend", session.Backend().Id()), + } var err error switch msg.Recipient.Type { case RecipientTypeSession: + commonFields = append(commonFields, + zap.String("recipientsessionid", recipientSessionId), + ) err = h.events.PublishSessionMessage(recipientSessionId, session.Backend(), async) case RecipientTypeUser: + commonFields = append(commonFields, + zap.String("recipientuserid", msg.Recipient.UserId), + ) err = h.events.PublishUserMessage(msg.Recipient.UserId, session.Backend(), async) case RecipientTypeRoom: + commonFields = append(commonFields, + zap.String("recipientroomid", room.Id()), + ) err = h.events.PublishRoomMessage(room.Id(), room.Backend(), async) default: err = fmt.Errorf("unsupported recipient type: %s", msg.Recipient.Type) } if err != nil { - log.Printf("Error publishing message to remote session: %s", err) + h.log.Error("Error publishing message to remote", + append(commonFields, zap.Error(err))..., + ) } } } @@ -2267,7 +2486,10 @@ func (h *Hub) processInternalMsg(sess Session, message *ClientMessage) { // Client is not connected yet. return } else if session.ClientType() != HelloClientTypeInternal { - log.Printf("Ignore internal message %+v from %s", msg, session.PublicId()) + h.log.Info("Ignore internal message", + zap.Any("message", msg), + zap.String("sessionid", session.PublicId()), + ) return } @@ -2280,19 +2502,27 @@ func (h *Hub) processInternalMsg(sess Session, message *ClientMessage) { msg := msg.AddSession room := h.getRoomForBackend(msg.RoomId, session.Backend()) if room == nil { - log.Printf("Ignore add session message %+v for invalid room %s from %s", *msg, msg.RoomId, session.PublicId()) + h.log.Warn("Ignore add session message for invalid room", + zap.Any("message", *msg), + zap.String("roomid", msg.RoomId), + zap.String("sessionid", session.PublicId()), + ) return } sessionIdData := h.newSessionIdData(session.Backend()) privateSessionId, err := h.encodeSessionId(sessionIdData, privateSessionName) if err != nil { - log.Printf("Could not encode private virtual session id: %s", err) + h.log.Error("Could not encode private virtual session id", + zap.Error(err), + ) return } publicSessionId, err := h.encodeSessionId(sessionIdData, publicSessionName) if err != nil { - log.Printf("Could not encode public virtual session id: %s", err) + h.log.Error("Could not encode public virtual session id", + zap.Error(err), + ) return } @@ -2301,9 +2531,12 @@ func (h *Hub) processInternalMsg(sess Session, message *ClientMessage) { virtualSessionId := GetVirtualSessionId(session, msg.SessionId) - sess, err := NewVirtualSession(session, privateSessionId, publicSessionId, sessionIdData, msg) + sess, err := NewVirtualSession(h.log, session, privateSessionId, publicSessionId, sessionIdData, msg) if err != nil { - log.Printf("Could not create virtual session %s: %s", virtualSessionId, err) + h.log.Error("Could not create virtual session", + zap.String("virtualsessionid", virtualSessionId), + zap.Error(err), + ) reply := message.NewErrorServerMessage(NewError("add_failed", "Could not create virtual session.")) session.SendMessage(reply) return @@ -2318,7 +2551,11 @@ func (h *Hub) processInternalMsg(sess Session, message *ClientMessage) { var response BackendClientResponse if err := h.backend.PerformJSONRequest(ctx, session.ParsedBackendUrl(), request, &response); err != nil { sess.Close() - log.Printf("Could not join virtual session %s at backend %s: %s", virtualSessionId, session.BackendUrl(), err) + h.log.Error("Could not join virtual session at backend", + zap.String("virtualsessionid", virtualSessionId), + zap.String("backend", session.BackendUrl()), + zap.Error(err), + ) reply := message.NewErrorServerMessage(NewError("add_failed", "Could not join virtual session.")) session.SendMessage(reply) return @@ -2326,7 +2563,11 @@ func (h *Hub) processInternalMsg(sess Session, message *ClientMessage) { if response.Type == "error" { sess.Close() - log.Printf("Could not join virtual session %s at backend %s: %+v", virtualSessionId, session.BackendUrl(), response.Error) + h.log.Error("Could not join virtual session at backend", + zap.String("virtualsessionid", virtualSessionId), + zap.String("backend", session.BackendUrl()), + zap.Any("error", response.Error), + ) reply := message.NewErrorServerMessage(NewError("add_failed", response.Error.Error())) session.SendMessage(reply) return @@ -2336,7 +2577,11 @@ func (h *Hub) processInternalMsg(sess Session, message *ClientMessage) { var response BackendClientSessionResponse if err := h.backend.PerformJSONRequest(ctx, session.ParsedBackendUrl(), request, &response); err != nil { sess.Close() - log.Printf("Could not add virtual session %s at backend %s: %s", virtualSessionId, session.BackendUrl(), err) + h.log.Error("Could not add virtual session at backend", + zap.String("virtualsessionid", virtualSessionId), + zap.String("backend", session.BackendUrl()), + zap.Error(err), + ) reply := message.NewErrorServerMessage(NewError("add_failed", "Could not add virtual session.")) session.SendMessage(reply) return @@ -2349,7 +2594,11 @@ func (h *Hub) processInternalMsg(sess Session, message *ClientMessage) { h.mu.Unlock() statsHubSessionsCurrent.WithLabelValues(session.Backend().Id(), sess.ClientType()).Inc() statsHubSessionsTotal.WithLabelValues(session.Backend().Id(), sess.ClientType()).Inc() - log.Printf("Session %s added virtual session %s with initial flags %d", session.PublicId(), sess.PublicId(), sess.Flags()) + h.log.Info("Session added virtual session", + zap.String("ownersessionid", session.PublicId()), + zap.String("sessionid", sess.PublicId()), + zap.Uint32("flags", sess.Flags()), + ) session.AddVirtualSession(sess) sess.SetRoom(room) room.AddSession(sess, nil) @@ -2357,7 +2606,11 @@ func (h *Hub) processInternalMsg(sess Session, message *ClientMessage) { msg := msg.UpdateSession room := h.getRoomForBackend(msg.RoomId, session.Backend()) if room == nil { - log.Printf("Ignore remove session message %+v for invalid room %s from %s", *msg, msg.RoomId, session.PublicId()) + h.log.Warn("Ignore update session message for invalid room", + zap.Any("message", *msg), + zap.String("roomid", msg.RoomId), + zap.String("sessionid", session.PublicId()), + ) return } @@ -2385,7 +2638,9 @@ func (h *Hub) processInternalMsg(sess Session, message *ClientMessage) { } } } else { - log.Printf("Ignore update request for non-virtual session %s", sess.PublicId()) + h.log.Warn("Ignore update request for non-virtual session", + zap.String("sessionid", sess.PublicId()), + ) } if changed != 0 { room.NotifySessionChanged(sess, changed) @@ -2395,7 +2650,11 @@ func (h *Hub) processInternalMsg(sess Session, message *ClientMessage) { msg := msg.RemoveSession room := h.getRoomForBackend(msg.RoomId, session.Backend()) if room == nil { - log.Printf("Ignore remove session message %+v for invalid room %s from %s", *msg, msg.RoomId, session.PublicId()) + h.log.Warn("Ignore remove session message for invalid room", + zap.Any("message", *msg), + zap.String("roomid", msg.RoomId), + zap.String("sessionid", session.PublicId()), + ) return } @@ -2411,7 +2670,10 @@ func (h *Hub) processInternalMsg(sess Session, message *ClientMessage) { sess := h.sessions[sid] h.mu.Unlock() if sess != nil { - log.Printf("Session %s removed virtual session %s", session.PublicId(), sess.PublicId()) + h.log.Info("Session removed virtual session", + zap.String("ownersessionid", session.PublicId()), + zap.String("sessionid", sess.PublicId()), + ) if vsess, ok := sess.(*VirtualSession); ok { // We should always have a VirtualSession here. vsess.CloseWithFeedback(session, message) @@ -2445,7 +2707,11 @@ func (h *Hub) processInternalMsg(sess Session, message *ClientMessage) { asyncMessage.Room.Transient.TTL = removeCallStatusTTL } if err := h.events.PublishBackendRoomMessage(roomId, session.Backend(), asyncMessage); err != nil { - log.Printf("Error publishing dialout message %+v to room %s", msg.Dialout, roomId) + h.log.Error("Error publishing dialout message to room", + zap.Any("message", msg.Dialout), + zap.String("roomid", roomId), + zap.Error(err), + ) } } else { if err := h.events.PublishRoomMessage(roomId, session.Backend(), &AsyncMessage{ @@ -2455,11 +2721,18 @@ func (h *Hub) processInternalMsg(sess Session, message *ClientMessage) { Dialout: msg.Dialout, }, }); err != nil { - log.Printf("Error publishing dialout message %+v to room %s", msg.Dialout, roomId) + h.log.Error("Error publishing dialout message to room", + zap.Any("message", msg.Dialout), + zap.String("roomid", roomId), + zap.Error(err), + ) } } default: - log.Printf("Ignore unsupported internal message %+v from %s", msg, session.PublicId()) + h.log.Warn("Ignore unsupported internal message", + zap.Any("message", msg), + zap.String("sessionid", session.PublicId()), + ) return } } @@ -2545,7 +2818,11 @@ func (h *Hub) isInSameCallRemote(ctx context.Context, senderSession *ClientSessi if errors.Is(err, context.Canceled) { return } else if err != nil { - log.Printf("Error checking session %s in call on %s: %s", recipientSessionId, client.Target(), err) + h.log.Error("Error checking session in call", + zap.String("sessionid", recipientSessionId), + zap.String("target", client.Target()), + zap.Error(err), + ) return } else if !inCall { return @@ -2598,14 +2875,19 @@ func (h *Hub) processMcuMessage(session *ClientSession, client_message *ClientMe switch data.Type { case "requestoffer": if session.PublicId() == message.Recipient.SessionId { - log.Printf("Not requesting offer from itself for session %s", session.PublicId()) + h.log.Debug("Not requesting offer from itself for session", + zap.String("sessionid", session.PublicId()), + ) return } // A user is only allowed to subscribe a stream if she is in the same room // as the other user and both have their "inCall" flag set. if !h.allowSubscribeAnyStream && !h.isInSameCall(ctx, session, message.Recipient.SessionId) { - log.Printf("Session %s is not in the same call as session %s, not requesting offer", session.PublicId(), message.Recipient.SessionId) + h.log.Debug("Sessions are not in the same call, not requesting offer", + zap.String("sessionid", session.PublicId()), + zap.String("recipientsessionid", message.Recipient.SessionId), + ) sendNotAllowed(session, client_message, "Not allowed to request offer.") return } @@ -2619,13 +2901,20 @@ func (h *Hub) processMcuMessage(session *ClientSession, client_message *ClientMe clientType = "publisher" mc, err = session.GetOrCreatePublisher(ctx, h.mcu, StreamType(data.RoomType), data) if err, ok := err.(*PermissionError); ok { - log.Printf("Session %s is not allowed to offer %s, ignoring (%s)", session.PublicId(), data.RoomType, err) + h.log.Info("Session is not allowed to offer, ignoring", + zap.String("sessionid", session.PublicId()), + zap.String("streamtype", data.RoomType), + zap.Error(err), + ) sendNotAllowed(session, client_message, "Not allowed to publish.") return } case "selectStream": if session.PublicId() == message.Recipient.SessionId { - log.Printf("Not selecting substream for own %s stream in session %s", data.RoomType, session.PublicId()) + h.log.Debug("Not selecting substream for own stream in session", + zap.String("streamtype", data.RoomType), + zap.String("sessionid", session.PublicId()), + ) return } @@ -2634,7 +2923,11 @@ func (h *Hub) processMcuMessage(session *ClientSession, client_message *ClientMe default: if session.PublicId() == message.Recipient.SessionId { if err := session.IsAllowedToSend(data); err != nil { - log.Printf("Session %s is not allowed to send candidate for %s, ignoring (%s)", session.PublicId(), data.RoomType, err) + h.log.Info("Session is not allowed to send candidate, ignoring", + zap.String("sessionid", session.PublicId()), + zap.String("streamtype", data.RoomType), + zap.Error(err), + ) sendNotAllowed(session, client_message, "Not allowed to send candidate.") return } @@ -2647,18 +2940,33 @@ func (h *Hub) processMcuMessage(session *ClientSession, client_message *ClientMe } } if err != nil { - log.Printf("Could not create MCU %s for session %s to send %+v to %s: %s", clientType, session.PublicId(), data, message.Recipient.SessionId, err) + h.log.Error(fmt.Sprintf("Could not create MCU %s", clientType), + zap.String("sessionid", session.PublicId()), + zap.String("recipientsessionid", message.Recipient.SessionId), + zap.Any("message", data), + zap.Error(err), + ) sendMcuClientNotFound(session, client_message) return } else if mc == nil { - log.Printf("No MCU %s found for session %s to send %+v to %s", clientType, session.PublicId(), data, message.Recipient.SessionId) + h.log.Error(fmt.Sprintf("No MCU %s found", clientType), + zap.String("sessionid", session.PublicId()), + zap.String("recipientsessionid", message.Recipient.SessionId), + zap.Any("message", data), + zap.Error(err), + ) sendMcuClientNotFound(session, client_message) return } mc.SendMessage(session.Context(), message, data, func(err error, response map[string]interface{}) { if err != nil { - log.Printf("Could not send MCU message %+v for session %s to %s: %s", data, session.PublicId(), message.Recipient.SessionId, err) + h.log.Error("Could not send MCU message", + zap.String("sessionid", session.PublicId()), + zap.String("recipientsessionid", message.Recipient.SessionId), + zap.Any("message", data), + zap.Error(err), + ) sendMcuProcessingFailed(session, client_message) return } else if response == nil { @@ -2684,7 +2992,11 @@ func (h *Hub) sendMcuMessageResponse(session *ClientSession, mcuClient McuClient } answer_data, err := json.Marshal(answer_message) if err != nil { - log.Printf("Could not serialize answer %+v to %s: %s", answer_message, session.PublicId(), err) + h.log.Error("Could not serialize answer", + zap.Any("answer", answer_message), + zap.String("sessionid", session.PublicId()), + zap.Error(err), + ) return } response_message = &ServerMessage{ @@ -2709,7 +3021,11 @@ func (h *Hub) sendMcuMessageResponse(session *ClientSession, mcuClient McuClient } offer_data, err := json.Marshal(offer_message) if err != nil { - log.Printf("Could not serialize offer %+v to %s: %s", offer_message, session.PublicId(), err) + h.log.Error("Could not serialize offer", + zap.Any("message", offer_message), + zap.String("sessionid", session.PublicId()), + zap.Error(err), + ) return } response_message = &ServerMessage{ @@ -2724,7 +3040,10 @@ func (h *Hub) sendMcuMessageResponse(session *ClientSession, mcuClient McuClient }, } default: - log.Printf("Unsupported response %+v received to send to %s", response, session.PublicId()) + h.log.Warn("Unsupported response received", + zap.Any("response", response), + zap.String("sessionid", session.PublicId()), + ) return } @@ -2765,7 +3084,9 @@ func (h *Hub) processRoomInCallChanged(message *BackendServerRoomRequest) { if err := json.Unmarshal(message.InCall.InCall, &flags); err != nil { var incall bool if err := json.Unmarshal(message.InCall.InCall, &incall); err != nil { - log.Printf("Unsupported InCall flags type: %+v, ignoring", string(message.InCall.InCall)) + h.log.Warn("Unsupported InCall flags type, ignoring", + zap.Binary("incall", message.InCall.InCall), + ) return } @@ -2873,13 +3194,19 @@ func (h *Hub) serveWs(w http.ResponseWriter, r *http.Request) { conn, err := h.upgrader.Upgrade(w, r, header) if err != nil { - log.Printf("Could not upgrade request from %s: %s", addr, err) + h.log.Error("Could not upgrade request", + zap.String("addr", addr), + zap.Error(err), + ) return } - client, err := NewClient(r.Context(), conn, addr, agent, h) + client, err := NewClient(r.Context(), h.log, conn, addr, agent, h) if err != nil { - log.Printf("Could not create client for %s: %s", addr, err) + h.log.Error("Could not create client", + zap.String("addr", addr), + zap.Error(err), + ) return } @@ -2918,7 +3245,10 @@ func (h *Hub) OnLookupCountry(client HandlerClient) string { var err error country, err = h.geoip.LookupCountry(ip) if err != nil { - log.Printf("Could not lookup country for %s: %s", ip, err) + h.log.Error("Could not lookup country", + zap.Stringer("ip", ip), + zap.Error(err), + ) return unknownCountry } diff --git a/hub_test.go b/hub_test.go index 4edf73e5..7090bf2b 100644 --- a/hub_test.go +++ b/hub_test.go @@ -132,6 +132,7 @@ func getTestConfigWithMultipleBackends(server *httptest.Server) (*goconf.ConfigF func CreateHubForTestWithConfig(t *testing.T, getConfigFunc func(*httptest.Server) (*goconf.ConfigFile, error)) (*Hub, AsyncEvents, *mux.Router, *httptest.Server) { require := require.New(t) + log := GetLoggerForTest(t) r := mux.NewRouter() registerBackendHandler(t, r) @@ -143,9 +144,9 @@ func CreateHubForTestWithConfig(t *testing.T, getConfigFunc func(*httptest.Serve events := getAsyncEventsForTest(t) config, err := getConfigFunc(server) require.NoError(err) - h, err := NewHub(config, events, nil, nil, nil, r, "no-version") + h, err := NewHub(log, config, events, nil, nil, nil, r, "no-version") require.NoError(err) - b, err := NewBackendServer(config, h, "no-version") + b, err := NewBackendServer(log, config, h, "no-version") require.NoError(err) require.NoError(b.Start(r)) @@ -174,6 +175,7 @@ func CreateHubWithMultipleBackendsForTest(t *testing.T) (*Hub, AsyncEvents, *mux func CreateClusteredHubsForTestWithConfig(t *testing.T, getConfigFunc func(*httptest.Server) (*goconf.ConfigFile, error)) (*Hub, *Hub, *mux.Router, *mux.Router, *httptest.Server, *httptest.Server) { require := require.New(t) + log := GetLoggerForTest(t) r1 := mux.NewRouter() registerBackendHandler(t, r1) @@ -205,7 +207,7 @@ func CreateClusteredHubsForTestWithConfig(t *testing.T, getConfigFunc func(*http addr1, addr2 = addr2, addr1 } - events1, err := NewAsyncEvents(nats1) + events1, err := NewAsyncEvents(log, nats1) require.NoError(err) t.Cleanup(func() { events1.Close() @@ -213,11 +215,11 @@ func CreateClusteredHubsForTestWithConfig(t *testing.T, getConfigFunc func(*http config1, err := getConfigFunc(server1) require.NoError(err) client1, _ := NewGrpcClientsForTest(t, addr2) - h1, err := NewHub(config1, events1, grpcServer1, client1, nil, r1, "no-version") + h1, err := NewHub(log, config1, events1, grpcServer1, client1, nil, r1, "no-version") require.NoError(err) - b1, err := NewBackendServer(config1, h1, "no-version") + b1, err := NewBackendServer(log, config1, h1, "no-version") require.NoError(err) - events2, err := NewAsyncEvents(nats2) + events2, err := NewAsyncEvents(log, nats2) require.NoError(err) t.Cleanup(func() { events2.Close() @@ -225,9 +227,9 @@ func CreateClusteredHubsForTestWithConfig(t *testing.T, getConfigFunc func(*http config2, err := getConfigFunc(server2) require.NoError(err) client2, _ := NewGrpcClientsForTest(t, addr1) - h2, err := NewHub(config2, events2, grpcServer2, client2, nil, r2, "no-version") + h2, err := NewHub(log, config2, events2, grpcServer2, client2, nil, r2, "no-version") require.NoError(err) - b2, err := NewBackendServer(config2, h2, "no-version") + b2, err := NewBackendServer(log, config2, h2, "no-version") require.NoError(err) require.NoError(b1.Start(r1)) require.NoError(b2.Start(r2)) @@ -751,7 +753,6 @@ func performHousekeeping(hub *Hub, now time.Time) *sync.WaitGroup { func TestWebsocketFeatures(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) _, _, _, server := CreateHubForTest(t) @@ -786,7 +787,6 @@ func TestWebsocketFeatures(t *testing.T) { func TestInitialWelcome(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -809,7 +809,6 @@ func TestInitialWelcome(t *testing.T) { func TestExpectClientHello(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -841,7 +840,6 @@ func TestExpectClientHello(t *testing.T) { func TestExpectClientHelloUnsupportedVersion(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -867,7 +865,6 @@ func TestExpectClientHelloUnsupportedVersion(t *testing.T) { func TestClientHelloV1(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -887,7 +884,6 @@ func TestClientHelloV1(t *testing.T) { } func TestClientHelloV2(t *testing.T) { - CatchLogForTest(t) for _, algo := range testHelloV2Algorithms { t.Run(algo, func(t *testing.T) { require := require.New(t) @@ -924,7 +920,6 @@ func TestClientHelloV2(t *testing.T) { } func TestClientHelloV2_IssuedInFuture(t *testing.T) { - CatchLogForTest(t) for _, algo := range testHelloV2Algorithms { t.Run(algo, func(t *testing.T) { require := require.New(t) @@ -952,7 +947,6 @@ func TestClientHelloV2_IssuedInFuture(t *testing.T) { } func TestClientHelloV2_Expired(t *testing.T) { - CatchLogForTest(t) for _, algo := range testHelloV2Algorithms { t.Run(algo, func(t *testing.T) { require := require.New(t) @@ -979,7 +973,6 @@ func TestClientHelloV2_Expired(t *testing.T) { } func TestClientHelloV2_IssuedAtMissing(t *testing.T) { - CatchLogForTest(t) for _, algo := range testHelloV2Algorithms { t.Run(algo, func(t *testing.T) { require := require.New(t) @@ -1007,7 +1000,6 @@ func TestClientHelloV2_IssuedAtMissing(t *testing.T) { } func TestClientHelloV2_ExpiresAtMissing(t *testing.T) { - CatchLogForTest(t) for _, algo := range testHelloV2Algorithms { t.Run(algo, func(t *testing.T) { require := require.New(t) @@ -1035,7 +1027,6 @@ func TestClientHelloV2_ExpiresAtMissing(t *testing.T) { } func TestClientHelloV2_CachedCapabilities(t *testing.T) { - CatchLogForTest(t) for _, algo := range testHelloV2Algorithms { t.Run(algo, func(t *testing.T) { require := require.New(t) @@ -1076,7 +1067,6 @@ func TestClientHelloV2_CachedCapabilities(t *testing.T) { func TestClientHelloWithSpaces(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -1098,7 +1088,6 @@ func TestClientHelloWithSpaces(t *testing.T) { func TestClientHelloAllowAll(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTestWithConfig(t, func(server *httptest.Server) (*goconf.ConfigFile, error) { @@ -1127,7 +1116,6 @@ func TestClientHelloAllowAll(t *testing.T) { } func TestClientHelloSessionLimit(t *testing.T) { - CatchLogForTest(t) for _, subtest := range clusteredTests { t.Run(subtest, func(t *testing.T) { t.Parallel() @@ -1262,7 +1250,6 @@ func TestClientHelloSessionLimit(t *testing.T) { func TestSessionIdsUnordered(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -1322,7 +1309,6 @@ func TestSessionIdsUnordered(t *testing.T) { func TestClientHelloResume(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -1357,7 +1343,6 @@ func TestClientHelloResume(t *testing.T) { func TestClientHelloResumeThrottle(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -1434,7 +1419,6 @@ func TestClientHelloResumeThrottle(t *testing.T) { func TestClientHelloResumeExpired(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -1474,7 +1458,6 @@ func TestClientHelloResumeExpired(t *testing.T) { func TestClientHelloResumeTakeover(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -1520,7 +1503,6 @@ func TestClientHelloResumeTakeover(t *testing.T) { func TestClientHelloResumeOtherHub(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -1587,7 +1569,6 @@ func TestClientHelloResumeOtherHub(t *testing.T) { func TestClientHelloResumePublicId(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) // Test that a client can't resume a "public" session of another user. @@ -1645,7 +1626,6 @@ func TestClientHelloResumePublicId(t *testing.T) { func TestClientHelloByeResume(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -1687,7 +1667,6 @@ func TestClientHelloByeResume(t *testing.T) { func TestClientHelloResumeAndJoin(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -1765,7 +1744,6 @@ func runGrpcProxyTest(t *testing.T, f func(hub1, hub2 *Hub, server1, server2 *ht } func TestClientHelloResumeProxy(t *testing.T) { - CatchLogForTest(t) ensureNoGoroutinesLeak(t, func(t *testing.T) { runGrpcProxyTest(t, func(hub1, hub2 *Hub, server1, server2 *httptest.Server) { require := require.New(t) @@ -1824,7 +1802,6 @@ func TestClientHelloResumeProxy(t *testing.T) { } func TestClientHelloResumeProxy_Takeover(t *testing.T) { - CatchLogForTest(t) ensureNoGoroutinesLeak(t, func(t *testing.T) { runGrpcProxyTest(t, func(hub1, hub2 *Hub, server1, server2 *httptest.Server) { require := require.New(t) @@ -1895,7 +1872,6 @@ func TestClientHelloResumeProxy_Takeover(t *testing.T) { } func TestClientHelloResumeProxy_Disconnect(t *testing.T) { - CatchLogForTest(t) ensureNoGoroutinesLeak(t, func(t *testing.T) { runGrpcProxyTest(t, func(hub1, hub2 *Hub, server1, server2 *httptest.Server) { require := require.New(t) @@ -1937,7 +1913,6 @@ func TestClientHelloResumeProxy_Disconnect(t *testing.T) { func TestClientHelloClient(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -1959,7 +1934,6 @@ func TestClientHelloClient(t *testing.T) { func TestClientHelloClient_V3Api(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -1986,7 +1960,6 @@ func TestClientHelloClient_V3Api(t *testing.T) { func TestClientHelloInternal(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -2007,7 +1980,6 @@ func TestClientHelloInternal(t *testing.T) { } func TestClientMessageToSessionId(t *testing.T) { - CatchLogForTest(t) for _, subtest := range clusteredTests { t.Run(subtest, func(t *testing.T) { t.Parallel() @@ -2027,12 +1999,12 @@ func TestClientMessageToSessionId(t *testing.T) { hub1, hub2, server1, server2 = CreateClusteredHubsForTest(t) } - mcu1, err := NewTestMCU() + mcu1, err := NewTestMCU(t) require.NoError(err) hub1.SetMcu(mcu1) if hub1 != hub2 { - mcu2, err := NewTestMCU() + mcu2, err := NewTestMCU(t) require.NoError(err) hub2.SetMcu(mcu2) } @@ -2084,7 +2056,6 @@ func TestClientMessageToSessionId(t *testing.T) { } func TestClientControlToSessionId(t *testing.T) { - CatchLogForTest(t) for _, subtest := range clusteredTests { t.Run(subtest, func(t *testing.T) { t.Parallel() @@ -2148,7 +2119,6 @@ func TestClientControlToSessionId(t *testing.T) { func TestClientControlMissingPermissions(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -2218,7 +2188,6 @@ func TestClientControlMissingPermissions(t *testing.T) { func TestClientMessageToUserId(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -2267,7 +2236,6 @@ func TestClientMessageToUserId(t *testing.T) { func TestClientControlToUserId(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -2316,7 +2284,6 @@ func TestClientControlToUserId(t *testing.T) { func TestClientMessageToUserIdMultipleSessions(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -2375,7 +2342,6 @@ func WaitForUsersJoined(ctx context.Context, t *testing.T, client1 *TestClient, } func TestClientMessageToRoom(t *testing.T) { - CatchLogForTest(t) for _, subtest := range clusteredTests { t.Run(subtest, func(t *testing.T) { t.Parallel() @@ -2450,7 +2416,6 @@ func TestClientMessageToRoom(t *testing.T) { } func TestClientControlToRoom(t *testing.T) { - CatchLogForTest(t) for _, subtest := range clusteredTests { t.Run(subtest, func(t *testing.T) { t.Parallel() @@ -2526,7 +2491,6 @@ func TestClientControlToRoom(t *testing.T) { func TestJoinRoom(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -2559,7 +2523,6 @@ func TestJoinRoom(t *testing.T) { func TestJoinRoomTwice(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -2616,7 +2579,6 @@ func TestJoinRoomTwice(t *testing.T) { func TestExpectAnonymousJoinRoom(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -2653,7 +2615,6 @@ func TestExpectAnonymousJoinRoom(t *testing.T) { func TestExpectAnonymousJoinRoomAfterLeave(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -2718,7 +2679,6 @@ func TestExpectAnonymousJoinRoomAfterLeave(t *testing.T) { func TestJoinRoomChange(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -2760,7 +2720,6 @@ func TestJoinRoomChange(t *testing.T) { func TestJoinMultiple(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -2816,7 +2775,6 @@ func TestJoinMultiple(t *testing.T) { func TestJoinDisplaynamesPermission(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -2875,7 +2833,6 @@ func TestJoinDisplaynamesPermission(t *testing.T) { func TestInitialRoomPermissions(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -2908,7 +2865,6 @@ func TestInitialRoomPermissions(t *testing.T) { func TestJoinRoomSwitchClient(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -3195,7 +3151,6 @@ func TestGetRealUserIP(t *testing.T) { func TestClientMessageToSessionIdWhileDisconnected(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -3261,7 +3216,6 @@ func TestClientMessageToSessionIdWhileDisconnected(t *testing.T) { func TestRoomParticipantsListUpdateWhileDisconnected(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -3357,7 +3311,6 @@ func TestRoomParticipantsListUpdateWhileDisconnected(t *testing.T) { } func TestClientTakeoverRoomSession(t *testing.T) { - CatchLogForTest(t) for _, subtest := range clusteredTests { t.Run(subtest, func(t *testing.T) { t.Parallel() @@ -3474,7 +3427,6 @@ func RunTestClientTakeoverRoomSession(t *testing.T) { func TestClientSendOfferPermissions(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -3482,7 +3434,7 @@ func TestClientSendOfferPermissions(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), testTimeout) defer cancel() - mcu, err := NewTestMCU() + mcu, err := NewTestMCU(t) require.NoError(err) require.NoError(mcu.Start(ctx)) defer mcu.Stop() @@ -3584,7 +3536,6 @@ func TestClientSendOfferPermissions(t *testing.T) { func TestClientSendOfferPermissionsAudioOnly(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -3592,7 +3543,7 @@ func TestClientSendOfferPermissionsAudioOnly(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), testTimeout) defer cancel() - mcu, err := NewTestMCU() + mcu, err := NewTestMCU(t) require.NoError(err) require.NoError(mcu.Start(ctx)) defer mcu.Stop() @@ -3656,7 +3607,6 @@ func TestClientSendOfferPermissionsAudioOnly(t *testing.T) { func TestClientSendOfferPermissionsAudioVideo(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -3664,7 +3614,7 @@ func TestClientSendOfferPermissionsAudioVideo(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), testTimeout) defer cancel() - mcu, err := NewTestMCU() + mcu, err := NewTestMCU(t) require.NoError(err) require.NoError(mcu.Start(ctx)) defer mcu.Stop() @@ -3761,7 +3711,6 @@ loop: func TestClientSendOfferPermissionsAudioVideoMedia(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -3769,7 +3718,7 @@ func TestClientSendOfferPermissionsAudioVideoMedia(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), testTimeout) defer cancel() - mcu, err := NewTestMCU() + mcu, err := NewTestMCU(t) require.NoError(err) require.NoError(mcu.Start(ctx)) defer mcu.Stop() @@ -3868,7 +3817,6 @@ loop: } func TestClientRequestOfferNotInRoom(t *testing.T) { - CatchLogForTest(t) for _, subtest := range clusteredTests { t.Run(subtest, func(t *testing.T) { t.Parallel() @@ -3890,7 +3838,7 @@ func TestClientRequestOfferNotInRoom(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), testTimeout) defer cancel() - mcu, err := NewTestMCU() + mcu, err := NewTestMCU(t) require.NoError(err) require.NoError(mcu.Start(ctx)) defer mcu.Stop() @@ -4052,7 +4000,6 @@ func TestClientRequestOfferNotInRoom(t *testing.T) { func TestNoSendBetweenSessionsOnDifferentBackends(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) // Clients can't send messages to sessions connected from other backends. @@ -4115,7 +4062,6 @@ func TestNoSendBetweenSessionsOnDifferentBackends(t *testing.T) { func TestNoSameRoomOnDifferentBackends(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubWithMultipleBackendsForTest(t) @@ -4201,7 +4147,6 @@ func TestNoSameRoomOnDifferentBackends(t *testing.T) { } func TestClientSendOffer(t *testing.T) { - CatchLogForTest(t) for _, subtest := range clusteredTests { t.Run(subtest, func(t *testing.T) { t.Parallel() @@ -4223,7 +4168,7 @@ func TestClientSendOffer(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), testTimeout) defer cancel() - mcu, err := NewTestMCU() + mcu, err := NewTestMCU(t) require.NoError(err) require.NoError(mcu.Start(ctx)) defer mcu.Stop() @@ -4302,7 +4247,6 @@ func TestClientSendOffer(t *testing.T) { func TestClientUnshareScreen(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -4310,7 +4254,7 @@ func TestClientUnshareScreen(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), testTimeout) defer cancel() - mcu, err := NewTestMCU() + mcu, err := NewTestMCU(t) require.NoError(err) require.NoError(mcu.Start(ctx)) defer mcu.Stop() @@ -4375,7 +4319,6 @@ func TestClientUnshareScreen(t *testing.T) { } func TestVirtualClientSessions(t *testing.T) { - CatchLogForTest(t) for _, subtest := range clusteredTests { t.Run(subtest, func(t *testing.T) { t.Parallel() @@ -4626,7 +4569,6 @@ func TestVirtualClientSessions(t *testing.T) { } func DoTestSwitchToOne(t *testing.T, details map[string]interface{}) { - CatchLogForTest(t) for _, subtest := range clusteredTests { t.Run(subtest, func(t *testing.T) { t.Parallel() @@ -4739,7 +4681,6 @@ func TestSwitchToOneList(t *testing.T) { } func DoTestSwitchToMultiple(t *testing.T, details1 map[string]interface{}, details2 map[string]interface{}) { - CatchLogForTest(t) for _, subtest := range clusteredTests { t.Run(subtest, func(t *testing.T) { t.Parallel() @@ -4860,7 +4801,6 @@ func TestSwitchToMultipleMixed(t *testing.T) { func TestGeoipOverrides(t *testing.T) { t.Parallel() - CatchLogForTest(t) assert := assert.New(t) country1 := "DE" country2 := "IT" @@ -4886,7 +4826,6 @@ func TestGeoipOverrides(t *testing.T) { func TestDialoutStatus(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) _, _, _, hub, _, server := CreateBackendServerForTest(t) @@ -5051,7 +4990,6 @@ func TestDialoutStatus(t *testing.T) { func TestGracefulShutdownInitial(t *testing.T) { t.Parallel() - CatchLogForTest(t) hub, _, _, _ := CreateHubForTest(t) hub.ScheduleShutdown() @@ -5060,7 +4998,6 @@ func TestGracefulShutdownInitial(t *testing.T) { func TestGracefulShutdownOnBye(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -5094,7 +5031,6 @@ func TestGracefulShutdownOnBye(t *testing.T) { func TestGracefulShutdownOnExpiration(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) diff --git a/janus_client.go b/janus_client.go index b7b33a5f..d7760ee0 100644 --- a/janus_client.go +++ b/janus_client.go @@ -33,7 +33,6 @@ import ( "context" "encoding/json" "fmt" - "log" "net/http" "strconv" "sync" @@ -42,6 +41,7 @@ import ( "github.com/gorilla/websocket" "github.com/notedit/janus-go" + "go.uber.org/zap" ) const ( @@ -221,6 +221,7 @@ func (l *dummyGatewayListener) ConnectionInterrupted() { // Gateway represents a connection to an instance of the Janus Gateway. type JanusGateway struct { + log *zap.Logger listener GatewayListener // Sessions is a map of the currently active sessions to the gateway. @@ -258,7 +259,7 @@ type JanusGateway struct { // return gateway, nil // } -func NewJanusGateway(ctx context.Context, wsURL string, listener GatewayListener) (*JanusGateway, error) { +func NewJanusGateway(ctx context.Context, log *zap.Logger, wsURL string, listener GatewayListener) (*JanusGateway, error) { conn, _, err := janusDialer.DialContext(ctx, wsURL, nil) if err != nil { return nil, err @@ -268,6 +269,7 @@ func NewJanusGateway(ctx context.Context, wsURL string, listener GatewayListener listener = new(dummyGatewayListener) } gateway := &JanusGateway{ + log: log, conn: conn, listener: listener, transactions: make(map[uint64]*transaction), @@ -376,7 +378,9 @@ loop: err := gateway.conn.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(20*time.Second)) gateway.writeMu.Unlock() if err != nil { - log.Println("Error sending ping to MCU:", err) + gateway.log.Error("Error sending ping to MCU", + zap.Error(err), + ) } case <-gateway.closer.C: break loop @@ -401,7 +405,9 @@ func (gateway *JanusGateway) recv() { _, reader, err := conn.NextReader() if err != nil { - log.Printf("conn.NextReader: %s", err) + gateway.log.Error("conn.NextReader", + zap.Error(err), + ) gateway.writeMu.Lock() gateway.conn = nil gateway.writeMu.Unlock() @@ -412,7 +418,9 @@ func (gateway *JanusGateway) recv() { decodeBuffer.Reset() if _, err := decodeBuffer.ReadFrom(reader); err != nil { - log.Printf("decodeBuffer.ReadFrom: %s", err) + gateway.log.Error("decodeBuffer.ReadFrom", + zap.Error(err), + ) gateway.writeMu.Lock() gateway.conn = nil gateway.writeMu.Unlock() @@ -425,13 +433,18 @@ func (gateway *JanusGateway) recv() { decoder := json.NewDecoder(data) decoder.UseNumber() if err := decoder.Decode(&base); err != nil { - log.Printf("json.Unmarshal of %s: %s", decodeBuffer.String(), err) + gateway.log.Error("json.Unmarshal", + zap.ByteString("data", decodeBuffer.Bytes()), + zap.Error(err), + ) continue } typeFunc, ok := msgtypes[base.Type] if !ok { - log.Printf("Unknown message type received: %s", decodeBuffer.String()) + gateway.log.Error("Unknown message type received", + zap.ByteString("data", decodeBuffer.Bytes()), + ) continue } @@ -440,7 +453,10 @@ func (gateway *JanusGateway) recv() { decoder = json.NewDecoder(data) decoder.UseNumber() if err := decoder.Decode(&msg); err != nil { - log.Printf("json.Unmarshal of %s: %s", decodeBuffer.String(), err) + gateway.log.Error("json.Unmarshal", + zap.ByteString("data", decodeBuffer.Bytes()), + zap.Error(err), + ) continue // Decode error } @@ -450,14 +466,19 @@ func (gateway *JanusGateway) recv() { if base.Handle == 0 { // Nope. No idea what's going on... // Error() - log.Printf("Received event without handle, ignoring: %s", decodeBuffer.String()) + gateway.log.Warn("Received event without handle, ignoring", + zap.ByteString("data", decodeBuffer.Bytes()), + ) } else { // Lookup Session gateway.Lock() session := gateway.Sessions[base.Session] gateway.Unlock() if session == nil { - log.Printf("Unable to deliver message %s. Session %d gone?", decodeBuffer.String(), base.Session) + gateway.log.Warn("Unable to deliver message. Session gone?", + zap.ByteString("data", decodeBuffer.Bytes()), + zap.Uint64("sessionid", base.Session), + ) continue } @@ -466,7 +487,10 @@ func (gateway *JanusGateway) recv() { handle := session.Handles[base.Handle] session.Unlock() if handle == nil { - log.Printf("Unable to deliver message %s. Handle %d gone?", decodeBuffer.String(), base.Handle) + gateway.log.Warn("Unable to deliver message. Handle gone?", + zap.ByteString("data", decodeBuffer.Bytes()), + zap.Uint64("handle", base.Handle), + ) continue } @@ -476,7 +500,10 @@ func (gateway *JanusGateway) recv() { } else { id, err := strconv.ParseUint(base.ID, 10, 64) if err != nil { - log.Printf("Could not decode transaction id %s: %s", base.ID, err) + gateway.log.Warn("Could not decode transaction id", + zap.String("id", base.ID), + zap.Error(err), + ) continue } @@ -486,7 +513,9 @@ func (gateway *JanusGateway) recv() { gateway.Unlock() if transaction == nil { // Error() - log.Printf("Received event for unknown transaction, ignoring: %s", decodeBuffer.String()) + gateway.log.Warn("Received event for unknown transaction, ignoring", + zap.ByteString("data", decodeBuffer.Bytes()), + ) continue } diff --git a/mcu_janus.go b/mcu_janus.go index 5807daf9..99ea21e6 100644 --- a/mcu_janus.go +++ b/mcu_janus.go @@ -26,7 +26,6 @@ import ( "encoding/json" "errors" "fmt" - "log" "strconv" "sync" "sync/atomic" @@ -34,6 +33,7 @@ import ( "github.com/dlintw/goconf" "github.com/notedit/janus-go" + "go.uber.org/zap" ) const ( @@ -107,7 +107,11 @@ func getPluginIntValue(data janus.PluginData, pluginName string, key string) uin result, err := convertIntValue(val) if err != nil { - log.Printf("Invalid value %+v for %s: %s", val, key, err) + zap.L().Warn("Invalid integer value", + zap.String("key", key), + zap.Any("value", val), + zap.Error(err), + ) result = 0 } return result @@ -134,6 +138,7 @@ type clientInterface interface { } type mcuJanus struct { + log *zap.Logger url string mu sync.Mutex @@ -169,7 +174,7 @@ type mcuJanus struct { func emptyOnConnected() {} func emptyOnDisconnected() {} -func NewMcuJanus(ctx context.Context, url string, config *goconf.ConfigFile) (Mcu, error) { +func NewMcuJanus(ctx context.Context, log *zap.Logger, url string, config *goconf.ConfigFile) (Mcu, error) { maxStreamBitrate, _ := config.GetInt("mcu", "maxstreambitrate") if maxStreamBitrate <= 0 { maxStreamBitrate = defaultMaxStreamBitrate @@ -185,6 +190,7 @@ func NewMcuJanus(ctx context.Context, url string, config *goconf.ConfigFile) (Mc mcuTimeout := time.Duration(mcuTimeoutSeconds) * time.Second mcu := &mcuJanus{ + log: log, url: url, mcuTimeout: mcuTimeout, closeChan: make(chan struct{}, 1), @@ -215,18 +221,26 @@ func (m *mcuJanus) disconnect() { m.handle = nil m.closeChan <- struct{}{} if _, err := handle.Detach(context.TODO()); err != nil { - log.Printf("Error detaching handle %d: %s", handle.Id, err) + m.log.Error("Error detaching Janus handle", + zap.Uint64("handle", handle.Id), + zap.Error(err), + ) } } if m.session != nil { if _, err := m.session.Destroy(context.TODO()); err != nil { - log.Printf("Error destroying session %d: %s", m.session.Id, err) + m.log.Error("Error destroying Janus session", + zap.Uint64("sessionid", m.session.Id), + zap.Error(err), + ) } m.session = nil } if m.gw != nil { if err := m.gw.Close(); err != nil { - log.Println("Error while closing connection to MCU", err) + m.log.Error("Error while closing connection to Janus", + zap.Error(err), + ) } m.gw = nil } @@ -234,7 +248,7 @@ func (m *mcuJanus) disconnect() { func (m *mcuJanus) reconnect(ctx context.Context) error { m.disconnect() - gw, err := NewJanusGateway(ctx, m.url, m) + gw, err := NewJanusGateway(ctx, m.log, m.url, m) if err != nil { return err } @@ -254,7 +268,7 @@ func (m *mcuJanus) doReconnect(ctx context.Context) { return } - log.Println("Reconnection to Janus gateway successful") + m.log.Info("Reconnection to Janus gateway successful") m.mu.Lock() clear(m.publishers) m.publisherCreated.Reset() @@ -274,9 +288,14 @@ func (m *mcuJanus) scheduleReconnect(err error) { defer m.mu.Unlock() m.reconnectTimer.Reset(m.reconnectInterval) if err == nil { - log.Printf("Connection to Janus gateway was interrupted, reconnecting in %s", m.reconnectInterval) + m.log.Warn("Connection to Janus gateway was interrupted, reconnecting", + zap.Duration("wait", m.reconnectInterval), + ) } else { - log.Printf("Reconnect to Janus gateway failed (%s), reconnecting in %s", err, m.reconnectInterval) + m.log.Error("Reconnect to Janus gateway failed, reconnecting", + zap.Duration("wait", m.reconnectInterval), + zap.Error(err), + ) } m.reconnectInterval = m.reconnectInterval * 2 @@ -304,7 +323,11 @@ func (m *mcuJanus) Start(ctx context.Context) error { return err } - log.Printf("Connected to %s %s by %s", info.Name, info.VersionString, info.Author) + m.log.Info("Connected to Janus", + zap.String("name", info.Name), + zap.String("version", info.VersionString), + zap.String("author", info.Author), + ) plugin, found := info.Plugins[pluginVideoRoom] if !found { return fmt.Errorf("Plugin %s is not supported", pluginVideoRoom) @@ -312,32 +335,44 @@ func (m *mcuJanus) Start(ctx context.Context) error { m.version = info.Version - log.Printf("Found %s %s by %s", plugin.Name, plugin.VersionString, plugin.Author) + m.log.Info("Found Janus plugin", + zap.String("name", plugin.Name), + zap.String("version", plugin.VersionString), + zap.String("author", plugin.Author), + ) if !info.DataChannels { return fmt.Errorf("Data channels are not supported") } - log.Println("Data channels are supported") + m.log.Info("Data channels are supported") if !info.FullTrickle { - log.Println("WARNING: Full-Trickle is NOT enabled in Janus!") + m.log.Warn("Full-Trickle is NOT enabled in Janus!") } else { - log.Println("Full-Trickle is enabled") + m.log.Info("Full-Trickle is enabled") } - log.Printf("Maximum bandwidth %d bits/sec per publishing stream", m.maxStreamBitrate.Load()) - log.Printf("Maximum bandwidth %d bits/sec per screensharing stream", m.maxScreenBitrate.Load()) + m.log.Info("Maximum bandwidth per publishing stream", + zap.Int32("bitrate", m.maxStreamBitrate.Load()), + ) + m.log.Info("Maximum bandwidth per screensharing stream", + zap.Int32("bitrate", m.maxScreenBitrate.Load()), + ) if m.session, err = m.gw.Create(ctx); err != nil { m.disconnect() return err } - log.Println("Created Janus session", m.session.Id) + m.log.Info("Created Janus session", + zap.Uint64("sessionid", m.session.Id), + ) m.connectedSince = time.Now() if m.handle, err = m.session.Attach(ctx, pluginVideoRoom); err != nil { m.disconnect() return err } - log.Println("Created Janus handle", m.handle.Id) + m.log.Info("Created Janus handle", + zap.Uint64("handle", m.handle.Id), + ) go m.run() @@ -382,14 +417,18 @@ func (m *mcuJanus) Reload(config *goconf.ConfigFile) { if maxStreamBitrate <= 0 { maxStreamBitrate = defaultMaxStreamBitrate } - log.Printf("Maximum bandwidth %d bits/sec per publishing stream", m.maxStreamBitrate.Load()) + m.log.Info("Maximum bandwidth per publishing stream", + zap.Int32("bitrate", m.maxStreamBitrate.Load()), + ) m.maxStreamBitrate.Store(int32(maxStreamBitrate)) maxScreenBitrate, _ := config.GetInt("mcu", "maxscreenbitrate") if maxScreenBitrate <= 0 { maxScreenBitrate = defaultMaxScreenBitrate } - log.Printf("Maximum bandwidth %d bits/sec per screensharing stream", m.maxScreenBitrate.Load()) + m.log.Info("Maximum bandwidth per screensharing stream", + zap.Int32("bitrate", m.maxScreenBitrate.Load()), + ) m.maxScreenBitrate.Store(int32(maxScreenBitrate)) } @@ -446,7 +485,9 @@ func (m *mcuJanus) GetStats() interface{} { func (m *mcuJanus) sendKeepalive(ctx context.Context) { if _, err := m.session.KeepAlive(ctx); err != nil { - log.Println("Could not send keepalive request", err) + m.log.Error("Could not send Janus keepalive request", + zap.Error(err), + ) if e, ok := err.(*janus.ErrorMsg); ok { switch e.Err.Code { case JANUS_ERROR_SESSION_NOT_FOUND: @@ -499,7 +540,10 @@ func (m *mcuJanus) createPublisherRoom(ctx context.Context, handle *JanusHandle, create_response, err := handle.Request(ctx, create_msg) if err != nil { if _, err2 := handle.Detach(ctx); err2 != nil { - log.Printf("Error detaching handle %d: %s", handle.Id, err2) + m.log.Error("Error detaching Janus handle", + zap.Uint64("handle", handle.Id), + zap.Error(err2), + ) } return 0, 0, err } @@ -507,12 +551,18 @@ func (m *mcuJanus) createPublisherRoom(ctx context.Context, handle *JanusHandle, roomId := getPluginIntValue(create_response.PluginData, pluginVideoRoom, "room") if roomId == 0 { if _, err := handle.Detach(ctx); err != nil { - log.Printf("Error detaching handle %d: %s", handle.Id, err) + m.log.Error("Error detaching Janus handle", + zap.Uint64("handle", handle.Id), + zap.Error(err), + ) } return 0, 0, fmt.Errorf("No room id received: %+v", create_response) } - log.Println("Created room", roomId, create_response.PluginData) + m.log.Info("Created Janus room", + zap.Uint64("roomid", roomId), + zap.Any("data", create_response.PluginData), + ) return roomId, bitrate, nil } @@ -526,12 +576,20 @@ func (m *mcuJanus) getOrCreatePublisherHandle(ctx context.Context, id string, st return nil, 0, 0, 0, err } - log.Printf("Attached %s as publisher %d to plugin %s in session %d", streamType, handle.Id, pluginVideoRoom, session.Id) + m.log.Info("Attached Janus publisher", + zap.Uint64("sessionid", session.Id), + zap.Any("streamtype", streamType), + zap.Uint64("handle", handle.Id), + zap.String("plugin", pluginVideoRoom), + ) roomId, bitrate, err := m.createPublisherRoom(ctx, handle, id, streamType, bitrate) if err != nil { if _, err2 := handle.Detach(ctx); err2 != nil { - log.Printf("Error detaching handle %d: %s", handle.Id, err2) + m.log.Error("Error detaching Janus handle", + zap.Uint64("handle", handle.Id), + zap.Error(err2), + ) } return nil, 0, 0, 0, err } @@ -546,7 +604,10 @@ func (m *mcuJanus) getOrCreatePublisherHandle(ctx context.Context, id string, st response, err := handle.Message(ctx, msg, nil) if err != nil { if _, err2 := handle.Detach(ctx); err2 != nil { - log.Printf("Error detaching handle %d: %s", handle.Id, err2) + m.log.Error("Error detaching Janus handle", + zap.Uint64("handle", handle.Id), + zap.Error(err2), + ) } return nil, 0, 0, 0, err } @@ -566,6 +627,7 @@ func (m *mcuJanus) NewPublisher(ctx context.Context, listener McuListener, id st client := &mcuJanusPublisher{ mcuJanusClient: mcuJanusClient{ + baseLog: m.log, mcu: m, listener: listener, @@ -592,9 +654,13 @@ func (m *mcuJanus) NewPublisher(ctx context.Context, listener McuListener, id st client.mcuJanusClient.handleConnected = client.handleConnected client.mcuJanusClient.handleSlowLink = client.handleSlowLink client.mcuJanusClient.handleMedia = client.handleMedia + client.updateLogger() m.registerClient(client) - log.Printf("Publisher %s is using handle %d", client.id, client.handleId) + m.log.Info("Publisher is using handle", + zap.String("publisherid", client.id), + zap.Uint64("handle", client.handleId), + ) go client.run(handle, client.closeChan) m.mu.Lock() m.publishers[getStreamId(id, streamType)] = client @@ -649,7 +715,13 @@ func (m *mcuJanus) getOrCreateSubscriberHandle(ctx context.Context, publisher st return nil, nil, err } - log.Printf("Attached subscriber to room %d of publisher %s in plugin %s in session %d as %d", pub.roomId, publisher, pluginVideoRoom, session.Id, handle.Id) + m.log.Info("Attached Janus subscriber", + zap.Uint64("sessionid", session.Id), + zap.Uint64("roomid", pub.roomId), + zap.String("publisher", publisher), + zap.String("plugin", pluginVideoRoom), + zap.Uint64("handle", handle.Id), + ) return handle, pub, nil } @@ -665,6 +737,7 @@ func (m *mcuJanus) NewSubscriber(ctx context.Context, listener McuListener, publ client := &mcuJanusSubscriber{ mcuJanusClient: mcuJanusClient{ + baseLog: m.log, mcu: m, listener: listener, @@ -687,6 +760,7 @@ func (m *mcuJanus) NewSubscriber(ctx context.Context, listener McuListener, publ client.mcuJanusClient.handleConnected = client.handleConnected client.mcuJanusClient.handleSlowLink = client.handleSlowLink client.mcuJanusClient.handleMedia = client.handleMedia + client.updateLogger() m.registerClient(client) go client.run(handle, client.closeChan) statsSubscribersCurrent.WithLabelValues(string(streamType)).Inc() @@ -724,7 +798,10 @@ func (m *mcuJanus) getOrCreateRemotePublisher(ctx context.Context, controller Re roomId, bitrate, err := m.createPublisherRoom(ctx, handle, controller.PublisherId(), streamType, bitrate) if err != nil { if _, err2 := handle.Detach(ctx); err2 != nil { - log.Printf("Error detaching handle %d: %s", handle.Id, err2) + m.log.Error("Error detaching Janus handle", + zap.Uint64("handle", handle.Id), + zap.Error(err2), + ) } return nil, err } @@ -737,7 +814,10 @@ func (m *mcuJanus) getOrCreateRemotePublisher(ctx context.Context, controller Re }) if err != nil { if _, err2 := handle.Detach(ctx); err2 != nil { - log.Printf("Error detaching handle %d: %s", handle.Id, err2) + m.log.Error("Error detaching Janus handle", + zap.Uint64("handle", handle.Id), + zap.Error(err2), + ) } return nil, err } @@ -749,7 +829,8 @@ func (m *mcuJanus) getOrCreateRemotePublisher(ctx context.Context, controller Re pub = &mcuJanusRemotePublisher{ mcuJanusPublisher: mcuJanusPublisher{ mcuJanusClient: mcuJanusClient{ - mcu: m, + baseLog: m.log, + mcu: m, id: id, session: response.Session, @@ -777,6 +858,7 @@ func (m *mcuJanus) getOrCreateRemotePublisher(ctx context.Context, controller Re pub.mcuJanusClient.handleConnected = pub.handleConnected pub.mcuJanusClient.handleSlowLink = pub.handleSlowLink pub.mcuJanusClient.handleMedia = pub.handleMedia + pub.updateLogger() if err := controller.StartPublishing(ctx, pub); err != nil { go pub.Close(context.Background()) @@ -822,11 +904,18 @@ func (m *mcuJanus) NewRemoteSubscriber(ctx context.Context, listener McuListener return nil, err } - log.Printf("Attached subscriber to room %d of publisher %s in plugin %s in session %d as %d", pub.roomId, pub.id, pluginVideoRoom, session.Id, handle.Id) + m.log.Info("Attached remote Janus subscriber", + zap.Uint64("sessionid", session.Id), + zap.Uint64("roomid", pub.roomId), + zap.String("publisher", pub.id), + zap.String("plugin", pluginVideoRoom), + zap.Uint64("handle", handle.Id), + ) client := &mcuJanusRemoteSubscriber{ mcuJanusSubscriber: mcuJanusSubscriber{ mcuJanusClient: mcuJanusClient{ + baseLog: m.log, mcu: m, listener: listener, @@ -852,6 +941,7 @@ func (m *mcuJanus) NewRemoteSubscriber(ctx context.Context, listener McuListener client.mcuJanusClient.handleConnected = client.handleConnected client.mcuJanusClient.handleSlowLink = client.handleSlowLink client.mcuJanusClient.handleMedia = client.handleMedia + client.updateLogger() m.registerClient(client) go client.run(handle, client.closeChan) statsSubscribersCurrent.WithLabelValues(string(publisher.StreamType())).Inc() diff --git a/mcu_janus_client.go b/mcu_janus_client.go index f1d254bb..dca042cf 100644 --- a/mcu_janus_client.go +++ b/mcu_janus_client.go @@ -23,15 +23,17 @@ package signaling import ( "context" - "log" "reflect" "strconv" "sync" "github.com/notedit/janus-go" + "go.uber.org/zap" ) type mcuJanusClient struct { + baseLog *zap.Logger + log *zap.Logger mcu *mcuJanus listener McuListener mu sync.Mutex // nolint @@ -56,6 +58,15 @@ type mcuJanusClient struct { handleMedia func(event *janus.MediaMsg) } +func (c *mcuJanusClient) updateLogger() { + c.log = c.baseLog.With( + zap.Uint64("id", c.id), + zap.Uint64("handle", c.handleId), + zap.Uint64("roomid", c.roomId), + zap.Any("streamtype", c.streamType), + ) +} + func (c *mcuJanusClient) Id() string { return strconv.FormatUint(c.id, 10) } @@ -84,7 +95,10 @@ func (c *mcuJanusClient) closeClient(ctx context.Context) bool { close(c.closeChan) if _, err := handle.Detach(ctx); err != nil { if e, ok := err.(*janus.ErrorMsg); !ok || e.Err.Code != JANUS_ERROR_HANDLE_NOT_FOUND { - log.Println("Could not detach client", handle.Id, err) + c.log.Error("Could not detach client", + zap.Uint64("handle", handle.Id), + zap.Error(err), + ) } } return true @@ -114,7 +128,10 @@ loop: case *TrickleMsg: c.handleTrickle(t) default: - log.Println("Received unsupported event type", msg, reflect.TypeOf(msg)) + c.log.Error("Received unsupported event type", + zap.Any("message", msg), + zap.Any("type", reflect.TypeOf(msg)), + ) } case f := <-c.deferred: f() @@ -162,7 +179,9 @@ func (c *mcuJanusClient) sendAnswer(ctx context.Context, answer map[string]inter callback(err, nil) return } - log.Println("Started listener", start_response) + c.log.Info("Started listener", + zap.Any("response", start_response), + ) callback(nil, nil) } diff --git a/mcu_janus_publisher.go b/mcu_janus_publisher.go index b0037271..75079395 100644 --- a/mcu_janus_publisher.go +++ b/mcu_janus_publisher.go @@ -25,13 +25,13 @@ import ( "context" "errors" "fmt" - "log" "strconv" "strings" "sync/atomic" "github.com/notedit/janus-go" "github.com/pion/sdp/v3" + "go.uber.org/zap" ) const ( @@ -57,43 +57,63 @@ type mcuJanusPublisher struct { answerSdp atomic.Pointer[sdp.SessionDescription] } +func (p *mcuJanusPublisher) updateLogger() { + p.mcuJanusClient.updateLogger() + p.log = p.log.With( + zap.String("publisherid", p.id), + ) +} + func (p *mcuJanusPublisher) handleEvent(event *janus.EventMsg) { if videoroom := getPluginStringValue(event.Plugindata, pluginVideoRoom, "videoroom"); videoroom != "" { ctx := context.TODO() switch videoroom { case "destroyed": - log.Printf("Publisher %d: associated room has been destroyed, closing", p.handleId) + p.log.Info("Publisher: associated room has been destroyed, closing") go p.Close(ctx) case "slow_link": // Ignore, processed through "handleSlowLink" in the general events. default: - log.Printf("Unsupported videoroom publisher event in %d: %+v", p.handleId, event) + p.log.Warn("Unsupported videoroom event for publisher", + zap.String("videoroom", videoroom), + zap.Any("event", event), + ) } } else { - log.Printf("Unsupported publisher event in %d: %+v", p.handleId, event) + p.log.Warn("Unsupported event for publisher", + zap.Any("event", event), + ) } } func (p *mcuJanusPublisher) handleHangup(event *janus.HangupMsg) { - log.Printf("Publisher %d received hangup (%s), closing", p.handleId, event.Reason) + p.log.Info("Publisher received hangup, closing", + zap.String("reason", event.Reason), + ) go p.Close(context.Background()) } func (p *mcuJanusPublisher) handleDetached(event *janus.DetachedMsg) { - log.Printf("Publisher %d received detached, closing", p.handleId) + p.log.Info("Publisher received detached, closing") go p.Close(context.Background()) } func (p *mcuJanusPublisher) handleConnected(event *janus.WebRTCUpMsg) { - log.Printf("Publisher %d received connected", p.handleId) + p.log.Info("Publisher received connected") p.mcu.publisherConnected.Notify(getStreamId(p.id, p.streamType)) } func (p *mcuJanusPublisher) handleSlowLink(event *janus.SlowLinkMsg) { if event.Uplink { - log.Printf("Publisher %s (%d) is reporting %d lost packets on the uplink (Janus -> client)", p.listener.PublicId(), p.handleId, event.Lost) + p.log.Info("Publisher is reporting lost packets on the uplink (Janus -> client)", + zap.String("sessionid", p.listener.PublicId()), + zap.Int64("lost", event.Lost), + ) } else { - log.Printf("Publisher %s (%d) is reporting %d lost packets on the downlink (client -> Janus)", p.listener.PublicId(), p.handleId, event.Lost) + p.log.Info("Publisher is reporting lost packets on the downlink (client -> Janus)", + zap.String("sessionid", p.listener.PublicId()), + zap.Int64("lost", event.Lost), + ) } } @@ -119,7 +139,9 @@ func (p *mcuJanusPublisher) NotifyReconnected() { ctx := context.TODO() handle, session, roomId, _, err := p.mcu.getOrCreatePublisherHandle(ctx, p.id, p.streamType, p.bitrate) if err != nil { - log.Printf("Could not reconnect publisher %s: %s", p.id, err) + p.log.Error("Could not reconnect publisher", + zap.Error(err), + ) // TODO(jojo): Retry return } @@ -128,8 +150,9 @@ func (p *mcuJanusPublisher) NotifyReconnected() { p.handleId = handle.Id p.session = session p.roomId = roomId + p.updateLogger() - log.Printf("Publisher %s reconnected on handle %d", p.id, p.handleId) + p.log.Info("Publisher reconnected") } func (p *mcuJanusPublisher) Close(ctx context.Context) { @@ -141,9 +164,11 @@ func (p *mcuJanusPublisher) Close(ctx context.Context) { "room": p.roomId, } if _, err := handle.Request(ctx, destroy_msg); err != nil { - log.Printf("Error destroying room %d: %s", p.roomId, err) + p.log.Error("Error destroying room", + zap.Error(err), + ) } else { - log.Printf("Room %d destroyed", p.roomId) + p.log.Info("Room destroyed") } p.mcu.mu.Lock() delete(p.mcu.publishers, getStreamId(p.id, p.streamType)) @@ -195,15 +220,22 @@ func (p *mcuJanusPublisher) SendMessage(ctx context.Context, message *MessageCli sdpData, found := jsep["sdp"] if !found { - log.Printf("No sdp found in answer %+v", jsep) + p.log.Warn("No sdp found in answer", + zap.Any("jsep", jsep), + ) } else { sdpString, ok := sdpData.(string) if !ok { - log.Printf("Invalid sdp found in answer %+v", jsep) + p.log.Warn("Invalid sdp found in answer", + zap.Any("jsep", jsep), + ) } else { var answerSdp sdp.SessionDescription if err := answerSdp.UnmarshalString(sdpString); err != nil { - log.Printf("Error parsing answer sdp %+v: %s", sdpString, err) + p.log.Error("Error parsing answer", + zap.String("sdp", sdpString), + zap.Error(err), + ) p.answerSdp.Store(nil) p.sdpFlags.Remove(sdpHasAnswer) } else { @@ -338,7 +370,10 @@ func (p *mcuJanusPublisher) GetStreams(ctx context.Context) ([]PublisherStream, switch a.Key { case sdp.AttrKeyExtMap: if err := extmap.Unmarshal(extmap.Name() + ":" + a.Value); err != nil { - log.Printf("Error parsing extmap %s: %s", a.Value, err) + p.log.Error("Error parsing extmap", + zap.String("value", a.Value), + zap.Error(err), + ) continue } @@ -371,7 +406,9 @@ func (p *mcuJanusPublisher) GetStreams(ctx context.Context) ([]PublisherStream, } else if strings.EqualFold(s.Type, "data") { // nolint // Already handled above. } else { - log.Printf("Skip type %s", s.Type) + p.log.Debug("Skip stream type", + zap.String("type", s.Type), + ) continue } @@ -418,7 +455,12 @@ func (p *mcuJanusPublisher) PublishRemote(ctx context.Context, remoteId string, } } - log.Printf("Publishing %s to %s (port=%d, rtcpPort=%d) for %s", p.id, hostname, port, rtcpPort, remoteId) + p.log.Info("Publishing remote stream", + zap.String("hostname", hostname), + zap.Int("port", port), + zap.Int("rtcpport", rtcpPort), + zap.String("remoteid", remoteId), + ) return nil } @@ -452,6 +494,8 @@ func (p *mcuJanusPublisher) UnpublishRemote(ctx context.Context, remoteId string } } - log.Printf("Unpublished remote %s for %s", p.id, remoteId) + p.log.Info("Unpublished remote stream", + zap.String("remoteid", remoteId), + ) return nil } diff --git a/mcu_janus_remote_publisher.go b/mcu_janus_remote_publisher.go index 47593b01..3f5ec08f 100644 --- a/mcu_janus_remote_publisher.go +++ b/mcu_janus_remote_publisher.go @@ -23,10 +23,10 @@ package signaling import ( "context" - "log" "sync/atomic" "github.com/notedit/janus-go" + "go.uber.org/zap" ) type mcuJanusRemotePublisher struct { @@ -38,6 +38,10 @@ type mcuJanusRemotePublisher struct { rtcpPort int } +func (p *mcuJanusRemotePublisher) updateLogger() { + p.mcuJanusPublisher.updateLogger() +} + func (p *mcuJanusRemotePublisher) addRef() int64 { return p.ref.Add(1) } @@ -59,38 +63,51 @@ func (p *mcuJanusRemotePublisher) handleEvent(event *janus.EventMsg) { ctx := context.TODO() switch videoroom { case "destroyed": - log.Printf("Remote publisher %d: associated room has been destroyed, closing", p.handleId) + p.log.Info("Remote publisher: associated room has been destroyed, closing") go p.Close(ctx) case "slow_link": // Ignore, processed through "handleSlowLink" in the general events. default: - log.Printf("Unsupported videoroom remote publisher event in %d: %+v", p.handleId, event) + p.log.Warn("Unsupported videoroom event for remote publisher", + zap.String("videoroom", videoroom), + zap.Any("event", event), + ) } } else { - log.Printf("Unsupported remote publisher event in %d: %+v", p.handleId, event) + p.log.Warn("Unsupported remote publisher event", + zap.Any("event", event), + ) } } func (p *mcuJanusRemotePublisher) handleHangup(event *janus.HangupMsg) { - log.Printf("Remote publisher %d received hangup (%s), closing", p.handleId, event.Reason) + p.log.Info("Remote publisher received hangup, closing", + zap.String("reason", event.Reason), + ) go p.Close(context.Background()) } func (p *mcuJanusRemotePublisher) handleDetached(event *janus.DetachedMsg) { - log.Printf("Remote publisher %d received detached, closing", p.handleId) + p.log.Info("Remote publisher received detached, closing") go p.Close(context.Background()) } func (p *mcuJanusRemotePublisher) handleConnected(event *janus.WebRTCUpMsg) { - log.Printf("Remote publisher %d received connected", p.handleId) + p.log.Info("Remote publisher received connected") p.mcu.publisherConnected.Notify(getStreamId(p.id, p.streamType)) } func (p *mcuJanusRemotePublisher) handleSlowLink(event *janus.SlowLinkMsg) { if event.Uplink { - log.Printf("Remote publisher %s (%d) is reporting %d lost packets on the uplink (Janus -> client)", p.listener.PublicId(), p.handleId, event.Lost) + p.log.Info("Remote publisher is reporting lost packets on the uplink (Janus -> client)", + zap.String("sessionid", p.listener.PublicId()), + zap.Int64("lost", event.Lost), + ) } else { - log.Printf("Remote publisher %s (%d) is reporting %d lost packets on the downlink (client -> Janus)", p.listener.PublicId(), p.handleId, event.Lost) + p.log.Info("Remote publisher is reporting lost packets on the downlink (client -> Janus)", + zap.String("sessionid", p.listener.PublicId()), + zap.Int64("lost", event.Lost), + ) } } @@ -98,7 +115,9 @@ func (p *mcuJanusRemotePublisher) NotifyReconnected() { ctx := context.TODO() handle, session, roomId, _, err := p.mcu.getOrCreatePublisherHandle(ctx, p.id, p.streamType, p.bitrate) if err != nil { - log.Printf("Could not reconnect remote publisher %s: %s", p.id, err) + p.log.Error("Could not reconnect remote publisher", + zap.Error(err), + ) // TODO(jojo): Retry return } @@ -107,8 +126,9 @@ func (p *mcuJanusRemotePublisher) NotifyReconnected() { p.handleId = handle.Id p.session = session p.roomId = roomId + p.updateLogger() - log.Printf("Remote publisher %s reconnected on handle %d", p.id, p.handleId) + p.log.Info("Remote publisher reconnected") } func (p *mcuJanusRemotePublisher) Close(ctx context.Context) { @@ -124,9 +144,13 @@ func (p *mcuJanusRemotePublisher) Close(ctx context.Context) { "id": streamTypeUserIds[p.streamType], }) if err != nil { - log.Printf("Error removing remote publisher %s in room %d: %s", p.id, p.roomId, err) + p.log.Error("Error removing remote publisher", + zap.Error(err), + ) } else { - log.Printf("Removed remote publisher: %+v", response) + p.log.Info("Removed remote publisher", + zap.Any("response", response), + ) } if p.roomId != 0 { destroy_msg := map[string]interface{}{ @@ -134,9 +158,11 @@ func (p *mcuJanusRemotePublisher) Close(ctx context.Context) { "room": p.roomId, } if _, err := handle.Request(ctx, destroy_msg); err != nil { - log.Printf("Error destroying room %d: %s", p.roomId, err) + p.log.Error("Error destroying room for remote publisher", + zap.Error(err), + ) } else { - log.Printf("Room %d destroyed", p.roomId) + p.log.Info("Room for remote publisher destroyed") } p.mcu.mu.Lock() delete(p.mcu.remotePublishers, getStreamId(p.id, p.streamType)) diff --git a/mcu_janus_remote_subscriber.go b/mcu_janus_remote_subscriber.go index 09004160..7c6b738b 100644 --- a/mcu_janus_remote_subscriber.go +++ b/mcu_janus_remote_subscriber.go @@ -23,11 +23,11 @@ package signaling import ( "context" - "log" "strconv" "sync/atomic" "github.com/notedit/janus-go" + "go.uber.org/zap" ) type mcuJanusRemoteSubscriber struct { @@ -36,12 +36,16 @@ type mcuJanusRemoteSubscriber struct { remote atomic.Pointer[mcuJanusRemotePublisher] } +func (p *mcuJanusRemoteSubscriber) updateLogger() { + p.mcuJanusSubscriber.updateLogger() +} + func (p *mcuJanusRemoteSubscriber) handleEvent(event *janus.EventMsg) { if videoroom := getPluginStringValue(event.Plugindata, pluginVideoRoom, "videoroom"); videoroom != "" { ctx := context.TODO() switch videoroom { case "destroyed": - log.Printf("Remote subscriber %d: associated room has been destroyed, closing", p.handleId) + p.log.Info("Remote subscriber: associated room has been destroyed, closing") go p.Close(ctx) case "event": // Handle renegotiations, but ignore other events like selected @@ -53,33 +57,46 @@ func (p *mcuJanusRemoteSubscriber) handleEvent(event *janus.EventMsg) { case "slow_link": // Ignore, processed through "handleSlowLink" in the general events. default: - log.Printf("Unsupported videoroom event %s for remote subscriber %d: %+v", videoroom, p.handleId, event) + p.log.Warn("Unsupported videoroom event for remote subscriber", + zap.String("videoroom", videoroom), + zap.Any("event", event), + ) } } else { - log.Printf("Unsupported event for remote subscriber %d: %+v", p.handleId, event) + p.log.Warn("Unsupported event for remote subscriber", + zap.Any("event", event), + ) } } func (p *mcuJanusRemoteSubscriber) handleHangup(event *janus.HangupMsg) { - log.Printf("Remote subscriber %d received hangup (%s), closing", p.handleId, event.Reason) + p.log.Info("Remote subscriber received hangup, closing", + zap.String("reason", event.Reason), + ) go p.Close(context.Background()) } func (p *mcuJanusRemoteSubscriber) handleDetached(event *janus.DetachedMsg) { - log.Printf("Remote subscriber %d received detached, closing", p.handleId) + p.log.Info("Remote subscriber received detached, closing") go p.Close(context.Background()) } func (p *mcuJanusRemoteSubscriber) handleConnected(event *janus.WebRTCUpMsg) { - log.Printf("Remote subscriber %d received connected", p.handleId) + p.log.Info("Remote subscriber received connected") p.mcu.SubscriberConnected(p.Id(), p.publisher, p.streamType) } func (p *mcuJanusRemoteSubscriber) handleSlowLink(event *janus.SlowLinkMsg) { if event.Uplink { - log.Printf("Remote subscriber %s (%d) is reporting %d lost packets on the uplink (Janus -> client)", p.listener.PublicId(), p.handleId, event.Lost) + p.log.Info("Remote subscriber is reporting lost packets on the uplink (Janus -> client)", + zap.String("sessionid", p.listener.PublicId()), + zap.Int64("lost", event.Lost), + ) } else { - log.Printf("Remote subscriber %s (%d) is reporting %d lost packets on the downlink (client -> Janus)", p.listener.PublicId(), p.handleId, event.Lost) + p.log.Info("Remote subscriber is reporting lost packets on the downlink (client -> Janus)", + zap.String("sessionid", p.listener.PublicId()), + zap.Int64("lost", event.Lost), + ) } } @@ -93,7 +110,10 @@ func (p *mcuJanusRemoteSubscriber) NotifyReconnected() { handle, pub, err := p.mcu.getOrCreateSubscriberHandle(ctx, p.publisher, p.streamType) if err != nil { // TODO(jojo): Retry? - log.Printf("Could not reconnect remote subscriber for publisher %s: %s", p.publisher, err) + p.log.Info("Could not reconnect remote subscriber", + zap.String("publisher", p.publisher), + zap.Error(err), + ) p.Close(context.Background()) return } @@ -102,8 +122,11 @@ func (p *mcuJanusRemoteSubscriber) NotifyReconnected() { p.handleId = handle.Id p.roomId = pub.roomId p.sid = strconv.FormatUint(handle.Id, 10) + p.updateLogger() p.listener.SubscriberSidUpdated(p) - log.Printf("Subscriber %d for publisher %s reconnected on handle %d", p.id, p.publisher, p.handleId) + p.log.Info("Remote subscriber reconnected", + zap.String("publisher", p.publisher), + ) } func (p *mcuJanusRemoteSubscriber) Close(ctx context.Context) { diff --git a/mcu_janus_subscriber.go b/mcu_janus_subscriber.go index b63f4e96..eb23cd09 100644 --- a/mcu_janus_subscriber.go +++ b/mcu_janus_subscriber.go @@ -24,10 +24,10 @@ package signaling import ( "context" "fmt" - "log" "strconv" "github.com/notedit/janus-go" + "go.uber.org/zap" ) type mcuJanusSubscriber struct { @@ -40,12 +40,16 @@ func (p *mcuJanusSubscriber) Publisher() string { return p.publisher } +func (p *mcuJanusSubscriber) updateLogger() { + p.mcuJanusClient.updateLogger() +} + func (p *mcuJanusSubscriber) handleEvent(event *janus.EventMsg) { if videoroom := getPluginStringValue(event.Plugindata, pluginVideoRoom, "videoroom"); videoroom != "" { ctx := context.TODO() switch videoroom { case "destroyed": - log.Printf("Subscriber %d: associated room has been destroyed, closing", p.handleId) + p.log.Info("Subscriber: associated room has been destroyed, closing") go p.Close(ctx) case "event": // Handle renegotiations, but ignore other events like selected @@ -57,33 +61,46 @@ func (p *mcuJanusSubscriber) handleEvent(event *janus.EventMsg) { case "slow_link": // Ignore, processed through "handleSlowLink" in the general events. default: - log.Printf("Unsupported videoroom event %s for subscriber %d: %+v", videoroom, p.handleId, event) + p.log.Warn("Unsupported videoroom event for subscriber", + zap.String("videoroom", videoroom), + zap.Any("event", event), + ) } } else { - log.Printf("Unsupported event for subscriber %d: %+v", p.handleId, event) + p.log.Warn("Unsupported event for subscriber", + zap.Any("event", event), + ) } } func (p *mcuJanusSubscriber) handleHangup(event *janus.HangupMsg) { - log.Printf("Subscriber %d received hangup (%s), closing", p.handleId, event.Reason) + p.log.Info("Subscriber received hangup, closing", + zap.String("reason", event.Reason), + ) go p.Close(context.Background()) } func (p *mcuJanusSubscriber) handleDetached(event *janus.DetachedMsg) { - log.Printf("Subscriber %d received detached, closing", p.handleId) + p.log.Info("Subscriber received detached, closing") go p.Close(context.Background()) } func (p *mcuJanusSubscriber) handleConnected(event *janus.WebRTCUpMsg) { - log.Printf("Subscriber %d received connected", p.handleId) + p.log.Info("Subscriber received connected") p.mcu.SubscriberConnected(p.Id(), p.publisher, p.streamType) } func (p *mcuJanusSubscriber) handleSlowLink(event *janus.SlowLinkMsg) { if event.Uplink { - log.Printf("Subscriber %s (%d) is reporting %d lost packets on the uplink (Janus -> client)", p.listener.PublicId(), p.handleId, event.Lost) + p.log.Info("Subscriber is reporting lost packets on the uplink (Janus -> client)", + zap.String("sessionid", p.listener.PublicId()), + zap.Int64("lost", event.Lost), + ) } else { - log.Printf("Subscriber %s (%d) is reporting %d lost packets on the downlink (client -> Janus)", p.listener.PublicId(), p.handleId, event.Lost) + p.log.Info("Subscriber is reporting lost packets on the downlink (client -> Janus)", + zap.String("sessionid", p.listener.PublicId()), + zap.Int64("lost", event.Lost), + ) } } @@ -97,7 +114,10 @@ func (p *mcuJanusSubscriber) NotifyReconnected() { handle, pub, err := p.mcu.getOrCreateSubscriberHandle(ctx, p.publisher, p.streamType) if err != nil { // TODO(jojo): Retry? - log.Printf("Could not reconnect subscriber for publisher %s: %s", p.publisher, err) + p.log.Error("Could not reconnect subscriber", + zap.String("publisher", p.publisher), + zap.Error(err), + ) p.Close(context.Background()) return } @@ -106,8 +126,11 @@ func (p *mcuJanusSubscriber) NotifyReconnected() { p.handleId = handle.Id p.roomId = pub.roomId p.sid = strconv.FormatUint(handle.Id, 10) + p.updateLogger() p.listener.SubscriberSidUpdated(p) - log.Printf("Subscriber %d for publisher %s reconnected on handle %d", p.id, p.publisher, p.handleId) + p.log.Info("Subscriber reconnected", + zap.String("publisher", p.publisher), + ) } func (p *mcuJanusSubscriber) Close(ctx context.Context) { @@ -187,19 +210,29 @@ retry: p.handleId = handle.Id p.roomId = pub.roomId p.sid = strconv.FormatUint(handle.Id, 10) + p.updateLogger() p.listener.SubscriberSidUpdated(p) p.closeChan = make(chan struct{}, 1) go p.run(p.handle, p.closeChan) - log.Printf("Already connected subscriber %d for %s, leaving and re-joining on handle %d", p.id, p.streamType, p.handleId) + p.log.Warn("Already connected subscriber, leaving and re-joining", + zap.String("publisher", p.publisher), + zap.Any("streamtype", p.streamType), + ) goto retry case JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM: fallthrough case JANUS_VIDEOROOM_ERROR_NO_SUCH_FEED: switch error_code { case JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM: - log.Printf("Publisher %s not created yet for %s, wait and retry to join room %d as subscriber", p.publisher, p.streamType, p.roomId) + p.log.Info("Publisher not created yet, wait and retry to join room as subscriber", + zap.String("publisher", p.publisher), + zap.Any("streamtype", p.streamType), + ) case JANUS_VIDEOROOM_ERROR_NO_SUCH_FEED: - log.Printf("Publisher %s not sending yet for %s, wait and retry to join room %d as subscriber", p.publisher, p.streamType, p.roomId) + p.log.Info("Publisher not sending yet, wait and retry to join room as subscriber", + zap.String("publisher", p.publisher), + zap.Any("streamtype", p.streamType), + ) } if !loggedNotPublishingYet { @@ -211,7 +244,10 @@ retry: callback(err, nil) return } - log.Printf("Retry subscribing %s from %s", p.streamType, p.publisher) + p.log.Info("Retry subscribing", + zap.String("publisher", p.publisher), + zap.Any("streamtype", p.streamType), + ) goto retry default: // TODO(jojo): Should we handle other errors, too? diff --git a/mcu_proxy.go b/mcu_proxy.go index 47df82d5..3d33479f 100644 --- a/mcu_proxy.go +++ b/mcu_proxy.go @@ -28,7 +28,6 @@ import ( "encoding/json" "errors" "fmt" - "log" "net" "net/http" "net/url" @@ -44,6 +43,7 @@ import ( "github.com/dlintw/goconf" "github.com/golang-jwt/jwt/v4" "github.com/gorilla/websocket" + "go.uber.org/zap" ) const ( @@ -76,6 +76,7 @@ type McuProxy interface { } type mcuProxyPubSubCommon struct { + log *zap.Logger sid string streamType StreamType maxBitrate int @@ -108,7 +109,9 @@ func (c *mcuProxyPubSubCommon) doSendMessage(ctx context.Context, msg *ProxyClie } if proxyDebugMessages { - log.Printf("Response from %s: %+v", c.conn, response) + c.log.Debug("Response received", + zap.Stringer("response", response), + ) } if response.Type == "error" { callback(response.Error, nil) @@ -127,7 +130,9 @@ func (c *mcuProxyPubSubCommon) doProcessPayload(client McuClient, msg *PayloadPr case "candidate": c.listener.OnIceCandidate(client, msg.Payload["candidate"]) default: - log.Printf("Unsupported payload from %s: %+v", c.conn, msg) + c.log.Warn("Unsupported payload", + zap.Any("message", msg), + ) } } @@ -138,9 +143,12 @@ type mcuProxyPublisher struct { mediaTypes MediaType } -func newMcuProxyPublisher(id string, sid string, streamType StreamType, maxBitrate int, mediaTypes MediaType, proxyId string, conn *mcuProxyConnection, listener McuListener) *mcuProxyPublisher { +func newMcuProxyPublisher(log *zap.Logger, id string, sid string, streamType StreamType, maxBitrate int, mediaTypes MediaType, proxyId string, conn *mcuProxyConnection, listener McuListener) *mcuProxyPublisher { return &mcuProxyPublisher{ mcuProxyPubSubCommon: mcuProxyPubSubCommon{ + log: log.With( + zap.String("proxyid", proxyId), + ), sid: sid, streamType: streamType, maxBitrate: maxBitrate, @@ -163,7 +171,7 @@ func (p *mcuProxyPublisher) SetMedia(mt MediaType) { } func (p *mcuProxyPublisher) NotifyClosed() { - log.Printf("Publisher %s at %s was closed", p.proxyId, p.conn) + p.log.Info("Publisher was closed") p.listener.PublisherClosed(p) p.conn.removePublisher(p) } @@ -180,14 +188,18 @@ func (p *mcuProxyPublisher) Close(ctx context.Context) { } if response, err := p.conn.performSyncRequest(ctx, msg); err != nil { - log.Printf("Could not delete publisher %s at %s: %s", p.proxyId, p.conn, err) + p.log.Error("Could not delete publisher", + zap.Error(err), + ) return } else if response.Type == "error" { - log.Printf("Could not delete publisher %s at %s: %s", p.proxyId, p.conn, response.Error) + p.log.Error("Could not delete publisher", + zap.Any("error", response.Error), + ) return } - log.Printf("Deleted publisher %s at %s", p.proxyId, p.conn) + p.log.Info("Deleted publisher") } func (p *mcuProxyPublisher) SendMessage(ctx context.Context, message *MessageClientMessage, data *MessageClientMessageData, callback func(error, map[string]interface{})) { @@ -215,7 +227,9 @@ func (p *mcuProxyPublisher) ProcessEvent(msg *EventProxyServerMessage) { case "publisher-closed": p.NotifyClosed() default: - log.Printf("Unsupported event from %s: %+v", p.conn, msg) + p.log.Warn("Unsupported event", + zap.Any("event", msg), + ) } } @@ -238,9 +252,12 @@ type mcuProxySubscriber struct { publisherConn *mcuProxyConnection } -func newMcuProxySubscriber(publisherId string, sid string, streamType StreamType, maxBitrate int, proxyId string, conn *mcuProxyConnection, listener McuListener, publisherConn *mcuProxyConnection) *mcuProxySubscriber { - return &mcuProxySubscriber{ +func newMcuProxySubscriber(log *zap.Logger, publisherId string, sid string, streamType StreamType, maxBitrate int, proxyId string, conn *mcuProxyConnection, listener McuListener, publisherConn *mcuProxyConnection) *mcuProxySubscriber { + result := &mcuProxySubscriber{ mcuProxyPubSubCommon: mcuProxyPubSubCommon{ + log: log.With( + zap.String("proxyid", proxyId), + ), sid: sid, streamType: streamType, maxBitrate: maxBitrate, @@ -252,6 +269,14 @@ func newMcuProxySubscriber(publisherId string, sid string, streamType StreamType publisherId: publisherId, publisherConn: publisherConn, } + + if publisherConn != nil { + result.log = result.log.With( + zap.Stringer("publisherconn", publisherConn), + ) + } + + return result } func (s *mcuProxySubscriber) Publisher() string { @@ -260,9 +285,9 @@ func (s *mcuProxySubscriber) Publisher() string { func (s *mcuProxySubscriber) NotifyClosed() { if s.publisherConn != nil { - log.Printf("Remote subscriber %s at %s (forwarded to %s) was closed", s.proxyId, s.conn, s.publisherConn) + s.log.Info("Remote subscriber was closed") } else { - log.Printf("Subscriber %s at %s was closed", s.proxyId, s.conn) + s.log.Info("Subscriber was closed") } s.listener.SubscriberClosed(s) s.conn.removeSubscriber(s) @@ -281,24 +306,32 @@ func (s *mcuProxySubscriber) Close(ctx context.Context) { if response, err := s.conn.performSyncRequest(ctx, msg); err != nil { if s.publisherConn != nil { - log.Printf("Could not delete remote subscriber %s at %s (forwarded to %s): %s", s.proxyId, s.conn, s.publisherConn, err) + s.log.Error("Could not delete remote subscriber", + zap.Error(err), + ) } else { - log.Printf("Could not delete subscriber %s at %s: %s", s.proxyId, s.conn, err) + s.log.Error("Could not delete subscriber", + zap.Error(err), + ) } return } else if response.Type == "error" { if s.publisherConn != nil { - log.Printf("Could not delete remote subscriber %s at %s (forwarded to %s): %s", s.proxyId, s.conn, s.publisherConn, response.Error) + s.log.Error("Could not delete remote subscriber", + zap.Any("error", response.Error), + ) } else { - log.Printf("Could not delete subscriber %s at %s: %s", s.proxyId, s.conn, response.Error) + s.log.Error("Could not delete subscriber", + zap.Any("error", response.Error), + ) } return } if s.publisherConn != nil { - log.Printf("Deleted remote subscriber %s at %s (forwarded to %s)", s.proxyId, s.conn, s.publisherConn) + s.log.Info("Deleted remote subscriber") } else { - log.Printf("Deleted subscriber %s at %s", s.proxyId, s.conn) + s.log.Info("Deleted subscriber") } } @@ -330,11 +363,14 @@ func (s *mcuProxySubscriber) ProcessEvent(msg *EventProxyServerMessage) { case "subscriber-closed": s.NotifyClosed() default: - log.Printf("Unsupported event from %s: %+v", s.conn, msg) + s.log.Warn("Unsupported event", + zap.Any("event", msg), + ) } } type mcuProxyConnection struct { + log *zap.Logger proxy *mcuProxy rawUrl string url *url.URL @@ -373,13 +409,16 @@ type mcuProxyConnection struct { subscribers map[string]*mcuProxySubscriber } -func newMcuProxyConnection(proxy *mcuProxy, baseUrl string, ip net.IP) (*mcuProxyConnection, error) { +func newMcuProxyConnection(log *zap.Logger, proxy *mcuProxy, baseUrl string, ip net.IP) (*mcuProxyConnection, error) { parsed, err := url.Parse(baseUrl) if err != nil { return nil, err } conn := &mcuProxyConnection{ + log: log.With( + zap.String("url", baseUrl), + ), proxy: proxy, rawUrl: baseUrl, url: parsed, @@ -391,6 +430,11 @@ func newMcuProxyConnection(proxy *mcuProxy, baseUrl string, ip net.IP) (*mcuProx publisherIds: make(map[string]string), subscribers: make(map[string]*mcuProxySubscriber), } + if len(ip) > 0 { + conn.log = conn.log.With( + zap.Stringer("ip", ip), + ) + } conn.reconnectInterval.Store(int64(initialReconnectInterval)) conn.load.Store(loadNotConnected) conn.bandwidth.Store(nil) @@ -564,7 +608,10 @@ func (c *mcuProxyConnection) readPump() { rtt := now.Sub(time.Unix(0, ts)) if rtt >= rttLogDuration { rtt_ms := rtt.Nanoseconds() / time.Millisecond.Nanoseconds() - log.Printf("Proxy at %s has RTT of %d ms (%s)", c, rtt_ms, rtt) + c.log.Info("Proxy has RTT", + zap.Int64("rttms", rtt_ms), + zap.Duration("rtt", rtt), + ) } } return nil @@ -580,14 +627,19 @@ func (c *mcuProxyConnection) readPump() { websocket.CloseNormalClosure, websocket.CloseGoingAway, websocket.CloseNoStatusReceived) { - log.Printf("Error reading from %s: %v", c, err) + c.log.Error("Error reading from proxy", + zap.Error(err), + ) } break } var msg ProxyServerMessage if err := json.Unmarshal(message, &msg); err != nil { - log.Printf("Error unmarshaling %s from %s: %s", string(message), c, err) + c.log.Error("Error unmarshaling", + zap.ByteString("message", message), + zap.Error(err), + ) continue } @@ -606,7 +658,9 @@ func (c *mcuProxyConnection) sendPing() bool { msg := strconv.FormatInt(now.UnixNano(), 10) c.conn.SetWriteDeadline(now.Add(writeWait)) // nolint if err := c.conn.WriteMessage(websocket.PingMessage, []byte(msg)); err != nil { - log.Printf("Could not send ping to proxy at %s: %v", c, err) + c.log.Error("Could not send ping", + zap.Error(err), + ) go c.scheduleReconnect() return false } @@ -658,7 +712,9 @@ func (c *mcuProxyConnection) stop(ctx context.Context) { c.closer.Close() if err := c.sendClose(); err != nil { if err != ErrNotConnected { - log.Printf("Could not send close message to %s: %s", c, err) + c.log.Error("Could not send close message", + zap.Error(err), + ) } c.close() return @@ -668,7 +724,9 @@ func (c *mcuProxyConnection) stop(ctx context.Context) { case <-c.closedDone.C: case <-ctx.Done(): if err := ctx.Err(); err != nil { - log.Printf("Error waiting for connection to %s get closed: %s", c, err) + c.log.Error("Error waiting for connection to get closed", + zap.Error(err), + ) c.close() } } @@ -705,7 +763,9 @@ func (c *mcuProxyConnection) closeIfEmpty() bool { c.subscribersLock.RUnlock() if total > 0 { // Connection will be closed once all clients have disconnected. - log.Printf("Connection to %s is still used by %d clients, defer closing", c, total) + c.log.Info("Connection is still used by clients, defer closing", + zap.Int64("count", total), + ) return false } @@ -713,7 +773,7 @@ func (c *mcuProxyConnection) closeIfEmpty() bool { ctx, cancel := context.WithTimeout(context.Background(), closeTimeout) defer cancel() - log.Printf("All clients disconnected, closing connection to %s", c) + c.log.Info("All clients disconnected, closing connection") c.stop(ctx) c.proxy.removeConnection(c) @@ -723,7 +783,9 @@ func (c *mcuProxyConnection) closeIfEmpty() bool { func (c *mcuProxyConnection) scheduleReconnect() { if err := c.sendClose(); err != nil && err != ErrNotConnected { - log.Printf("Could not send close message to %s: %s", c, err) + c.log.Error("Could not send close message", + zap.Error(err), + ) } c.close() @@ -745,7 +807,9 @@ func (c *mcuProxyConnection) scheduleReconnect() { func (c *mcuProxyConnection) reconnect() { u, err := c.url.Parse("proxy") if err != nil { - log.Printf("Could not resolve url to proxy at %s: %s", c, err) + c.log.Error("Could not resolve url to proxy", + zap.Error(err), + ) c.scheduleReconnect() return } @@ -774,7 +838,9 @@ func (c *mcuProxyConnection) reconnect() { } conn, _, err := dialer.Dial(u.String(), nil) if err != nil { - log.Printf("Could not connect to %s: %s", c, err) + c.log.Error("Could not connect", + zap.Error(err), + ) c.scheduleReconnect() return } @@ -784,7 +850,7 @@ func (c *mcuProxyConnection) reconnect() { return } - log.Printf("Connected to %s", c) + c.log.Info("Connected") c.closed.Store(false) c.mu.Lock() @@ -795,7 +861,9 @@ func (c *mcuProxyConnection) reconnect() { c.reconnectInterval.Store(int64(initialReconnectInterval)) c.shutdownScheduled.Store(false) if err := c.sendHello(); err != nil { - log.Printf("Could not send hello request to %s: %s", c, err) + c.log.Error("Could not send hello request", + zap.Error(err), + ) c.scheduleReconnect() return } @@ -913,19 +981,25 @@ func (c *mcuProxyConnection) processMessage(msg *ProxyServerMessage) { switch msg.Type { case "error": if msg.Error.Code == "no_such_session" { - log.Printf("Session %s could not be resumed on %s, registering new", c.SessionId(), c) + c.log.Error("Session could not be resumed, registering new", + zap.String("sessionid", c.SessionId()), + ) c.clearPublishers() c.clearSubscribers() c.clearCallbacks() c.sessionId.Store("") if err := c.sendHello(); err != nil { - log.Printf("Could not send hello request to %s: %s", c, err) + c.log.Error("Could not send hello request", + zap.Error(err), + ) c.scheduleReconnect() } return } - log.Printf("Hello connection to %s failed with %+v, reconnecting", c, msg.Error) + c.log.Error("Hello connection failed, reconnecting", + zap.Any("error", msg.Error), + ) c.scheduleReconnect() case "hello": resumed := c.SessionId() == msg.Hello.SessionId @@ -933,17 +1007,26 @@ func (c *mcuProxyConnection) processMessage(msg *ProxyServerMessage) { country := "" if msg.Hello.Server != nil { if country = msg.Hello.Server.Country; country != "" && !IsValidCountry(country) { - log.Printf("Proxy %s sent invalid country %s in hello response", c, country) + c.log.Warn("Proxy sent invalid country in hello response", + zap.String("country", country), + ) country = "" } } c.country.Store(country) if resumed { - log.Printf("Resumed session %s on %s", c.SessionId(), c) + c.log.Info("Resumed session", + zap.String("sessionid", c.SessionId()), + ) } else if country != "" { - log.Printf("Received session %s from %s (in %s)", c.SessionId(), c, country) + c.log.Info("Received session", + zap.String("sessionid", c.SessionId()), + zap.String("country", country), + ) } else { - log.Printf("Received session %s from %s", c.SessionId(), c) + c.log.Info("Received session", + zap.String("sessionid", c.SessionId()), + ) } if c.trackClose.CompareAndSwap(false, true) { statsConnectedProxyBackendsCurrent.WithLabelValues(c.Country()).Inc() @@ -951,14 +1034,18 @@ func (c *mcuProxyConnection) processMessage(msg *ProxyServerMessage) { c.connectedNotifier.Notify() default: - log.Printf("Received unsupported hello response %+v from %s, reconnecting", msg, c) + c.log.Error("Received unsupported hello response, reconnecting", + zap.Stringer("response", msg), + ) c.scheduleReconnect() } return } if proxyDebugMessages { - log.Printf("Received from %s: %+v", c, msg) + c.log.Debug("Received proxy message", + zap.Stringer("message", msg), + ) } callback := c.getCallback(msg.Id) if callback != nil { @@ -974,7 +1061,9 @@ func (c *mcuProxyConnection) processMessage(msg *ProxyServerMessage) { case "bye": c.processBye(msg) default: - log.Printf("Unsupported message received from %s: %+v", c, msg) + c.log.Warn("Unsupported message received", + zap.Stringer("message", msg), + ) } } @@ -996,38 +1085,45 @@ func (c *mcuProxyConnection) processPayload(msg *ProxyServerMessage) { return } - log.Printf("Received payload for unknown client %+v from %s", payload, c) + c.log.Warn("Received payload for unknown client", + zap.Any("payload", payload), + ) } func (c *mcuProxyConnection) processEvent(msg *ProxyServerMessage) { event := msg.Event switch event.Type { case "backend-disconnected": - log.Printf("Upstream backend at %s got disconnected, reset MCU objects", c) + c.log.Warn("Upstream backend got disconnected, reset MCU objects") c.clearPublishers() c.clearSubscribers() c.clearCallbacks() // TODO: Should we also reconnect? return case "backend-connected": - log.Printf("Upstream backend at %s is connected", c) + c.log.Info("Upstream backend is connected") return case "update-load": if proxyDebugMessages { - log.Printf("Load of %s now at %d (%s)", c, event.Load, event.Bandwidth) + c.log.Debug("Load changed", + zap.Int64("load", event.Load), + zap.Any("bandwidth", event.Bandwidth), + ) } c.load.Store(event.Load) c.bandwidth.Store(event.Bandwidth) statsProxyBackendLoadCurrent.WithLabelValues(c.url.String()).Set(float64(event.Load)) return case "shutdown-scheduled": - log.Printf("Proxy %s is scheduled to shutdown", c) + c.log.Info("Proxy is scheduled to shutdown") c.shutdownScheduled.Store(true) return } if proxyDebugMessages { - log.Printf("Process event from %s: %+v", c, event) + c.log.Debug("Process proxy event", + zap.Any("event", event), + ) } c.publishersLock.RLock() publisher, found := c.publishers[event.ClientId] @@ -1045,17 +1141,23 @@ func (c *mcuProxyConnection) processEvent(msg *ProxyServerMessage) { return } - log.Printf("Received event for unknown client %+v from %s", event, c) + c.log.Warn("Received event for unknown client", + zap.Any("event", event), + ) } func (c *mcuProxyConnection) processBye(msg *ProxyServerMessage) { bye := msg.Bye switch bye.Reason { case "session_resumed": - log.Printf("Session %s on %s was resumed by other client, resetting", c.SessionId(), c) + c.log.Warn("Session was resumed by other client, resetting", + zap.String("sessionid", c.SessionId()), + ) c.sessionId.Store("") default: - log.Printf("Received bye with unsupported reason from %s %+v", c, bye) + c.log.Warn("Received bye with unsupported reason", + zap.Any("bye", bye), + ) } } @@ -1090,7 +1192,9 @@ func (c *mcuProxyConnection) sendMessage(msg *ProxyClientMessage) error { func (c *mcuProxyConnection) sendMessageLocked(msg *ProxyClientMessage) error { if proxyDebugMessages { - log.Printf("Send message to %s: %+v", c, msg) + c.log.Debug("Send proxy message", + zap.Stringer("message", msg), + ) } if c.conn == nil { return ErrNotConnected @@ -1161,8 +1265,12 @@ func (c *mcuProxyConnection) newPublisher(ctx context.Context, listener McuListe } proxyId := response.Command.Id - log.Printf("Created %s publisher %s on %s for %s", streamType, proxyId, c, id) - publisher := newMcuProxyPublisher(id, sid, streamType, response.Command.Bitrate, mediaTypes, proxyId, c, listener) + c.log.Info("Created publisher", + zap.Any("streamtype", streamType), + zap.String("proxyid", proxyId), + zap.String("id", id), + ) + publisher := newMcuProxyPublisher(c.log, id, sid, streamType, response.Command.Bitrate, mediaTypes, proxyId, c, listener) c.publishersLock.Lock() c.publishers[proxyId] = publisher c.publisherIds[getStreamId(id, streamType)] = proxyId @@ -1191,8 +1299,12 @@ func (c *mcuProxyConnection) newSubscriber(ctx context.Context, listener McuList } proxyId := response.Command.Id - log.Printf("Created %s subscriber %s on %s for %s", streamType, proxyId, c, publisherSessionId) - subscriber := newMcuProxySubscriber(publisherSessionId, response.Command.Sid, streamType, response.Command.Bitrate, proxyId, c, listener, nil) + c.log.Info("Created subscriber", + zap.Any("streamtype", streamType), + zap.String("proxyid", proxyId), + zap.String("publisherid", publisherSessionId), + ) + subscriber := newMcuProxySubscriber(c.log, publisherSessionId, response.Command.Sid, streamType, response.Command.Bitrate, proxyId, c, listener, nil) c.subscribersLock.Lock() c.subscribers[proxyId] = subscriber c.subscribersLock.Unlock() @@ -1232,8 +1344,13 @@ func (c *mcuProxyConnection) newRemoteSubscriber(ctx context.Context, listener M } proxyId := response.Command.Id - log.Printf("Created remote %s subscriber %s on %s for %s (forwarded to %s)", streamType, proxyId, c, publisherSessionId, publisherConn) - subscriber := newMcuProxySubscriber(publisherSessionId, response.Command.Sid, streamType, response.Command.Bitrate, proxyId, c, listener, publisherConn) + c.log.Info("Created remote subscriber", + zap.Any("streamtype", streamType), + zap.String("proxyid", proxyId), + zap.String("publisherid", publisherSessionId), + zap.Stringer("publisherconn", publisherConn), + ) + subscriber := newMcuProxySubscriber(c.log, publisherSessionId, response.Command.Sid, streamType, response.Command.Bitrate, proxyId, c, listener, publisherConn) c.subscribersLock.Lock() c.subscribers[proxyId] = subscriber c.subscribersLock.Unlock() @@ -1243,6 +1360,7 @@ func (c *mcuProxyConnection) newRemoteSubscriber(ctx context.Context, listener M } type mcuProxy struct { + log *zap.Logger urlType string tokenId string tokenKey *rsa.PrivateKey @@ -1269,7 +1387,7 @@ type mcuProxy struct { rpcClients *GrpcClients } -func NewMcuProxy(config *goconf.ConfigFile, etcdClient *EtcdClient, rpcClients *GrpcClients, dnsMonitor *DnsMonitor) (Mcu, error) { +func NewMcuProxy(log *zap.Logger, config *goconf.ConfigFile, etcdClient *EtcdClient, rpcClients *GrpcClients, dnsMonitor *DnsMonitor) (Mcu, error) { urlType, _ := config.GetString("mcu", "urltype") if urlType == "" { urlType = proxyUrlTypeStatic @@ -1297,7 +1415,9 @@ func NewMcuProxy(config *goconf.ConfigFile, etcdClient *EtcdClient, rpcClients * proxyTimeoutSeconds = defaultProxyTimeoutSeconds } proxyTimeout := time.Duration(proxyTimeoutSeconds) * time.Second - log.Printf("Using a timeout of %s for proxy requests", proxyTimeout) + log.Info("Using a timeout for proxy requests", + zap.Duration("timeout", proxyTimeout), + ) maxStreamBitrate, _ := config.GetInt("mcu", "maxstreambitrate") if maxStreamBitrate <= 0 { @@ -1309,6 +1429,7 @@ func NewMcuProxy(config *goconf.ConfigFile, etcdClient *EtcdClient, rpcClients * } mcu := &mcuProxy{ + log: log, urlType: urlType, tokenId: tokenId, tokenKey: tokenKey, @@ -1334,7 +1455,7 @@ func NewMcuProxy(config *goconf.ConfigFile, etcdClient *EtcdClient, rpcClients * skipverify, _ := config.GetBool("mcu", "skipverify") if skipverify { - log.Println("WARNING: MCU verification is disabled!") + log.Warn("MCU verification is disabled!") mcu.dialer.TLSClientConfig = &tls.Config{ InsecureSkipVerify: skipverify, } @@ -1342,9 +1463,9 @@ func NewMcuProxy(config *goconf.ConfigFile, etcdClient *EtcdClient, rpcClients * switch urlType { case proxyUrlTypeStatic: - mcu.config, err = NewProxyConfigStatic(config, mcu, dnsMonitor) + mcu.config, err = NewProxyConfigStatic(log, config, mcu, dnsMonitor) case proxyUrlTypeEtcd: - mcu.config, err = NewProxyConfigEtcd(config, etcdClient, mcu) + mcu.config, err = NewProxyConfigEtcd(log, config, etcdClient, mcu) default: err = fmt.Errorf("Unsupported proxy URL type %s", urlType) } @@ -1370,7 +1491,9 @@ func (m *mcuProxy) loadContinentsMap(config *goconf.ConfigFile) error { for option, value := range options { option = strings.ToUpper(strings.TrimSpace(option)) if !IsValidContinent(option) { - log.Printf("Ignore unknown continent %s", option) + m.log.Warn("Ignore unknown continent", + zap.String("continent", option), + ) continue } @@ -1378,18 +1501,26 @@ func (m *mcuProxy) loadContinentsMap(config *goconf.ConfigFile) error { for _, v := range strings.Split(value, ",") { v = strings.ToUpper(strings.TrimSpace(v)) if !IsValidContinent(v) { - log.Printf("Ignore unknown continent %s for override %s", v, option) + m.log.Warn("Ignore unknown continent for override", + zap.String("continent", v), + zap.String("override", option), + ) continue } values = append(values, v) } if len(values) == 0 { - log.Printf("No valid values found for continent override %s, ignoring", option) + m.log.Warn("No valid values found for continent override, ignoring", + zap.String("override", option), + ) continue } continentsMap[option] = values - log.Printf("Mapping users on continent %s to %s", option, values) + m.log.Info("Mapping users on continent", + zap.String("continent", option), + zap.Any("target", values), + ) } m.setContinentsMap(continentsMap) @@ -1397,8 +1528,12 @@ func (m *mcuProxy) loadContinentsMap(config *goconf.ConfigFile) error { } func (m *mcuProxy) Start(ctx context.Context) error { - log.Printf("Maximum bandwidth %d bits/sec per publishing stream", m.maxStreamBitrate.Load()) - log.Printf("Maximum bandwidth %d bits/sec per screensharing stream", m.maxScreenBitrate.Load()) + m.log.Info("Maximum bandwidth per publishing stream", + zap.Int32("bitrate", m.maxStreamBitrate.Load()), + ) + m.log.Info("Maximum bandwidth per screensharing stream", + zap.Int32("bitrate", m.maxScreenBitrate.Load()), + ) return m.config.Start() } @@ -1464,10 +1599,13 @@ func (m *mcuProxy) AddConnection(ignoreErrors bool, url string, ips ...net.IP) e var conns []*mcuProxyConnection if len(ips) == 0 { - conn, err := newMcuProxyConnection(m, url, nil) + conn, err := newMcuProxyConnection(m.log, m, url, nil) if err != nil { if ignoreErrors { - log.Printf("Could not create proxy connection to %s: %s", url, err) + m.log.Error("Could not create proxy connection", + zap.String("url", url), + zap.Error(err), + ) return nil } @@ -1477,10 +1615,14 @@ func (m *mcuProxy) AddConnection(ignoreErrors bool, url string, ips ...net.IP) e conns = append(conns, conn) } else { for _, ip := range ips { - conn, err := newMcuProxyConnection(m, url, ip) + conn, err := newMcuProxyConnection(m.log, m, url, ip) if err != nil { if ignoreErrors { - log.Printf("Could not create proxy connection to %s (%s): %s", url, ip, err) + m.log.Error("Could not create proxy connection", + zap.String("url", url), + zap.Stringer("ip", ip), + zap.Error(err), + ) continue } @@ -1492,7 +1634,9 @@ func (m *mcuProxy) AddConnection(ignoreErrors bool, url string, ips ...net.IP) e } for _, conn := range conns { - log.Printf("Adding new connection to %s", conn) + m.log.Info("Adding new connection", + zap.Stringer("conn", conn), + ) conn.start() m.connections = append(m.connections, conn) @@ -1544,7 +1688,9 @@ func (m *mcuProxy) iterateConnections(url string, ips []net.IP, f func(conn *mcu func (m *mcuProxy) RemoveConnection(url string, ips ...net.IP) { m.iterateConnections(url, ips, func(conn *mcuProxyConnection) { - log.Printf("Removing connection to %s", conn) + m.log.Info("Removing connection", + zap.Stringer("conn", conn), + ) conn.closeIfEmpty() }) } @@ -1561,22 +1707,30 @@ func (m *mcuProxy) Reload(config *goconf.ConfigFile) { if maxStreamBitrate <= 0 { maxStreamBitrate = defaultMaxStreamBitrate } - log.Printf("Maximum bandwidth %d bits/sec per publishing stream", m.maxStreamBitrate.Load()) + m.log.Info("Maximum bandwidth per publishing stream", + zap.Int32("bitrate", m.maxStreamBitrate.Load()), + ) m.maxStreamBitrate.Store(int32(maxStreamBitrate)) maxScreenBitrate, _ := config.GetInt("mcu", "maxscreenbitrate") if maxScreenBitrate <= 0 { maxScreenBitrate = defaultMaxScreenBitrate } - log.Printf("Maximum bandwidth %d bits/sec per screensharing stream", m.maxScreenBitrate.Load()) + m.log.Info("Maximum bandwidth per screensharing stream", + zap.Int32("bitrate", m.maxScreenBitrate.Load()), + ) m.maxScreenBitrate.Store(int32(maxScreenBitrate)) if err := m.loadContinentsMap(config); err != nil { - log.Printf("Error loading continents map: %s", err) + m.log.Error("Error loading continents map", + zap.Error(err), + ) } if err := m.config.Reload(config); err != nil { - log.Printf("could not reload proxy configuration: %s", err) + m.log.Error("Could not reload proxy configuration", + zap.Error(err), + ) } } @@ -1786,7 +1940,12 @@ func (m *mcuProxy) createPublisher(ctx context.Context, listener McuListener, id publisher, err := conn.newPublisher(subctx, listener, id, sid, streamType, bitrate, mediaTypes) if err != nil { - log.Printf("Could not create %s publisher for %s on %s: %s", streamType, id, conn, err) + m.log.Error("Could not create publisher", + zap.Any("streamtype", streamType), + zap.String("id", id), + zap.Stringer("conn", conn), + zap.Error(err), + ) continue } @@ -1909,7 +2068,12 @@ func (m *mcuProxy) createSubscriber(ctx context.Context, listener McuListener, i subscriber, err = conn.newRemoteSubscriber(ctx, listener, id, publisher, streamType, publisherConn) } if err != nil { - log.Printf("Could not create subscriber for %s publisher %s on %s: %s", streamType, publisher, conn, err) + m.log.Error("Could not create subscriber", + zap.Any("streamtype", streamType), + zap.String("publisherid", publisher), + zap.Stringer("conn", conn), + zap.Error(err), + ) continue } @@ -1935,7 +2099,10 @@ func (m *mcuProxy) NewSubscriber(ctx context.Context, listener McuListener, publ conn: conn, } } else { - log.Printf("No %s publisher %s found yet, deferring", streamType, publisher) + m.log.Info("No publisher found yet, deferring", + zap.Any("streamtype", streamType), + zap.String("publisherid", publisher), + ) ch := make(chan *proxyPublisherInfo, 1) getctx, cancel := context.WithCancel(ctx) defer cancel() @@ -1976,7 +2143,12 @@ func (m *mcuProxy) NewSubscriber(ctx context.Context, listener McuListener, publ if errors.Is(err, context.Canceled) { return } else if err != nil { - log.Printf("Error getting %s publisher id %s from %s: %s", streamType, publisher, client.Target(), err) + m.log.Error("Error getting publisher id", + zap.Any("streamtype", streamType), + zap.String("publisherid", publisher), + zap.String("target", client.Target()), + zap.Error(err), + ) return } else if id == "" { // Publisher not found on other server @@ -1984,7 +2156,11 @@ func (m *mcuProxy) NewSubscriber(ctx context.Context, listener McuListener, publ } cancel() // Cancel pending RPC calls. - log.Printf("Found publisher id %s through %s on proxy %s", id, client.Target(), url) + m.log.Info("Found publisher id on proxy", + zap.String("publisherid", id), + zap.String("target", client.Target()), + zap.String("url", url), + ) m.connectionsMu.RLock() connections := m.connections @@ -2001,16 +2177,24 @@ func (m *mcuProxy) NewSubscriber(ctx context.Context, listener McuListener, publ } if publisherConn == nil { - publisherConn, err = newMcuProxyConnection(m, url, ip) + publisherConn, err = newMcuProxyConnection(m.log, m, url, ip) if err != nil { - log.Printf("Could not create temporary connection to %s for %s publisher %s: %s", url, streamType, publisher, err) + m.log.Error("Could not create temporary connection", + zap.String("url", url), + zap.Any("streamtype", streamType), + zap.String("publisherid", publisher), + zap.Error(err), + ) return } publisherConn.setTemporary() publisherConn.start() if err := publisherConn.waitUntilConnected(ctx); err != nil { - log.Printf("Could not establish new connection to %s: %s", publisherConn, err) + m.log.Error("Could not establish new connection", + zap.Stringer("publisherconn", publisherConn), + zap.Error(err), + ) publisherConn.closeIfEmpty() return } @@ -2111,7 +2295,12 @@ func (m *mcuProxy) NewSubscriber(ctx context.Context, listener McuListener, publ if publisherInfo.conn.IsTemporary() { publisherInfo.conn.closeIfEmpty() } - log.Printf("Could not create subscriber for %s publisher %s on %s: %s", streamType, publisher, publisherInfo.conn, err) + m.log.Error("Could not create subscriber", + zap.Any("streamtype", streamType), + zap.String("publisherid", publisher), + zap.Stringer("publisherconn", publisherInfo.conn), + zap.Error(err), + ) return nil, err } diff --git a/mcu_proxy_test.go b/mcu_proxy_test.go index cfdb865f..8374716c 100644 --- a/mcu_proxy_test.go +++ b/mcu_proxy_test.go @@ -691,13 +691,14 @@ func newMcuProxyForTestWithOptions(t *testing.T, options proxyTestOptions) *mcuP etcdConfig.AddOption("etcd", "endpoints", options.etcd.Config().ListenClientUrls[0].String()) etcdConfig.AddOption("etcd", "loglevel", "error") - etcdClient, err := NewEtcdClient(etcdConfig, "") + log := GetLoggerForTest(t) + etcdClient, err := NewEtcdClient(log, etcdConfig, "") require.NoError(err) t.Cleanup(func() { assert.NoError(t, etcdClient.Close()) }) - mcu, err := NewMcuProxy(cfg, etcdClient, grpcClients, dnsMonitor) + mcu, err := NewMcuProxy(log, cfg, etcdClient, grpcClients, dnsMonitor) require.NoError(err) t.Cleanup(func() { mcu.Stop() @@ -749,7 +750,6 @@ func newMcuProxyForTest(t *testing.T) *mcuProxy { } func Test_ProxyPublisherSubscriber(t *testing.T) { - CatchLogForTest(t) t.Parallel() mcu := newMcuProxyForTest(t) @@ -783,7 +783,6 @@ func Test_ProxyPublisherSubscriber(t *testing.T) { } func Test_ProxyWaitForPublisher(t *testing.T) { - CatchLogForTest(t) t.Parallel() mcu := newMcuProxyForTest(t) @@ -831,7 +830,6 @@ func Test_ProxyWaitForPublisher(t *testing.T) { } func Test_ProxyPublisherBandwidth(t *testing.T) { - CatchLogForTest(t) t.Parallel() server1 := NewProxyServerForTest(t, "DE") server2 := NewProxyServerForTest(t, "DE") @@ -897,7 +895,6 @@ func Test_ProxyPublisherBandwidth(t *testing.T) { } func Test_ProxyPublisherBandwidthOverload(t *testing.T) { - CatchLogForTest(t) t.Parallel() server1 := NewProxyServerForTest(t, "DE") server2 := NewProxyServerForTest(t, "DE") @@ -966,7 +963,6 @@ func Test_ProxyPublisherBandwidthOverload(t *testing.T) { } func Test_ProxyPublisherLoad(t *testing.T) { - CatchLogForTest(t) t.Parallel() server1 := NewProxyServerForTest(t, "DE") server2 := NewProxyServerForTest(t, "DE") @@ -1012,7 +1008,6 @@ func Test_ProxyPublisherLoad(t *testing.T) { } func Test_ProxyPublisherCountry(t *testing.T) { - CatchLogForTest(t) t.Parallel() serverDE := NewProxyServerForTest(t, "DE") serverUS := NewProxyServerForTest(t, "US") @@ -1056,7 +1051,6 @@ func Test_ProxyPublisherCountry(t *testing.T) { } func Test_ProxyPublisherContinent(t *testing.T) { - CatchLogForTest(t) t.Parallel() serverDE := NewProxyServerForTest(t, "DE") serverUS := NewProxyServerForTest(t, "US") @@ -1100,7 +1094,6 @@ func Test_ProxyPublisherContinent(t *testing.T) { } func Test_ProxySubscriberCountry(t *testing.T) { - CatchLogForTest(t) t.Parallel() serverDE := NewProxyServerForTest(t, "DE") serverUS := NewProxyServerForTest(t, "US") @@ -1142,7 +1135,6 @@ func Test_ProxySubscriberCountry(t *testing.T) { } func Test_ProxySubscriberContinent(t *testing.T) { - CatchLogForTest(t) t.Parallel() serverDE := NewProxyServerForTest(t, "DE") serverUS := NewProxyServerForTest(t, "US") @@ -1184,7 +1176,6 @@ func Test_ProxySubscriberContinent(t *testing.T) { } func Test_ProxySubscriberBandwidth(t *testing.T) { - CatchLogForTest(t) t.Parallel() serverDE := NewProxyServerForTest(t, "DE") serverUS := NewProxyServerForTest(t, "US") @@ -1246,7 +1237,6 @@ func Test_ProxySubscriberBandwidth(t *testing.T) { } func Test_ProxySubscriberBandwidthOverload(t *testing.T) { - CatchLogForTest(t) t.Parallel() serverDE := NewProxyServerForTest(t, "DE") serverUS := NewProxyServerForTest(t, "US") @@ -1347,7 +1337,6 @@ func (h *mockGrpcServerHub) GetBackend(u *url.URL) *Backend { } func Test_ProxyRemotePublisher(t *testing.T) { - CatchLogForTest(t) t.Parallel() etcd := NewEtcdForTest(t) @@ -1423,7 +1412,6 @@ func Test_ProxyRemotePublisher(t *testing.T) { } func Test_ProxyRemotePublisherWait(t *testing.T) { - CatchLogForTest(t) t.Parallel() etcd := NewEtcdForTest(t) @@ -1515,7 +1503,6 @@ func Test_ProxyRemotePublisherWait(t *testing.T) { } func Test_ProxyRemotePublisherTemporary(t *testing.T) { - CatchLogForTest(t) t.Parallel() etcd := NewEtcdForTest(t) diff --git a/mcu_test.go b/mcu_test.go index ae1de237..39ac021d 100644 --- a/mcu_test.go +++ b/mcu_test.go @@ -25,11 +25,12 @@ import ( "context" "errors" "fmt" - "log" "sync" "sync/atomic" + "testing" "github.com/dlintw/goconf" + "go.uber.org/zap" ) const ( @@ -38,13 +39,17 @@ const ( ) type TestMCU struct { - mu sync.Mutex + t *testing.T + mu sync.Mutex + publishers map[string]*TestMCUPublisher subscribers map[string]*TestMCUSubscriber } -func NewTestMCU() (*TestMCU, error) { +func NewTestMCU(t *testing.T) (*TestMCU, error) { return &TestMCU{ + t: t, + publishers: make(map[string]*TestMCUPublisher), subscribers: make(map[string]*TestMCUSubscriber), }, nil @@ -84,6 +89,7 @@ func (m *TestMCU) NewPublisher(ctx context.Context, listener McuListener, id str } pub := &TestMCUPublisher{ TestMCUClient: TestMCUClient{ + t: m.t, id: id, sid: sid, streamType: streamType, @@ -130,6 +136,7 @@ func (m *TestMCU) NewSubscriber(ctx context.Context, listener McuListener, publi id := newRandomString(8) sub := &TestMCUSubscriber{ TestMCUClient: TestMCUClient{ + t: m.t, id: id, streamType: streamType, }, @@ -140,6 +147,7 @@ func (m *TestMCU) NewSubscriber(ctx context.Context, listener McuListener, publi } type TestMCUClient struct { + t *testing.T closed atomic.Bool id string @@ -165,7 +173,10 @@ func (c *TestMCUClient) MaxBitrate() int { func (c *TestMCUClient) Close(ctx context.Context) { if c.closed.CompareAndSwap(false, true) { - log.Printf("Close MCU client %s", c.id) + log := GetLoggerForTest(c.t) + log.Info("Close MCU client", + zap.String("id", c.id), + ) } } diff --git a/natsclient.go b/natsclient.go index 3c2f6d15..6544dec4 100644 --- a/natsclient.go +++ b/natsclient.go @@ -26,13 +26,13 @@ import ( "encoding/base64" "encoding/json" "fmt" - "log" "os" "os/signal" "strings" "time" "github.com/nats-io/nats.go" + "go.uber.org/zap" ) const ( @@ -63,17 +63,20 @@ func GetEncodedSubject(prefix string, suffix string) string { } type natsClient struct { + log *zap.Logger conn *nats.Conn } -func NewNatsClient(url string) (NatsClient, error) { +func NewNatsClient(log *zap.Logger, url string) (NatsClient, error) { if url == ":loopback:" { - log.Printf("WARNING: events url %s is deprecated, please use %s instead", url, NatsLoopbackUrl) + log.Warn(fmt.Sprintf("events url is deprecated, please use %s instead", NatsLoopbackUrl), + zap.String("url", url), + ) url = NatsLoopbackUrl } if url == NatsLoopbackUrl { - log.Println("Using internal NATS loopback client") - return NewLoopbackNatsClient() + log.Info("Using internal NATS loopback client") + return NewLoopbackNatsClient(log) } backoff, err := NewExponentialBackoff(initialConnectInterval, maxConnectInterval) @@ -81,7 +84,13 @@ func NewNatsClient(url string) (NatsClient, error) { return nil, err } - client := &natsClient{} + log = log.With( + zap.String("url", url), + ) + + client := &natsClient{ + log: log, + } client.conn, err = nats.Connect(url, nats.ClosedHandler(client.onClosed), @@ -93,7 +102,10 @@ func NewNatsClient(url string) (NatsClient, error) { // The initial connect must succeed, so we retry in the case of an error. for err != nil { - log.Printf("Could not create connection (%s), will retry in %s", err, backoff.NextWait()) + log.Error("Could not create connection, will retry", + zap.Duration("wait", backoff.NextWait()), + zap.Error(err), + ) backoff.Wait(ctx) if ctx.Err() != nil { return nil, fmt.Errorf("interrupted") @@ -101,7 +113,10 @@ func NewNatsClient(url string) (NatsClient, error) { client.conn, err = nats.Connect(url) } - log.Printf("Connection established to %s (%s)", client.conn.ConnectedUrl(), client.conn.ConnectedServerId()) + log.Info("Connection established", + zap.String("connectedurl", client.conn.ConnectedUrl()), + zap.String("serverid", client.conn.ConnectedServerId()), + ) return client, nil } @@ -110,15 +125,20 @@ func (c *natsClient) Close() { } func (c *natsClient) onClosed(conn *nats.Conn) { - log.Println("NATS client closed", conn.LastError()) + c.log.Info("NATS client closed", + zap.Error(conn.LastError()), + ) } func (c *natsClient) onDisconnected(conn *nats.Conn) { - log.Println("NATS client disconnected") + c.log.Info("NATS client disconnected") } func (c *natsClient) onReconnected(conn *nats.Conn) { - log.Printf("NATS client reconnected to %s (%s)", conn.ConnectedUrl(), conn.ConnectedServerId()) + c.log.Info("NATS client reconnected", + zap.String("connectedurl", conn.ConnectedUrl()), + zap.String("serverid", conn.ConnectedServerId()), + ) } func (c *natsClient) Subscribe(subject string, ch chan *nats.Msg) (NatsSubscription, error) { diff --git a/natsclient_loopback.go b/natsclient_loopback.go index 56b6fb69..d89eef70 100644 --- a/natsclient_loopback.go +++ b/natsclient_loopback.go @@ -24,14 +24,15 @@ package signaling import ( "container/list" "encoding/json" - "log" "strings" "sync" "github.com/nats-io/nats.go" + "go.uber.org/zap" ) type LoopbackNatsClient struct { + log *zap.Logger mu sync.Mutex subscriptions map[string]map[*loopbackNatsSubscription]bool @@ -39,8 +40,9 @@ type LoopbackNatsClient struct { incoming list.List } -func NewLoopbackNatsClient() (NatsClient, error) { +func NewLoopbackNatsClient(log *zap.Logger) (NatsClient, error) { client := &LoopbackNatsClient{ + log: log, subscriptions: make(map[string]map[*loopbackNatsSubscription]bool), } client.wakeup.L = &client.mu @@ -81,7 +83,9 @@ func (c *LoopbackNatsClient) processMessage(msg *nats.Msg) { select { case ch <- msg: default: - log.Printf("Slow consumer %s, dropping message", msg.Subject) + c.log.Warn("Slow consumer, dropping message", + zap.String("subject", msg.Subject), + ) } } } diff --git a/natsclient_loopback_test.go b/natsclient_loopback_test.go index d6cf5dea..33165834 100644 --- a/natsclient_loopback_test.go +++ b/natsclient_loopback_test.go @@ -52,7 +52,8 @@ func (c *LoopbackNatsClient) waitForSubscriptionsEmpty(ctx context.Context, t *t } func CreateLoopbackNatsClientForTest(t *testing.T) NatsClient { - result, err := NewLoopbackNatsClient() + log := GetLoggerForTest(t) + result, err := NewLoopbackNatsClient(log) require.NoError(t, err) t.Cleanup(func() { result.Close() diff --git a/natsclient_test.go b/natsclient_test.go index a28a0c75..8cbe852b 100644 --- a/natsclient_test.go +++ b/natsclient_test.go @@ -46,8 +46,9 @@ func startLocalNatsServer(t *testing.T) string { } func CreateLocalNatsClientForTest(t *testing.T) NatsClient { + log := GetLoggerForTest(t) url := startLocalNatsServer(t) - result, err := NewNatsClient(url) + result, err := NewNatsClient(log, url) require.NoError(t, err) t.Cleanup(func() { result.Close() @@ -98,7 +99,6 @@ func testNatsClient_Subscribe(t *testing.T, client NatsClient) { } func TestNatsClient_Subscribe(t *testing.T) { - CatchLogForTest(t) ensureNoGoroutinesLeak(t, func(t *testing.T) { client := CreateLocalNatsClientForTest(t) @@ -113,7 +113,6 @@ func testNatsClient_PublishAfterClose(t *testing.T, client NatsClient) { } func TestNatsClient_PublishAfterClose(t *testing.T) { - CatchLogForTest(t) ensureNoGoroutinesLeak(t, func(t *testing.T) { client := CreateLocalNatsClientForTest(t) @@ -130,7 +129,6 @@ func testNatsClient_SubscribeAfterClose(t *testing.T, client NatsClient) { } func TestNatsClient_SubscribeAfterClose(t *testing.T) { - CatchLogForTest(t) ensureNoGoroutinesLeak(t, func(t *testing.T) { client := CreateLocalNatsClientForTest(t) @@ -153,7 +151,6 @@ func testNatsClient_BadSubjects(t *testing.T, client NatsClient) { } func TestNatsClient_BadSubjects(t *testing.T) { - CatchLogForTest(t) ensureNoGoroutinesLeak(t, func(t *testing.T) { client := CreateLocalNatsClientForTest(t) diff --git a/proxy/proxy_client.go b/proxy/proxy_client.go index cee7328f..74d853e5 100644 --- a/proxy/proxy_client.go +++ b/proxy/proxy_client.go @@ -26,6 +26,8 @@ import ( "time" "github.com/gorilla/websocket" + "go.uber.org/zap" + signaling "github.com/strukturag/nextcloud-spreed-signaling" ) @@ -37,11 +39,11 @@ type ProxyClient struct { session atomic.Pointer[ProxySession] } -func NewProxyClient(proxy *ProxyServer, conn *websocket.Conn, addr string) (*ProxyClient, error) { +func NewProxyClient(log *zap.Logger, proxy *ProxyServer, conn *websocket.Conn, addr string) (*ProxyClient, error) { client := &ProxyClient{ proxy: proxy, } - client.SetConn(conn, addr, client) + client.SetConn(log, conn, addr, client) return client, nil } diff --git a/proxy/proxy_server.go b/proxy/proxy_server.go index ccf0be1b..d8d173f5 100644 --- a/proxy/proxy_server.go +++ b/proxy/proxy_server.go @@ -432,7 +432,7 @@ func (s *ProxyServer) Start(config *goconf.ConfigFile) error { for { switch mcuType { case signaling.McuTypeJanus: - mcu, err = signaling.NewMcuJanus(ctx, s.url, config) + mcu, err = signaling.NewMcuJanus(ctx, s.log, s.url, config) if err == nil { signaling.RegisterJanusMcuStats() } @@ -691,7 +691,7 @@ func (s *ProxyServer) proxyHandler(w http.ResponseWriter, r *http.Request) { return } - client, err := NewProxyClient(s, conn, addr) + client, err := NewProxyClient(s.log, s, conn, addr) if err != nil { log.Error("Could not create client", zap.String("addr", addr), diff --git a/proxy/proxy_tokens_etcd.go b/proxy/proxy_tokens_etcd.go index ad9b8afa..44562bbf 100644 --- a/proxy/proxy_tokens_etcd.go +++ b/proxy/proxy_tokens_etcd.go @@ -54,7 +54,7 @@ type tokensEtcd struct { } func NewProxyTokensEtcd(log *zap.Logger, config *goconf.ConfigFile) (ProxyTokens, error) { - client, err := signaling.NewEtcdClient(config, "tokens") + client, err := signaling.NewEtcdClient(log, config, "tokens") if err != nil { return nil, err } diff --git a/proxy_config_etcd.go b/proxy_config_etcd.go index 35ccade0..ab838016 100644 --- a/proxy_config_etcd.go +++ b/proxy_config_etcd.go @@ -25,15 +25,16 @@ import ( "context" "encoding/json" "errors" - "log" "sync" "time" "github.com/dlintw/goconf" clientv3 "go.etcd.io/etcd/client/v3" + "go.uber.org/zap" ) type proxyConfigEtcd struct { + log *zap.Logger mu sync.Mutex proxy McuProxy @@ -46,7 +47,7 @@ type proxyConfigEtcd struct { closeFunc context.CancelFunc } -func NewProxyConfigEtcd(config *goconf.ConfigFile, etcdClient *EtcdClient, proxy McuProxy) (ProxyConfig, error) { +func NewProxyConfigEtcd(log *zap.Logger, config *goconf.ConfigFile, etcdClient *EtcdClient, proxy McuProxy) (ProxyConfig, error) { if !etcdClient.IsConfigured() { return nil, errors.New("No etcd endpoints configured") } @@ -54,6 +55,7 @@ func NewProxyConfigEtcd(config *goconf.ConfigFile, etcdClient *EtcdClient, proxy closeCtx, closeFunc := context.WithCancel(context.Background()) result := &proxyConfigEtcd{ + log: log, proxy: proxy, client: etcdClient, @@ -116,9 +118,14 @@ func (p *proxyConfigEtcd) EtcdClientCreated(client *EtcdClient) { if errors.Is(err, context.Canceled) { return } else if errors.Is(err, context.DeadlineExceeded) { - log.Printf("Timeout getting initial list of proxy URLs, retry in %s", backoff.NextWait()) + p.log.Error("Timeout getting initial list of proxy URLs, retry", + zap.Duration("wait", backoff.NextWait()), + ) } else { - log.Printf("Could not get initial list of proxy URLs, retry in %s: %s", backoff.NextWait(), err) + p.log.Error("Could not get initial list of proxy URLs, retry", + zap.Duration("wait", backoff.NextWait()), + zap.Error(err), + ) } backoff.Wait(p.closeCtx) @@ -137,7 +144,11 @@ func (p *proxyConfigEtcd) EtcdClientCreated(client *EtcdClient) { for p.closeCtx.Err() == nil { var err error if nextRevision, err = client.Watch(p.closeCtx, p.keyPrefix, nextRevision, p, clientv3.WithPrefix()); err != nil { - log.Printf("Error processing watch for %s (%s), retry in %s", p.keyPrefix, err, backoff.NextWait()) + p.log.Error("Error processing watch, retry", + zap.String("prefix", p.keyPrefix), + zap.Duration("wait", backoff.NextWait()), + zap.Error(err), + ) backoff.Wait(p.closeCtx) continue } @@ -146,7 +157,10 @@ func (p *proxyConfigEtcd) EtcdClientCreated(client *EtcdClient) { backoff.Reset() prevRevision = nextRevision } else { - log.Printf("Processing watch for %s interrupted, retry in %s", p.keyPrefix, backoff.NextWait()) + p.log.Warn("Processing watch interrupted, retry", + zap.String("prefix", p.keyPrefix), + zap.Duration("wait", backoff.NextWait()), + ) backoff.Wait(p.closeCtx) } } @@ -166,11 +180,17 @@ func (p *proxyConfigEtcd) getProxyUrls(ctx context.Context, client *EtcdClient, func (p *proxyConfigEtcd) EtcdKeyUpdated(client *EtcdClient, key string, data []byte, prevValue []byte) { var info ProxyInformationEtcd if err := json.Unmarshal(data, &info); err != nil { - log.Printf("Could not decode proxy information %s: %s", string(data), err) + p.log.Error("Could not decode proxy information", + zap.ByteString("data", data), + zap.Error(err), + ) return } if err := info.CheckValid(); err != nil { - log.Printf("Received invalid proxy information %s: %s", string(data), err) + p.log.Error("Received invalid proxy information", + zap.ByteString("data", data), + zap.Error(err), + ) return } @@ -185,7 +205,11 @@ func (p *proxyConfigEtcd) EtcdKeyUpdated(client *EtcdClient, key string, data [] } if otherKey, otherFound := p.urlToKey[info.Address]; otherFound && otherKey != key { - log.Printf("Address %s is already registered for key %s, ignoring %s", info.Address, otherKey, key) + p.log.Warn("Address is already registered, ignoring", + zap.String("url", info.Address), + zap.String("key", key), + zap.String("other", otherKey), + ) return } @@ -194,11 +218,17 @@ func (p *proxyConfigEtcd) EtcdKeyUpdated(client *EtcdClient, key string, data [] p.proxy.KeepConnection(info.Address) } else { if err := p.proxy.AddConnection(false, info.Address); err != nil { - log.Printf("Could not create proxy connection to %s: %s", info.Address, err) + p.log.Error("Could not create proxy connection", + zap.String("url", info.Address), + zap.Error(err), + ) return } - log.Printf("Added new connection to %s (from %s)", info.Address, key) + p.log.Info("Added new proxy connection", + zap.String("url", info.Address), + zap.String("key", key), + ) p.keyInfos[key] = &info p.urlToKey[info.Address] = key } @@ -220,6 +250,9 @@ func (p *proxyConfigEtcd) removeEtcdProxyLocked(key string) { delete(p.keyInfos, key) delete(p.urlToKey, info.Address) - log.Printf("Removing connection to %s (from %s)", info.Address, key) + p.log.Info("Removing proxy connection", + zap.String("target", info.Address), + zap.String("key", key), + ) p.proxy.RemoveConnection(info.Address) } diff --git a/proxy_config_etcd_test.go b/proxy_config_etcd_test.go index 353f690e..082dba2f 100644 --- a/proxy_config_etcd_test.go +++ b/proxy_config_etcd_test.go @@ -40,10 +40,11 @@ type TestProxyInformationEtcd struct { func newProxyConfigEtcd(t *testing.T, proxy McuProxy) (*embed.Etcd, ProxyConfig) { t.Helper() + log := GetLoggerForTest(t) etcd, client := NewEtcdClientForTest(t) cfg := goconf.NewConfigFile() cfg.AddOption("mcu", "keyprefix", "proxies/") - p, err := NewProxyConfigEtcd(cfg, client, proxy) + p, err := NewProxyConfigEtcd(log, cfg, client, proxy) require.NoError(t, err) t.Cleanup(func() { p.Stop() @@ -60,7 +61,6 @@ func SetEtcdProxy(t *testing.T, etcd *embed.Etcd, path string, proxy *TestProxyI func TestProxyConfigEtcd(t *testing.T) { t.Parallel() - CatchLogForTest(t) proxy := newMcuProxyForConfig(t) etcd, config := newProxyConfigEtcd(t, proxy) diff --git a/proxy_config_static.go b/proxy_config_static.go index eda67d78..e838509d 100644 --- a/proxy_config_static.go +++ b/proxy_config_static.go @@ -23,13 +23,13 @@ package signaling import ( "errors" - "log" "net" "net/url" "strings" "sync" "github.com/dlintw/goconf" + "go.uber.org/zap" ) type ipList struct { @@ -40,6 +40,7 @@ type ipList struct { } type proxyConfigStatic struct { + log *zap.Logger mu sync.Mutex proxy McuProxy @@ -49,8 +50,9 @@ type proxyConfigStatic struct { connectionsMap map[string]*ipList } -func NewProxyConfigStatic(config *goconf.ConfigFile, proxy McuProxy, dnsMonitor *DnsMonitor) (ProxyConfig, error) { +func NewProxyConfigStatic(log *zap.Logger, config *goconf.ConfigFile, proxy McuProxy, dnsMonitor *DnsMonitor) (ProxyConfig, error) { result := &proxyConfigStatic{ + log: log, proxy: proxy, dnsMonitor: dnsMonitor, connectionsMap: make(map[string]*ipList), @@ -106,7 +108,10 @@ func (p *proxyConfigStatic) configure(config *goconf.ConfigFile, fromReload bool return err } - log.Printf("Could not parse URL %s: %s", u, err) + p.log.Error("Could not parse URL", + zap.String("url", u), + zap.Error(err), + ) continue } @@ -127,7 +132,10 @@ func (p *proxyConfigStatic) configure(config *goconf.ConfigFile, fromReload bool return err } - log.Printf("Could not create proxy connection to %s: %s", u, err) + p.log.Error("Could not create proxy connection", + zap.String("url", u), + zap.Error(err), + ) continue } } @@ -204,7 +212,11 @@ func (p *proxyConfigStatic) onLookup(entry *DnsMonitorEntry, all []net.IP, added if len(added) > 0 { if err := p.proxy.AddConnection(true, u, added...); err != nil { - log.Printf("Could not add proxy connection to %s with %+v: %s", u, added, err) + p.log.Error("Could not add proxy connection", + zap.String("url", u), + zap.Stringers("ips", added), + zap.Error(err), + ) } } diff --git a/proxy_config_static_test.go b/proxy_config_static_test.go index 70884e8e..44f67ddb 100644 --- a/proxy_config_static_test.go +++ b/proxy_config_static_test.go @@ -38,7 +38,8 @@ func newProxyConfigStatic(t *testing.T, proxy McuProxy, dns bool, urls ...string cfg.AddOption("mcu", "dnsdiscovery", "true") } dnsMonitor := newDnsMonitorForTest(t, time.Hour) // will be updated manually - p, err := NewProxyConfigStatic(cfg, proxy, dnsMonitor) + log := GetLoggerForTest(t) + p, err := NewProxyConfigStatic(log, cfg, proxy, dnsMonitor) require.NoError(t, err) t.Cleanup(func() { p.Stop() @@ -56,7 +57,6 @@ func updateProxyConfigStatic(t *testing.T, config ProxyConfig, dns bool, urls .. } func TestProxyConfigStaticSimple(t *testing.T) { - CatchLogForTest(t) proxy := newMcuProxyForConfig(t) config, _ := newProxyConfigStatic(t, proxy, false, "https://foo/") proxy.Expect("add", "https://foo/") @@ -73,7 +73,6 @@ func TestProxyConfigStaticSimple(t *testing.T) { } func TestProxyConfigStaticDNS(t *testing.T) { - CatchLogForTest(t) lookup := newMockDnsLookupForTest(t) proxy := newMcuProxyForConfig(t) config, dnsMonitor := newProxyConfigStatic(t, proxy, true, "https://foo/") diff --git a/remotesession.go b/remotesession.go index 85c271f2..eb4b8b51 100644 --- a/remotesession.go +++ b/remotesession.go @@ -26,12 +26,14 @@ import ( "encoding/json" "errors" "fmt" - "log" "sync/atomic" "time" + + "go.uber.org/zap" ) type RemoteSession struct { + log *zap.Logger hub *Hub client *Client remoteClient *GrpcClient @@ -40,8 +42,12 @@ type RemoteSession struct { proxy atomic.Pointer[SessionProxy] } -func NewRemoteSession(hub *Hub, client *Client, remoteClient *GrpcClient, sessionId string) (*RemoteSession, error) { +func NewRemoteSession(log *zap.Logger, hub *Hub, client *Client, remoteClient *GrpcClient, sessionId string) (*RemoteSession, error) { remoteSession := &RemoteSession{ + log: log.With( + zap.String("sessionid", sessionId), + zap.String("target", remoteClient.Target()), + ), hub: hub, client: client, remoteClient: remoteClient, @@ -97,7 +103,9 @@ func (s *RemoteSession) OnProxyMessage(msg *ServerSessionMessage) error { func (s *RemoteSession) OnProxyClose(err error) { if err != nil { - log.Printf("Proxy connection for session %s to %s was closed with error: %s", s.sessionId, s.remoteClient.Target(), err) + s.log.Error("Proxy connection for session was closed with error", + zap.Error(err), + ) } s.Close() } @@ -145,7 +153,10 @@ func (s *RemoteSession) OnClosed(client HandlerClient) { func (s *RemoteSession) OnMessageReceived(client HandlerClient, message []byte) { if err := s.sendProxyMessage(message); err != nil { - log.Printf("Error sending %s to the proxy for session %s: %s", string(message), s.sessionId, err) + s.log.Error("Error sending message to the remote session proxy", + zap.ByteString("message", message), + zap.Error(err), + ) s.Close() } } diff --git a/room.go b/room.go index 19a9ff50..ecd34fbc 100644 --- a/room.go +++ b/room.go @@ -26,13 +26,13 @@ import ( "context" "encoding/json" "fmt" - "log" "net/url" "strconv" "sync" "time" "github.com/prometheus/client_golang/prometheus" + "go.uber.org/zap" ) const ( @@ -60,6 +60,7 @@ func init() { } type Room struct { + log *zap.Logger id string hub *Hub events AsyncEvents @@ -95,8 +96,11 @@ func getRoomIdForBackend(id string, backend *Backend) string { return backend.Id() + "|" + id } -func NewRoom(roomId string, properties json.RawMessage, hub *Hub, events AsyncEvents, backend *Backend) (*Room, error) { +func NewRoom(log *zap.Logger, roomId string, properties json.RawMessage, hub *Hub, events AsyncEvents, backend *Backend) (*Room, error) { room := &Room{ + log: log.With( + zap.String("roomid", roomId), + ), id: roomId, hub: hub, events: events, @@ -213,7 +217,10 @@ func (r *Room) ProcessBackendRoomRequest(message *AsyncMessage) { case "asyncroom": r.processBackendRoomRequestAsyncRoom(message.AsyncRoom) default: - log.Printf("Unsupported backend room request with type %s in %s: %+v", message.Type, r.id, message) + r.log.Warn("Unsupported backend room request", + zap.String("type", message.Type), + zap.Any("message", message), + ) } } @@ -221,9 +228,13 @@ func (r *Room) processBackendRoomRequestRoom(message *BackendServerRoomRequest) received := message.ReceivedTime if last, found := r.lastRoomRequests[message.Type]; found && last > received { if msg, err := json.Marshal(message); err == nil { - log.Printf("Ignore old backend room request for %s: %s", r.Id(), string(msg)) + r.log.Debug("Ignore old backend room request", + zap.ByteString("message", msg), + ) } else { - log.Printf("Ignore old backend room request for %s: %+v", r.Id(), message) + r.log.Debug("Ignore old backend room request", + zap.Any("message", message), + ) } return } @@ -251,10 +262,15 @@ func (r *Room) processBackendRoomRequestRoom(message *BackendServerRoomRequest) case TransientActionDelete: r.RemoveTransientData(message.Transient.Key) default: - log.Printf("Unsupported transient action in room %s: %+v", r.Id(), message.Transient) + r.log.Warn("Unsupported transient action in room", + zap.Any("message", message.Transient), + ) } default: - log.Printf("Unsupported backend room request with type %s in %s: %+v", message.Type, r.Id(), message) + r.log.Warn("Unsupported backend room request", + zap.String("type", message.Type), + zap.Any("message", message), + ) } } @@ -266,7 +282,10 @@ func (r *Room) processBackendRoomRequestAsyncRoom(message *AsyncRoomMessage) { r.publishUsersChangedWithInternal() } default: - log.Printf("Unsupported async room request with type %s in %s: %+v", message.Type, r.Id(), message) + r.log.Warn("Unsupported async room request", + zap.String("type", message.Type), + zap.Any("message", message), + ) } } @@ -275,7 +294,10 @@ func (r *Room) AddSession(session Session, sessionData json.RawMessage) { if len(sessionData) > 0 { roomSessionData = &RoomSessionData{} if err := json.Unmarshal(sessionData, roomSessionData); err != nil { - log.Printf("Error decoding room session data \"%s\": %s", string(sessionData), err) + r.log.Error("Error decoding room session data", + zap.ByteString("data", sessionData), + zap.Error(err), + ) roomSessionData = nil } } @@ -303,7 +325,10 @@ func (r *Room) AddSession(session Session, sessionData json.RawMessage) { } if roomSessionData != nil { r.roomSessionData[sid] = roomSessionData - log.Printf("Session %s sent room session data %+v", session.PublicId(), roomSessionData) + r.log.Info("Session sent room session data", + zap.String("sessionid", session.PublicId()), + zap.Any("sessiondata", roomSessionData), + ) } r.mu.Unlock() if !found { @@ -328,7 +353,10 @@ func (r *Room) AddSession(session Session, sessionData json.RawMessage) { ClientType: session.ClientType(), }, }); err != nil { - log.Printf("Error publishing joined event for session %s: %s", sid, err) + r.log.Error("Error publishing joined event for session", + zap.String("sessionid", sid), + zap.Error(err), + ) } } @@ -385,7 +413,10 @@ func (r *Room) notifySessionJoined(sessionId string) { Type: "message", Message: msg, }); err != nil { - log.Printf("Error publishing joined events to session %s: %s", sessionId, err) + r.log.Error("Error publishing joined events to session", + zap.String("sessionid", sessionId), + zap.Error(err), + ) } // Notify about initial flags of virtual sessions. @@ -417,7 +448,10 @@ func (r *Room) notifySessionJoined(sessionId string) { Type: "message", Message: msg, }); err != nil { - log.Printf("Error publishing initial flags to session %s: %s", sessionId, err) + r.log.Error("Error publishing initial flags to session", + zap.String("sessionid", sessionId), + zap.Error(err), + ) } } } @@ -499,7 +533,9 @@ func (r *Room) UpdateProperties(properties json.RawMessage) { }, } if err := r.publish(message); err != nil { - log.Printf("Could not publish update properties message in room %s: %s", r.Id(), err) + r.log.Error("Could not publish update properties message in room", + zap.Error(err), + ) } } @@ -539,7 +575,9 @@ func (r *Room) PublishSessionJoined(session Session, sessionData *RoomSessionDat message.Event.Join[0].Federated = session.ClientType() == HelloClientTypeFederation } if err := r.publish(message); err != nil { - log.Printf("Could not publish session joined message in room %s: %s", r.Id(), err) + r.log.Error("Could not publish session joined message in room", + zap.Error(err), + ) } } @@ -560,7 +598,9 @@ func (r *Room) PublishSessionLeft(session Session) { }, } if err := r.publish(message); err != nil { - log.Printf("Could not publish session left message in room %s: %s", r.Id(), err) + r.log.Error("Could not publish session left message in room", + zap.Error(err), + ) } if session.ClientType() == HelloClientTypeInternal { @@ -664,7 +704,9 @@ func (r *Room) PublishUsersInCallChanged(changed []map[string]interface{}, users r.mu.Lock() if !r.inCallSessions[session] { r.inCallSessions[session] = true - log.Printf("Session %s joined call %s", session.PublicId(), r.id) + r.log.Info("Session joined call", + zap.String("sessionid", session.PublicId()), + ) } r.mu.Unlock() } else { @@ -693,7 +735,9 @@ func (r *Room) PublishUsersInCallChanged(changed []map[string]interface{}, users }, } if err := r.publish(message); err != nil { - log.Printf("Could not publish incall message in room %s: %s", r.Id(), err) + r.log.Error("Could not publish incall message in room", + zap.Error(err), + ) } } @@ -727,7 +771,9 @@ func (r *Room) PublishUsersInCallChangedAll(inCall int) { return } - log.Printf("Sessions %v joined call %s", joined, r.id) + r.log.Info("Sessions joined call", + zap.Any("sessions", joined), + ) } else if len(r.inCallSessions) > 0 { // Perform actual leaving asynchronously. ch := make(chan *ClientSession, 1) @@ -780,7 +826,9 @@ func (r *Room) PublishUsersInCallChangedAll(inCall int) { for _, session := range notify { if !session.SendMessage(message) { - log.Printf("Could not send incall message from room %s to %s", r.Id(), session.PublicId()) + r.log.Error("Could not send incall message from room to session", + zap.String("sessionid", session.PublicId()), + ) } } } @@ -802,7 +850,9 @@ func (r *Room) PublishUsersChanged(changed []map[string]interface{}, users []map }, } if err := r.publish(message); err != nil { - log.Printf("Could not publish users changed message in room %s: %s", r.Id(), err) + r.log.Error("Could not publish users changed message in room", + zap.Error(err), + ) } } @@ -861,7 +911,9 @@ func (r *Room) NotifySessionChanged(session Session, flags SessionChangeFlag) { r.mu.Lock() if !r.inCallSessions[session] { r.inCallSessions[session] = true - log.Printf("Session %s joined call %s", session.PublicId(), r.id) + r.log.Info("Session joined call", + zap.String("sessionid", session.PublicId()), + ) } r.mu.Unlock() } else if joinLeave == 2 { @@ -886,7 +938,9 @@ func (r *Room) publishUsersChangedWithInternal() { } if err := r.publish(message); err != nil { - log.Printf("Could not publish users changed message in room %s: %s", r.Id(), err) + r.log.Error("Could not publish users changed message in room", + zap.Error(err), + ) } } @@ -904,7 +958,9 @@ func (r *Room) publishSessionFlagsChanged(session *VirtualSession) { }, } if err := r.publish(message); err != nil { - log.Printf("Could not publish flags changed message in room %s: %s", r.Id(), err) + r.log.Error("Could not publish flags changed message in room", + zap.Error(err), + ) } } @@ -966,7 +1022,10 @@ func (r *Room) publishActiveSessions() (int, *sync.WaitGroup) { defer cancel() if err := r.hub.roomPing.SendPings(ctx, r.id, url, entries); err != nil { - log.Printf("Error pinging room %s for active entries %+v: %s", r.id, entries, err) + r.log.Error("Error pinging room for active entries", + zap.Any("entries", entries), + zap.Error(err), + ) } }(urls[u], e) } @@ -990,7 +1049,9 @@ func (r *Room) publishRoomMessage(message *BackendRoomMessageRequest) { }, } if err := r.publish(msg); err != nil { - log.Printf("Could not publish room message in room %s: %s", r.Id(), err) + r.log.Error("Could not publish room message in room", + zap.Error(err), + ) } } @@ -1017,7 +1078,10 @@ func (r *Room) publishSwitchTo(message *BackendRoomSwitchToMessageRequest) { Type: "message", Message: msg, }); err != nil { - log.Printf("Error publishing switchto event to session %s: %s", sessionId, err) + r.log.Error("Error publishing switchto event to session", + zap.String("sessionid", sessionId), + zap.Error(err), + ) } }(sessionId) } @@ -1045,7 +1109,10 @@ func (r *Room) publishSwitchTo(message *BackendRoomSwitchToMessageRequest) { Type: "message", Message: msg, }); err != nil { - log.Printf("Error publishing switchto event to session %s: %s", sessionId, err) + r.log.Error("Error publishing switchto event to session", + zap.String("sessionid", sessionId), + zap.Error(err), + ) } }(sessionId, details) } diff --git a/room_ping.go b/room_ping.go index 5902068a..e4dba8eb 100644 --- a/room_ping.go +++ b/room_ping.go @@ -23,10 +23,11 @@ package signaling import ( "context" - "log" "net/url" "sync" "time" + + "go.uber.org/zap" ) type pingEntries struct { @@ -63,6 +64,7 @@ func (e *pingEntries) RemoveRoom(roomId string) { // For that, all ping requests across rooms of enabled instances are combined // and sent out batched every "updateActiveSessionsInterval" seconds. type RoomPing struct { + log *zap.Logger mu sync.Mutex closer *Closer @@ -72,8 +74,9 @@ type RoomPing struct { entries map[string]*pingEntries } -func NewRoomPing(backend *BackendClient, capabilities *Capabilities) (*RoomPing, error) { +func NewRoomPing(log *zap.Logger, backend *BackendClient, capabilities *Capabilities) (*RoomPing, error) { result := &RoomPing{ + log: log, closer: NewCloser(), backend: backend, capabilities: capabilities, @@ -125,7 +128,11 @@ func (p *RoomPing) publishEntries(entries *pingEntries, timeout time.Duration) { defer cancel2() if err := p.sendPingsDirect(ctx2, roomId, entries.url, e); err != nil { - log.Printf("Error pinging room %s for active entries %+v: %s", roomId, e, err) + p.log.Error("Error pinging room for active entries", + zap.String("roomid", roomId), + zap.Any("entries", e), + zap.Error(err), + ) } } return @@ -179,7 +186,11 @@ func (p *RoomPing) sendPingsCombined(url *url.URL, entries []BackendPingEntry, l request := NewBackendClientPingRequest("", tosend) var response BackendClientResponse if err := p.backend.PerformJSONRequest(ctx, url, request, &response); err != nil { - log.Printf("Error sending combined ping session entries %+v to %s: %s", tosend, url, err) + p.log.Error("Error sending combined ping session entries", + zap.Any("entries", tosend), + zap.Stringer("url", url), + zap.Error(err), + ) } } } diff --git a/room_ping_test.go b/room_ping_test.go index 0bc775d4..62d172aa 100644 --- a/room_ping_test.go +++ b/room_ping_test.go @@ -34,6 +34,7 @@ import ( func NewRoomPingForTest(t *testing.T) (*url.URL, *RoomPing) { require := require.New(t) + log := GetLoggerForTest(t) r := mux.NewRouter() registerBackendHandler(t, r) @@ -45,10 +46,10 @@ func NewRoomPingForTest(t *testing.T) (*url.URL, *RoomPing) { config, err := getTestConfig(server) require.NoError(err) - backend, err := NewBackendClient(config, 1, "0.0", nil) + backend, err := NewBackendClient(log, config, 1, "0.0", nil) require.NoError(err) - p, err := NewRoomPing(backend, backend.capabilities) + p, err := NewRoomPing(log, backend, backend.capabilities) require.NoError(err) u, err := url.Parse(server.URL) @@ -58,7 +59,6 @@ func NewRoomPingForTest(t *testing.T) (*url.URL, *RoomPing) { } func TestSingleRoomPing(t *testing.T) { - CatchLogForTest(t) assert := assert.New(t) u, ping := NewRoomPingForTest(t) @@ -100,7 +100,6 @@ func TestSingleRoomPing(t *testing.T) { } func TestMultiRoomPing(t *testing.T) { - CatchLogForTest(t) assert := assert.New(t) u, ping := NewRoomPingForTest(t) @@ -138,7 +137,6 @@ func TestMultiRoomPing(t *testing.T) { } func TestMultiRoomPing_Separate(t *testing.T) { - CatchLogForTest(t) assert := assert.New(t) u, ping := NewRoomPingForTest(t) @@ -172,7 +170,6 @@ func TestMultiRoomPing_Separate(t *testing.T) { } func TestMultiRoomPing_DeleteRoom(t *testing.T) { - CatchLogForTest(t) assert := assert.New(t) u, ping := NewRoomPingForTest(t) diff --git a/room_test.go b/room_test.go index 13bfa12d..c243f8a7 100644 --- a/room_test.go +++ b/room_test.go @@ -77,14 +77,14 @@ func TestRoom_InCall(t *testing.T) { func TestRoom_Update(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) + log := GetLoggerForTest(t) hub, _, router, server := CreateHubForTest(t) config, err := getTestConfig(server) require.NoError(err) - b, err := NewBackendServer(config, hub, "no-version") + b, err := NewBackendServer(log, config, hub, "no-version") require.NoError(err) require.NoError(b.Start(router)) @@ -180,14 +180,14 @@ loop: func TestRoom_Delete(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) + log := GetLoggerForTest(t) hub, _, router, server := CreateHubForTest(t) config, err := getTestConfig(server) require.NoError(err) - b, err := NewBackendServer(config, hub, "no-version") + b, err := NewBackendServer(log, config, hub, "no-version") require.NoError(err) require.NoError(b.Start(router)) @@ -294,14 +294,14 @@ loop: func TestRoom_RoomSessionData(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) + log := GetLoggerForTest(t) hub, _, router, server := CreateHubForTest(t) config, err := getTestConfig(server) require.NoError(err) - b, err := NewBackendServer(config, hub, "no-version") + b, err := NewBackendServer(log, config, hub, "no-version") require.NoError(err) require.NoError(b.Start(router)) @@ -344,14 +344,14 @@ func TestRoom_RoomSessionData(t *testing.T) { func TestRoom_InCallAll(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) + log := GetLoggerForTest(t) hub, _, router, server := CreateHubForTest(t) config, err := getTestConfig(server) require.NoError(err) - b, err := NewBackendServer(config, hub, "no-version") + b, err := NewBackendServer(log, config, hub, "no-version") require.NoError(err) require.NoError(b.Start(router)) diff --git a/roomsessions_builtin.go b/roomsessions_builtin.go index 926fe9fb..73db3932 100644 --- a/roomsessions_builtin.go +++ b/roomsessions_builtin.go @@ -24,12 +24,15 @@ package signaling import ( "context" "errors" - "log" "sync" "sync/atomic" + + "go.uber.org/zap" ) type BuiltinRoomSessions struct { + log *zap.Logger + sessionIdToRoomSession map[string]string roomSessionToSessionid map[string]string mu sync.RWMutex @@ -37,8 +40,10 @@ type BuiltinRoomSessions struct { clients *GrpcClients } -func NewBuiltinRoomSessions(clients *GrpcClients) (RoomSessions, error) { +func NewBuiltinRoomSessions(log *zap.Logger, clients *GrpcClients) (RoomSessions, error) { return &BuiltinRoomSessions{ + log: log, + sessionIdToRoomSession: make(map[string]string), roomSessionToSessionid: make(map[string]string), @@ -124,10 +129,17 @@ func (r *BuiltinRoomSessions) LookupSessionId(ctx context.Context, roomSessionId if errors.Is(err, context.Canceled) { return } else if err != nil { - log.Printf("Received error while checking for room session id %s on %s: %s", roomSessionId, client.Target(), err) + r.log.Error("Received error while checking for room session id", + zap.String("roomsessionid", roomSessionId), + zap.String("target", client.Target()), + zap.Error(err), + ) return } else if sid == "" { - log.Printf("Received empty session id for room session id %s from %s", roomSessionId, client.Target()) + r.log.Warn("Received empty session id for room session id", + zap.String("roomsessionid", roomSessionId), + zap.String("target", client.Target()), + ) return } diff --git a/roomsessions_builtin_test.go b/roomsessions_builtin_test.go index c69e3460..c1ddb28b 100644 --- a/roomsessions_builtin_test.go +++ b/roomsessions_builtin_test.go @@ -28,7 +28,8 @@ import ( ) func TestBuiltinRoomSessions(t *testing.T) { - sessions, err := NewBuiltinRoomSessions(nil) + log := GetLoggerForTest(t) + sessions, err := NewBuiltinRoomSessions(log, nil) require.NoError(t, err) testRoomSessions(t, sessions) diff --git a/server/main.go b/server/main.go index b8ace972..d68c7766 100644 --- a/server/main.go +++ b/server/main.go @@ -27,7 +27,6 @@ import ( "errors" "flag" "fmt" - "log" "net" "net/http" "net/http/pprof" @@ -43,6 +42,8 @@ import ( "github.com/dlintw/goconf" "github.com/gorilla/mux" "github.com/nats-io/nats.go" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" signaling "github.com/strukturag/nextcloud-spreed-signaling" ) @@ -95,10 +96,17 @@ func createTLSListener(addr string, certFile, keyFile string) (net.Listener, err } type Listeners struct { + log *zap.Logger mu sync.Mutex listeners []net.Listener } +func newListeners(log *zap.Logger) *Listeners { + return &Listeners{ + log: log, + } +} + func (l *Listeners) Add(listener net.Listener) { l.mu.Lock() defer l.mu.Unlock() @@ -112,13 +120,15 @@ func (l *Listeners) Close() { for _, listener := range l.listeners { if err := listener.Close(); err != nil { - log.Printf("Error closing listener %s: %s", listener.Addr(), err) + l.log.Error("Error closing listener", + zap.Any("addr", listener.Addr()), + zap.Error(err), + ) } } } func main() { - log.SetFlags(log.Lshortfile) flag.Parse() if *showVersion { @@ -134,41 +144,67 @@ func main() { if *cpuprofile != "" { f, err := os.Create(*cpuprofile) if err != nil { - log.Fatal(err) + fmt.Printf("%s\n", err) + os.Exit(1) } if err := runtimepprof.StartCPUProfile(f); err != nil { - log.Fatalf("Error writing CPU profile to %s: %s", *cpuprofile, err) + fmt.Printf("Error writing CPU profile to %s: %s\n", *cpuprofile, err) + os.Exit(1) } - log.Printf("Writing CPU profile to %s ...", *cpuprofile) + fmt.Printf("Writing CPU profile to %s ...\n", *cpuprofile) defer runtimepprof.StopCPUProfile() } if *memprofile != "" { f, err := os.Create(*memprofile) if err != nil { - log.Fatal(err) + fmt.Printf("%s\n", err) + os.Exit(1) } defer func() { - log.Printf("Writing Memory profile to %s ...", *memprofile) + fmt.Printf("Writing Memory profile to %s ...\n", *memprofile) runtime.GC() if err := runtimepprof.WriteHeapProfile(f); err != nil { - log.Printf("Error writing Memory profile to %s: %s", *memprofile, err) + fmt.Printf("Error writing Memory profile to %s: %s\n", *memprofile, err) + os.Exit(1) } }() } - log.Printf("Starting up version %s/%s as pid %d", version, runtime.Version(), os.Getpid()) + fmt.Printf("Starting up version %s/%s as pid %d\n", version, runtime.Version(), os.Getpid()) config, err := goconf.ReadConfigFile(*configFlag) if err != nil { - log.Fatal("Could not read configuration: ", err) + fmt.Printf("Could not read configuration: %s\n", err) + os.Exit(1) + } + + var logConfig zap.Config + if debug, _ := config.GetBool("app", "debug"); debug { + logConfig = zap.NewDevelopmentConfig() + } else { + logConfig = zap.NewProductionConfig() + logConfig.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder + } + log, err := logConfig.Build( + // Only log stack traces when panicing. + zap.AddStacktrace(zap.DPanicLevel), + ) + if err != nil { + fmt.Printf("Could not create logger: %s\n", err) + os.Exit(1) } + restoreGlobalLogs := zap.ReplaceGlobals(log) + defer restoreGlobalLogs() + cpus := runtime.NumCPU() runtime.GOMAXPROCS(cpus) - log.Printf("Using a maximum of %d CPUs", cpus) + log.Debug("Using number of CPUs", + zap.Int("cpus", cpus), + ) signaling.RegisterStats() @@ -177,61 +213,81 @@ func main() { natsUrl = nats.DefaultURL } - events, err := signaling.NewAsyncEvents(natsUrl) + events, err := signaling.NewAsyncEvents(log, natsUrl) if err != nil { - log.Fatal("Could not create async events client: ", err) + log.Fatal("Could not create async events client", + zap.Error(err), + ) } defer events.Close() - dnsMonitor, err := signaling.NewDnsMonitor(dnsMonitorInterval) + dnsMonitor, err := signaling.NewDnsMonitor(log, dnsMonitorInterval) if err != nil { - log.Fatal("Could not create DNS monitor: ", err) + log.Fatal("Could not create DNS monitor", + zap.Error(err), + ) } if err := dnsMonitor.Start(); err != nil { - log.Fatal("Could not start DNS monitor: ", err) + log.Fatal("Could not start DNS monitor", + zap.Error(err), + ) } defer dnsMonitor.Stop() - etcdClient, err := signaling.NewEtcdClient(config, "mcu") + etcdClient, err := signaling.NewEtcdClient(log, config, "mcu") if err != nil { - log.Fatalf("Could not create etcd client: %s", err) + log.Fatal("Could not create etcd client", + zap.Error(err), + ) } defer func() { if err := etcdClient.Close(); err != nil { - log.Printf("Error while closing etcd client: %s", err) + log.Error("Error while closing etcd client", + zap.Error(err), + ) } }() - rpcServer, err := signaling.NewGrpcServer(config) + rpcServer, err := signaling.NewGrpcServer(log, config) if err != nil { - log.Fatalf("Could not create RPC server: %s", err) + log.Fatal("Could not create RPC server", + zap.Error(err), + ) } go func() { if err := rpcServer.Run(); err != nil { - log.Fatalf("Could not start RPC server: %s", err) + log.Fatal("Could not start RPC server", + zap.Error(err), + ) } }() defer rpcServer.Close() - rpcClients, err := signaling.NewGrpcClients(config, etcdClient, dnsMonitor) + rpcClients, err := signaling.NewGrpcClients(log, config, etcdClient, dnsMonitor) if err != nil { - log.Fatalf("Could not create RPC clients: %s", err) + log.Fatal("Could not create RPC clients", + zap.Error(err), + ) } defer rpcClients.Close() r := mux.NewRouter() - hub, err := signaling.NewHub(config, events, rpcServer, rpcClients, etcdClient, r, version) + hub, err := signaling.NewHub(log, config, events, rpcServer, rpcClients, etcdClient, r, version) if err != nil { - log.Fatal("Could not create hub: ", err) + log.Fatal("Could not create hub", + zap.Error(err), + ) } mcuUrl, _ := signaling.GetStringOptionWithEnv(config, "mcu", "url") mcuType, _ := config.GetString("mcu", "type") if mcuType == "" && mcuUrl != "" { - log.Printf("WARNING: Old-style MCU configuration detected with url but no type, defaulting to type %s", signaling.McuTypeJanus) + log.Warn("Old-style MCU configuration detected with url but no type, using defaulting type", + zap.String("type", signaling.McuTypeJanus), + ) mcuType = signaling.McuTypeJanus } else if mcuType == signaling.McuTypeJanus && mcuUrl == "" { - log.Printf("WARNING: Old-style MCU configuration detected with type but no url, disabling") + log.Warn("Old-style MCU configuration detected with type but no url, disabling") mcuType = "" } @@ -245,45 +301,62 @@ func main() { ctx := context.TODO() switch mcuType { case signaling.McuTypeJanus: - mcu, err = signaling.NewMcuJanus(ctx, mcuUrl, config) + mcu, err = signaling.NewMcuJanus(ctx, log, mcuUrl, config) signaling.UnregisterProxyMcuStats() signaling.RegisterJanusMcuStats() case signaling.McuTypeProxy: - mcu, err = signaling.NewMcuProxy(config, etcdClient, rpcClients, dnsMonitor) + mcu, err = signaling.NewMcuProxy(log, config, etcdClient, rpcClients, dnsMonitor) signaling.UnregisterJanusMcuStats() signaling.RegisterProxyMcuStats() default: - log.Fatal("Unsupported MCU type: ", mcuType) + log.Fatal("Unsupported MCU type", + zap.String("type", mcuType), + ) } if err == nil { err = mcu.Start(ctx) if err != nil { - log.Printf("Could not create %s MCU: %s", mcuType, err) + log.Error("Could not create MCU", + zap.String("type", mcuType), + zap.Error(err), + ) } } if err == nil { break } - log.Printf("Could not initialize %s MCU (%s) will retry in %s", mcuType, err, mcuRetry) + log.Error("Could not initialize MCU, will retry", + zap.String("type", mcuType), + zap.Duration("wait", mcuRetry), + zap.Error(err), + ) + mcuRetryTimer.Reset(mcuRetry) select { case sig := <-sigChan: switch sig { case os.Interrupt: - log.Fatalf("Cancelled") + log.Fatal("Cancelled") case syscall.SIGHUP: - log.Printf("Received SIGHUP, reloading %s", *configFlag) + log.Info("Received SIGHUP, reloading", + zap.String("filename", *configFlag), + ) if config, err = goconf.ReadConfigFile(*configFlag); err != nil { - log.Printf("Could not read configuration from %s: %s", *configFlag, err) + log.Error("Could not read configuration", + zap.String("filename", *configFlag), + zap.Error(err), + ) } else { mcuUrl, _ = signaling.GetStringOptionWithEnv(config, "mcu", "url") mcuType, _ = config.GetString("mcu", "type") if mcuType == "" && mcuUrl != "" { - log.Printf("WARNING: Old-style MCU configuration detected with url but no type, defaulting to type %s", signaling.McuTypeJanus) + log.Warn("Old-style MCU configuration detected with url but no type, using defaulting type", + zap.String("type", signaling.McuTypeJanus), + ) mcuType = signaling.McuTypeJanus } else if mcuType == signaling.McuTypeJanus && mcuUrl == "" { - log.Printf("WARNING: Old-style MCU configuration detected with type but no url, disabling") + log.Warn("Old-style MCU configuration detected with type but no url, disabling") mcuType = "" break mcuTypeLoop } @@ -300,7 +373,9 @@ func main() { if mcu != nil { defer mcu.Stop() - log.Printf("Using %s MCU", mcuType) + log.Info("Using MCU", + zap.String("type", mcuType), + ) hub.SetMcu(mcu) } } @@ -308,16 +383,20 @@ func main() { go hub.Run() defer hub.Stop() - server, err := signaling.NewBackendServer(config, hub, version) + server, err := signaling.NewBackendServer(log, config, hub, version) if err != nil { - log.Fatal("Could not create backend server: ", err) + log.Fatal("Could not create backend server", + zap.Error(err), + ) } if err := server.Start(r); err != nil { - log.Fatal("Could not start backend server: ", err) + log.Fatal("Could not start backend server", + zap.Error(err), + ) } if debug, _ := config.GetBool("app", "debug"); debug { - log.Println("Installing debug handlers in \"/debug/pprof\"") + log.Debug("Installing debug handlers in \"/debug/pprof\"") r.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index)) r.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline)) r.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile)) @@ -329,7 +408,8 @@ func main() { } } - var listeners Listeners + listeners := newListeners(log) + defer listeners.Close() if saddr, _ := signaling.GetStringOptionWithEnv(config, "https", "listen"); saddr != "" { cert, _ := config.GetString("https", "certificate") @@ -348,10 +428,13 @@ func main() { } for _, address := range strings.Split(saddr, " ") { go func(address string) { - log.Println("Listening on", address) + log := log.With(zap.String("addr", address)) + log.Debug("Listening") listener, err := createTLSListener(address, cert, key) if err != nil { - log.Fatal("Could not start listening: ", err) + log.Fatal("Could not start listening", + zap.Error(err), + ) } srv := &http.Server{ Handler: r, @@ -362,7 +445,9 @@ func main() { listeners.Add(listener) if err := srv.Serve(listener); err != nil { if !hub.IsShutdownScheduled() || !errors.Is(err, net.ErrClosed) { - log.Fatal("Could not start server: ", err) + log.Fatal("Could not start server", + zap.Error(err), + ) } } }(address) @@ -381,10 +466,13 @@ func main() { for _, address := range strings.Split(addr, " ") { go func(address string) { - log.Println("Listening on", address) + log := log.With(zap.String("addr", address)) + log.Debug("Listening") listener, err := createListener(address) if err != nil { - log.Fatal("Could not start listening: ", err) + log.Fatal("Could not start listening", + zap.Error(err), + ) } srv := &http.Server{ Handler: r, @@ -396,7 +484,9 @@ func main() { listeners.Add(listener) if err := srv.Serve(listener); err != nil { if !hub.IsShutdownScheduled() || !errors.Is(err, net.ErrClosed) { - log.Fatal("Could not start server: ", err) + log.Fatal("Could not start server", + zap.Error(err), + ) } } }(address) @@ -409,23 +499,28 @@ loop: case sig := <-sigChan: switch sig { case os.Interrupt: - log.Println("Interrupted") + log.Debug("Interrupted") break loop case syscall.SIGHUP: - log.Printf("Received SIGHUP, reloading %s", *configFlag) + log.Info("Received SIGHUP, reloading", + zap.String("filename", *configFlag), + ) if config, err := goconf.ReadConfigFile(*configFlag); err != nil { - log.Printf("Could not read configuration from %s: %s", *configFlag, err) + log.Error("Could not read configuration", + zap.String("filename", *configFlag), + zap.Error(err), + ) } else { hub.Reload(config) server.Reload(config) } case syscall.SIGUSR1: - log.Printf("Received SIGUSR1, scheduling server to shutdown") + log.Info("Received SIGUSR1, scheduling server to shutdown") hub.ScheduleShutdown() listeners.Close() } case <-hub.ShutdownChannel(): - log.Printf("All clients disconnected, shutting down") + log.Info("All clients disconnected, shutting down") break loop } } diff --git a/test_helpers.go b/test_helpers.go index b7f0bdd8..c7ff9cf4 100644 --- a/test_helpers.go +++ b/test_helpers.go @@ -22,37 +22,39 @@ package signaling import ( - "io" - "log" + "sync" "testing" + + "go.uber.org/zap" + "go.uber.org/zap/zaptest" ) var ( - prevWriter io.Writer - prevFlags int + loggers map[*testing.T]*zap.Logger + loggersMu sync.Mutex ) -func init() { - prevWriter = log.Writer() - prevFlags = log.Flags() -} - -type testLogWriter struct { - t testing.TB -} +func removeLoggerForTest(t *testing.T) { + loggersMu.Lock() + defer loggersMu.Unlock() -func (w *testLogWriter) Write(b []byte) (int, error) { - w.t.Helper() - w.t.Logf("%s", string(b)) - return len(b), nil + delete(loggers, t) } -func CatchLogForTest(t testing.TB) { - t.Cleanup(func() { - log.SetOutput(prevWriter) - log.SetFlags(prevFlags) - }) +func GetLoggerForTest(t *testing.T) *zap.Logger { + loggersMu.Lock() + defer loggersMu.Unlock() - log.SetOutput(&testLogWriter{t}) - log.SetFlags(prevFlags | log.Lshortfile) + log, found := loggers[t] + if !found { + if loggers == nil { + loggers = make(map[*testing.T]*zap.Logger) + } + log = zaptest.NewLogger(t) + loggers[t] = log + t.Cleanup(func() { + removeLoggerForTest(t) + }) + } + return log } diff --git a/throttle.go b/throttle.go index dcd53320..2639eecb 100644 --- a/throttle.go +++ b/throttle.go @@ -24,11 +24,12 @@ package signaling import ( "context" "errors" - "log" "net" "strconv" "sync" "time" + + "go.uber.org/zap" ) const ( @@ -91,6 +92,7 @@ type throttleEntry struct { } type memoryThrottler struct { + log *zap.Logger getNow func() time.Time doDelay func(context.Context, time.Duration) @@ -100,8 +102,9 @@ type memoryThrottler struct { closer *Closer } -func NewMemoryThrottler() (Throttler, error) { +func NewMemoryThrottler(log *zap.Logger) (Throttler, error) { result := &memoryThrottler{ + log: log, getNow: time.Now, clients: make(map[string]map[string][]throttleEntry), @@ -279,7 +282,10 @@ func (t *memoryThrottler) CheckBruteforce(ctx context.Context, client string, ac if l >= maxBruteforceAttempts { delta := now.Sub(entries[l-maxBruteforceAttempts].ts) if delta <= maxBruteforceDurationThreshold { - log.Printf("Detected bruteforce attempt on \"%s\" from %s", action, client) + t.log.Info("Detected bruteforce attempt", + zap.String("action", action), + zap.String("client", client), + ) statsThrottleBruteforceTotal.WithLabelValues(action).Inc() return doThrottle, ErrBruteforceDetected } @@ -303,7 +309,11 @@ func (t *memoryThrottler) throttle(ctx context.Context, client string, action st } count := t.addEntry(client, action, entry) delay := t.getDelay(count - 1) - log.Printf("Failed attempt on \"%s\" from %s, throttling by %s", action, client, delay) + t.log.Info("Failed attempt, throttling", + zap.String("action", action), + zap.String("client", client), + zap.Duration("delay", delay), + ) statsThrottleDelayedTotal.WithLabelValues(action, strconv.FormatInt(delay.Milliseconds(), 10)).Inc() t.doDelay(ctx, delay) } diff --git a/throttle_test.go b/throttle_test.go index a95102a7..f0ff0f20 100644 --- a/throttle_test.go +++ b/throttle_test.go @@ -32,7 +32,8 @@ import ( func newMemoryThrottlerForTest(t *testing.T) *memoryThrottler { t.Helper() - result, err := NewMemoryThrottler() + log := GetLoggerForTest(t) + result, err := NewMemoryThrottler(log) require.NoError(t, err) t.Cleanup(func() { diff --git a/transient_data_test.go b/transient_data_test.go index 66ac3d64..f6c36f36 100644 --- a/transient_data_test.go +++ b/transient_data_test.go @@ -86,7 +86,6 @@ func Test_TransientData(t *testing.T) { func Test_TransientMessages(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) diff --git a/virtualsession.go b/virtualsession.go index cfbd4356..853996a7 100644 --- a/virtualsession.go +++ b/virtualsession.go @@ -24,9 +24,10 @@ package signaling import ( "context" "encoding/json" - "log" "net/url" "sync/atomic" + + "go.uber.org/zap" ) const ( @@ -36,6 +37,7 @@ const ( ) type VirtualSession struct { + log *zap.Logger hub *Hub session *ClientSession privateId string @@ -57,8 +59,11 @@ func GetVirtualSessionId(session Session, sessionId string) string { return session.PublicId() + "|" + sessionId } -func NewVirtualSession(session *ClientSession, privateId string, publicId string, data *SessionIdData, msg *AddSessionInternalClientMessage) (*VirtualSession, error) { +func NewVirtualSession(log *zap.Logger, session *ClientSession, privateId string, publicId string, data *SessionIdData, msg *AddSessionInternalClientMessage) (*VirtualSession, error) { result := &VirtualSession{ + log: log.With( + zap.String("sessionid", publicId), + ), hub: session.hub, session: session, privateId: privateId, @@ -148,7 +153,9 @@ func (s *VirtualSession) SetRoom(room *Room) { s.room.Store(room) if room != nil { if err := s.hub.roomSessions.SetRoomSession(s, s.PublicId()); err != nil { - log.Printf("Error adding virtual room session %s: %s", s.PublicId(), err) + s.log.Error("Error adding virtual room session", + zap.Error(err), + ) } } else { s.hub.roomSessions.DeleteRoomSession(s) @@ -199,7 +206,11 @@ func (s *VirtualSession) notifyBackendRemoved(room *Room, session Session, messa var response BackendClientResponse if err := s.hub.backend.PerformJSONRequest(ctx, s.ParsedBackendUrl(), request, &response); err != nil { virtualSessionId := GetVirtualSessionId(s.session, s.PublicId()) - log.Printf("Could not leave virtual session %s at backend %s: %s", virtualSessionId, s.BackendUrl(), err) + s.log.Error("Could not leave virtual session at backend", + zap.String("virtualsessionid", virtualSessionId), + zap.String("backend", s.BackendUrl()), + zap.Error(err), + ) if session != nil && message != nil { reply := message.NewErrorServerMessage(NewError("remove_failed", "Could not remove virtual session from backend.")) session.SendMessage(reply) @@ -210,7 +221,11 @@ func (s *VirtualSession) notifyBackendRemoved(room *Room, session Session, messa if response.Type == "error" { virtualSessionId := GetVirtualSessionId(s.session, s.PublicId()) if session != nil && message != nil && (response.Error == nil || response.Error.Code != "no_such_room") { - log.Printf("Could not leave virtual session %s at backend %s: %+v", virtualSessionId, s.BackendUrl(), response.Error) + s.log.Error("Could not leave virtual session at backend", + zap.String("virtualsessionid", virtualSessionId), + zap.String("backend", s.BackendUrl()), + zap.Any("error", response.Error), + ) reply := message.NewErrorServerMessage(NewError("remove_failed", response.Error.Error())) session.SendMessage(reply) } @@ -224,7 +239,10 @@ func (s *VirtualSession) notifyBackendRemoved(room *Room, session Session, messa var response BackendClientSessionResponse err := s.hub.backend.PerformJSONRequest(ctx, s.ParsedBackendUrl(), request, &response) if err != nil { - log.Printf("Could not remove virtual session %s from backend %s: %s", s.PublicId(), s.BackendUrl(), err) + s.log.Error("Could not remove virtual session from backend", + zap.String("backend", s.BackendUrl()), + zap.Error(err), + ) if session != nil && message != nil { reply := message.NewErrorServerMessage(NewError("remove_failed", "Could not remove virtual session from backend.")) session.SendMessage(reply) @@ -287,7 +305,9 @@ func (s *VirtualSession) ProcessAsyncSessionMessage(message *AsyncMessage) { message.Message.Event.Type == "disinvite" && message.Message.Event.Disinvite != nil && message.Message.Event.Disinvite.RoomId == room.Id() { - log.Printf("Virtual session %s was disinvited from room %s, hanging up", s.PublicId(), room.Id()) + s.log.Info("Virtual session was disinvited from room, hanging up", + zap.String("roomid", room.Id()), + ) payload := map[string]interface{}{ "type": "hangup", "hangup": map[string]string{ @@ -296,7 +316,10 @@ func (s *VirtualSession) ProcessAsyncSessionMessage(message *AsyncMessage) { } data, err := json.Marshal(payload) if err != nil { - log.Printf("could not marshal control payload %+v: %s", payload, err) + s.log.Error("could not marshal control payload", + zap.Any("payload", payload), + zap.Error(err), + ) return } diff --git a/virtualsession_test.go b/virtualsession_test.go index bc9ca103..2e00918b 100644 --- a/virtualsession_test.go +++ b/virtualsession_test.go @@ -34,7 +34,6 @@ import ( func TestVirtualSession(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -262,7 +261,6 @@ func checkHasEntryWithInCall(message *RoomEventServerMessage, sessionId string, func TestVirtualSessionCustomInCall(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) @@ -417,7 +415,6 @@ func TestVirtualSessionCustomInCall(t *testing.T) { func TestVirtualSessionCleanup(t *testing.T) { t.Parallel() - CatchLogForTest(t) require := require.New(t) assert := assert.New(t) hub, _, _, server := CreateHubForTest(t) From 4771d5d893e5808417c03f520bd275dacacf771e Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Sun, 15 Sep 2024 19:22:58 +0200 Subject: [PATCH 3/3] client: Switch to using go.uber.org/zap for logging. --- client/main.go | 154 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 109 insertions(+), 45 deletions(-) diff --git a/client/main.go b/client/main.go index 674fc9e9..c537bf7d 100644 --- a/client/main.go +++ b/client/main.go @@ -28,7 +28,6 @@ import ( "flag" "fmt" "io" - "log" pseudorand "math/rand" "net" "net/http" @@ -46,6 +45,7 @@ import ( "github.com/gorilla/securecookie" "github.com/gorilla/websocket" "github.com/mailru/easyjson" + "go.uber.org/zap" signaling "github.com/strukturag/nextcloud-spreed-signaling" ) @@ -81,6 +81,8 @@ const ( ) type Stats struct { + log *zap.Logger + numRecvMessages atomic.Uint64 numSentMessages atomic.Uint64 resetRecvMessages uint64 @@ -107,10 +109,13 @@ func (s *Stats) Log() { sentMessages := totalSentMessages - s.resetSentMessages totalRecvMessages := s.numRecvMessages.Load() recvMessages := totalRecvMessages - s.resetRecvMessages - log.Printf("Stats: sent=%d (%d/sec), recv=%d (%d/sec), delta=%d", - totalSentMessages, sentMessages/perSec, - totalRecvMessages, recvMessages/perSec, - totalSentMessages-totalRecvMessages) + s.log.Info("Stats updated", + zap.Uint64("sent", totalSentMessages), + zap.Uint64("sentspeed", sentMessages/perSec), + zap.Uint64("recv", totalRecvMessages), + zap.Uint64("recvspeed", recvMessages/perSec), + zap.Uint64("delta", totalSentMessages-totalRecvMessages), + ) s.reset(now) } @@ -119,6 +124,7 @@ type MessagePayload struct { } type SignalingClient struct { + log *zap.Logger readyWg *sync.WaitGroup cookie *securecookie.SecureCookie @@ -135,13 +141,14 @@ type SignalingClient struct { userId string } -func NewSignalingClient(cookie *securecookie.SecureCookie, url string, stats *Stats, readyWg *sync.WaitGroup, doneWg *sync.WaitGroup) (*SignalingClient, error) { +func NewSignalingClient(log *zap.Logger, cookie *securecookie.SecureCookie, url string, stats *Stats, readyWg *sync.WaitGroup, doneWg *sync.WaitGroup) (*SignalingClient, error) { conn, _, err := websocket.DefaultDialer.Dial(url, nil) if err != nil { return nil, err } client := &SignalingClient{ + log: log, readyWg: readyWg, cookie: cookie, @@ -204,13 +211,19 @@ func (c *SignalingClient) processMessage(message *signaling.ServerMessage) { case "message": c.processMessageMessage(message) case "bye": - log.Printf("Received bye: %+v", message.Bye) + c.log.Error("Received bye", + zap.Any("bye", message.Bye), + ) c.Close() case "error": - log.Printf("Received error: %+v", message.Error) + c.log.Error("Received error", + zap.Any("error", message.Error), + ) c.Close() default: - log.Printf("Unsupported message type: %+v", *message) + c.log.Warn("Unsupported message type", + zap.Stringer("message", message), + ) } } @@ -236,7 +249,10 @@ func (c *SignalingClient) processHelloMessage(message *signaling.ServerMessage) c.privateSessionId = message.Hello.ResumeId c.publicSessionId = c.privateToPublicSessionId(c.privateSessionId) c.userId = message.Hello.UserId - log.Printf("Registered as %s (userid %s)", c.privateSessionId, c.userId) + c.log.Info("Registered", + zap.String("privateid", c.privateSessionId), + zap.String("userid", c.userId), + ) c.readyWg.Done() } @@ -249,14 +265,18 @@ func (c *SignalingClient) PublicSessionId() string { func (c *SignalingClient) processMessageMessage(message *signaling.ServerMessage) { var msg MessagePayload if err := json.Unmarshal(message.Message.Data, &msg); err != nil { - log.Println("Error in unmarshal", err) + c.log.Error("Error in unmarshal", + zap.Error(err), + ) return } now := time.Now() duration := now.Sub(msg.Now) if duration > messageReportDuration { - log.Printf("Message took %s", duration) + c.log.Warn("Message took too long", + zap.Duration("duration", duration), + ) } } @@ -283,13 +303,17 @@ func (c *SignalingClient) readPump() { websocket.CloseNormalClosure, websocket.CloseGoingAway, websocket.CloseNoStatusReceived) { - log.Printf("Error: %v", err) + c.log.Error("Error reading", + zap.Error(err), + ) } break } if messageType != websocket.TextMessage { - log.Println("Unsupported message type", messageType) + c.log.Error("Unsupported message type", + zap.Int("type", messageType), + ) break } @@ -297,7 +321,9 @@ func (c *SignalingClient) readPump() { if _, err := decodeBuffer.ReadFrom(reader); err != nil { c.lock.Lock() if c.conn != nil { - log.Println("Error reading message", err) + c.log.Error("Error reading message", + zap.Error(err), + ) } c.lock.Unlock() break @@ -305,7 +331,9 @@ func (c *SignalingClient) readPump() { var message signaling.ServerMessage if err := message.UnmarshalJSON(decodeBuffer.Bytes()); err != nil { - log.Printf("Error: %v", err) + c.log.Error("Error unmarshalling", + zap.Error(err), + ) break } @@ -327,7 +355,10 @@ func (c *SignalingClient) writeInternal(message *signaling.ClientMessage) bool { return false } - log.Println("Could not send message", message, err) + c.log.Error("Could not send message", + zap.Stringer("message", message), + zap.Error(err), + ) // TODO(jojo): Differentiate between JSON encode errors and websocket errors. closeData = websocket.FormatCloseMessage(websocket.CloseInternalServerErr, "") goto close @@ -413,29 +444,33 @@ func (c *SignalingClient) SendMessages(clients []*SignalingClient) { } } -func registerAuthHandler(router *mux.Router) { +func registerAuthHandler(log *zap.Logger, router *mux.Router) { router.HandleFunc("/auth", func(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) if err != nil { - log.Println("Error reading body:", err) + log.Error("Error reading body", + zap.Error(err), + ) return } rnd := r.Header.Get(signaling.HeaderBackendSignalingRandom) checksum := r.Header.Get(signaling.HeaderBackendSignalingChecksum) if rnd == "" || checksum == "" { - log.Println("No checksum headers found") + log.Error("No checksum headers found") return } if verify := signaling.CalculateBackendChecksum(rnd, body, backendSecret); verify != checksum { - log.Println("Backend checksum verification failed") + log.Error("Backend checksum verification failed") return } var request signaling.BackendClientRequest if err := request.UnmarshalJSON(body); err != nil { - log.Println(err) + log.Error("Error unmarshalling", + zap.Error(err), + ) return } @@ -449,7 +484,9 @@ func registerAuthHandler(router *mux.Router) { data, err := response.MarshalJSON() if err != nil { - log.Println(err) + log.Error("Error marshalling response message", + zap.Error(err), + ) return } @@ -467,7 +504,9 @@ func registerAuthHandler(router *mux.Router) { jsonpayload, err := payload.MarshalJSON() if err != nil { - log.Println(err) + log.Error("Error marshalling payload", + zap.Error(err), + ) return } @@ -477,10 +516,12 @@ func registerAuthHandler(router *mux.Router) { }) } -func getLocalIP() string { +func getLocalIP(log *zap.Logger) string { interfaces, err := net.InterfaceAddrs() if err != nil { - log.Fatal(err) + log.Fatal("Error getting interfaces", + zap.Error(err), + ) } for _, intf := range interfaces { switch t := intf.(type) { @@ -508,11 +549,14 @@ func reverseSessionId(s string) (string, error) { func main() { flag.Parse() - log.SetFlags(0) + + log := zap.Must(zap.NewDevelopment()) config, err := goconf.ReadConfigFile(*config) if err != nil { - log.Fatal("Could not read configuration: ", err) + log.Fatal("Could not read configuration", + zap.Error(err), + ) } secret, _ := config.GetString("backend", "secret") @@ -523,7 +567,9 @@ func main() { case 32: case 64: default: - log.Printf("WARNING: The sessions hash key should be 32 or 64 bytes but is %d bytes", len(hashKey)) + log.Warn("The sessions hash key should be 32 or 64 bytes", + zap.Int("len", len(hashKey)), + ) } blockKey, _ := config.GetString("sessions", "blockkey") @@ -535,24 +581,30 @@ func main() { case 24: case 32: default: - log.Fatalf("The sessions block key must be 16, 24 or 32 bytes but is %d bytes", len(blockKey)) + log.Fatal("The sessions block key must be 16, 24 or 32 bytes", + zap.Int("len", len(blockKey)), + ) } cookie := securecookie.New([]byte(hashKey), blockBytes).MaxAge(0) cpus := runtime.NumCPU() runtime.GOMAXPROCS(cpus) - log.Printf("Using a maximum of %d CPUs", cpus) + log.Debug("Using number of CPUs", + zap.Int("cpus", cpus), + ) interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt) r := mux.NewRouter() - registerAuthHandler(r) + registerAuthHandler(log, r) - localIP := getLocalIP() + localIP := getLocalIP(log) listener, err := net.Listen("tcp", localIP+":0") if err != nil { - log.Fatal(err) + log.Fatal("Error starting listener", + zap.Error(err), + ) } server := http.Server{ @@ -562,7 +614,9 @@ func main() { server.Serve(listener) // nolint }() backendUrl := "http://" + listener.Addr().String() - log.Println("Backend server running on", backendUrl) + log.Info("Backend server running", + zap.String("url", backendUrl), + ) urls := make([]url.URL, 0) urlstrings := make([]string, 0) @@ -575,24 +629,34 @@ func main() { urls = append(urls, u) urlstrings = append(urlstrings, u.String()) } - log.Printf("Connecting to %s", urlstrings) + log.Info("Connecting", + zap.Strings("urls", urlstrings), + ) clients := make([]*SignalingClient, 0) - stats := &Stats{} + stats := &Stats{ + log: log, + } if *maxClients < 2 { - log.Fatalf("Need at least 2 clients, got %d", *maxClients) + log.Fatal("Need at least 2 clients", + zap.Int("count", *maxClients), + ) } - log.Printf("Starting %d clients", *maxClients) + log.Info("Starting clients", + zap.Int("count", *maxClients), + ) var doneWg sync.WaitGroup var readyWg sync.WaitGroup for i := 0; i < *maxClients; i++ { - client, err := NewSignalingClient(cookie, urls[i%len(urls)].String(), stats, &readyWg, &doneWg) + client, err := NewSignalingClient(log, cookie, urls[i%len(urls)].String(), stats, &readyWg, &doneWg) if err != nil { - log.Fatal(err) + log.Fatal("Error creating signaling client", + zap.Error(err), + ) } defer client.Close() readyWg.Add(1) @@ -612,10 +676,10 @@ func main() { clients = append(clients, client) } - log.Println("Clients created") + log.Info("Clients created") readyWg.Wait() - log.Println("All connections established") + log.Info("All connections established") for _, c := range clients { doneWg.Add(1) @@ -632,14 +696,14 @@ loop: for { select { case <-interrupt: - log.Println("Interrupted") + log.Info("Interrupted") break loop case <-report.C: stats.Log() } } - log.Println("Waiting for clients to terminate ...") + log.Info("Waiting for clients to terminate ...") for _, c := range clients { c.Close() }