istio.io/istio@v0.0.0-20240520182934-d79c90f27776/cni/pkg/log/uds.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package log 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "io" 21 "net/http" 22 "strings" 23 "sync" 24 "time" 25 26 "istio.io/istio/cni/pkg/constants" 27 "istio.io/istio/pkg/log" 28 "istio.io/istio/pkg/network" 29 "istio.io/istio/pkg/uds" 30 ) 31 32 var pluginLog = log.RegisterScope("cni", "CNI network plugin") 33 34 type UDSLogger struct { 35 mu sync.Mutex 36 loggingServer *http.Server 37 } 38 39 type cniLog struct { 40 Level string `json:"level"` 41 Time time.Time `json:"time"` 42 Msg string `json:"msg"` 43 } 44 45 func NewUDSLogger() *UDSLogger { 46 l := &UDSLogger{} 47 mux := http.NewServeMux() 48 mux.HandleFunc(constants.UDSLogPath, l.handleLog) 49 loggingServer := &http.Server{ 50 Handler: mux, 51 } 52 l.loggingServer = loggingServer 53 return l 54 } 55 56 // StartUDSLogServer starts up a UDS server which receives log reported from CNI network plugin. 57 func (l *UDSLogger) StartUDSLogServer(sockAddress string, stop <-chan struct{}) error { 58 if sockAddress == "" { 59 return nil 60 } 61 log.Info("Start a UDS server for CNI plugin logs") 62 unixListener, err := uds.NewListener(sockAddress) 63 if err != nil { 64 return fmt.Errorf("failed to create UDS listener: %v", err) 65 } 66 go func() { 67 if err := l.loggingServer.Serve(unixListener); network.IsUnexpectedListenerError(err) { 68 log.Errorf("Error running UDS log server: %v", err) 69 } 70 }() 71 72 go func() { 73 <-stop 74 if err := l.loggingServer.Close(); err != nil { 75 log.Errorf("CNI log server terminated with error: %v", err) 76 } else { 77 log.Debug("CNI log server terminated") 78 } 79 }() 80 81 return nil 82 } 83 84 func (l *UDSLogger) handleLog(w http.ResponseWriter, req *http.Request) { 85 if req.Body == nil { 86 return 87 } 88 defer req.Body.Close() 89 data, err := io.ReadAll(req.Body) 90 if err != nil { 91 log.Errorf("Failed to read log report from cni plugin: %v", err) 92 return 93 } 94 l.processLog(data) 95 } 96 97 func (l *UDSLogger) processLog(body []byte) { 98 cniLogs := make([]string, 0) 99 err := json.Unmarshal(body, &cniLogs) 100 if err != nil { 101 log.Errorf("Failed to unmarshal CNI plugin logs: %v", err) 102 return 103 } 104 messages := make([]cniLog, 0, len(cniLogs)) 105 for _, l := range cniLogs { 106 var msg cniLog 107 if err := json.Unmarshal([]byte(l), &msg); err != nil { 108 log.Debugf("Failed to unmarshal CNI plugin log entry: %v", err) 109 continue 110 } 111 msg.Msg = strings.TrimSpace(msg.Msg) 112 messages = append(messages, msg) 113 } 114 // Lock log message printing to prevent log messages from different CNI 115 // processes interleave. 116 l.mu.Lock() 117 defer l.mu.Unlock() 118 for _, m := range messages { 119 // There is no fatal log from CNI plugin 120 switch m.Level { 121 case "debug": 122 pluginLog.LogWithTime(log.DebugLevel, m.Msg, m.Time) 123 case "info": 124 pluginLog.LogWithTime(log.InfoLevel, m.Msg, m.Time) 125 case "warn": 126 pluginLog.LogWithTime(log.WarnLevel, m.Msg, m.Time) 127 case "error": 128 pluginLog.LogWithTime(log.ErrorLevel, m.Msg, m.Time) 129 } 130 } 131 }