istio.io/istio@v0.0.0-20240520182934-d79c90f27776/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 "bytes" 19 "context" 20 "encoding/json" 21 "fmt" 22 "net" 23 "net/http" 24 "sync" 25 "time" 26 27 "go.uber.org/zap/buffer" 28 "go.uber.org/zap/zapcore" 29 ) 30 31 // An udsCore write entries to an UDS server with HTTP Post. Log messages will be encoded into a JSON array. 32 type udsCore struct { 33 client http.Client 34 minimumLevel zapcore.Level 35 url string 36 enc zapcore.Encoder 37 buffers []*buffer.Buffer 38 mu sync.Mutex 39 } 40 41 // teeToUDSServer returns a zapcore.Core that writes entries to both the provided core and to an uds server. 42 func teeToUDSServer(baseCore zapcore.Core, address, path string) zapcore.Core { 43 c := http.Client{ 44 Transport: &http.Transport{ 45 DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { 46 return net.Dial("unix", address) 47 }, 48 }, 49 Timeout: time.Second, 50 } 51 uc := &udsCore{ 52 client: c, 53 url: "http://unix" + path, 54 enc: zapcore.NewJSONEncoder(defaultEncoderConfig), 55 buffers: make([]*buffer.Buffer, 0), 56 } 57 for l := zapcore.DebugLevel; l <= zapcore.FatalLevel; l++ { 58 if baseCore.Enabled(l) { 59 uc.minimumLevel = l 60 break 61 } 62 } 63 return zapcore.NewTee(baseCore, uc) 64 } 65 66 // Enabled implements zapcore.Core. 67 func (u *udsCore) Enabled(l zapcore.Level) bool { 68 return l >= u.minimumLevel 69 } 70 71 // With implements zapcore.Core. 72 func (u *udsCore) With(fields []zapcore.Field) zapcore.Core { 73 return &udsCore{ 74 client: u.client, 75 minimumLevel: u.minimumLevel, 76 } 77 } 78 79 // Check implements zapcore.Core. 80 func (u *udsCore) Check(e zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry { 81 if u.Enabled(e.Level) { 82 return ce.AddCore(e, u) 83 } 84 return ce 85 } 86 87 // Sync implements zapcore.Core. It sends log messages with HTTP POST. 88 func (u *udsCore) Sync() error { 89 logs := u.logsFromBuffer() 90 msg, err := json.Marshal(logs) 91 if err != nil { 92 return fmt.Errorf("failed to sync uds log: %v", err) 93 } 94 resp, err := u.client.Post(u.url, "application/json", bytes.NewReader(msg)) 95 if err != nil { 96 return fmt.Errorf("failed to send logs to uds server %v: %v", u.url, err) 97 } 98 if resp.StatusCode != http.StatusOK { 99 return fmt.Errorf("uds server returns non-ok status %v: %v", u.url, resp.Status) 100 } 101 return nil 102 } 103 104 // Write implements zapcore.Core. Log messages will be temporarily buffered and sent to 105 // UDS server asyncrhonously. 106 func (u *udsCore) Write(entry zapcore.Entry, fields []zapcore.Field) error { 107 buffer, err := u.enc.EncodeEntry(entry, fields) 108 if err != nil { 109 return fmt.Errorf("failed to write log to uds logger: %v", err) 110 } 111 u.mu.Lock() 112 u.buffers = append(u.buffers, buffer) 113 u.mu.Unlock() 114 return nil 115 } 116 117 func (u *udsCore) logsFromBuffer() []string { 118 u.mu.Lock() 119 defer u.mu.Unlock() 120 logs := make([]string, 0, len(u.buffers)) 121 for _, b := range u.buffers { 122 logs = append(logs, b.String()) 123 b.Free() 124 } 125 u.buffers = make([]*buffer.Buffer, 0) 126 return logs 127 }