github.com/TeaOSLab/EdgeNode@v1.3.8/internal/nodes/http_access_log_queue.go (about) 1 package nodes 2 3 import ( 4 "bytes" 5 "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" 6 "github.com/TeaOSLab/EdgeNode/internal/goman" 7 "github.com/TeaOSLab/EdgeNode/internal/remotelogs" 8 "github.com/TeaOSLab/EdgeNode/internal/rpc" 9 "github.com/TeaOSLab/EdgeNode/internal/utils" 10 memutils "github.com/TeaOSLab/EdgeNode/internal/utils/mem" 11 "google.golang.org/grpc/codes" 12 "google.golang.org/grpc/status" 13 "strings" 14 "time" 15 "unicode/utf8" 16 ) 17 18 var sharedHTTPAccessLogQueue = NewHTTPAccessLogQueue() 19 20 // HTTPAccessLogQueue HTTP访问日志队列 21 type HTTPAccessLogQueue struct { 22 queue chan *pb.HTTPAccessLog 23 24 rpcClient *rpc.RPCClient 25 } 26 27 // NewHTTPAccessLogQueue 获取新对象 28 func NewHTTPAccessLogQueue() *HTTPAccessLogQueue { 29 // 队列中最大的值,超出此数量的访问日志会被丢弃 30 var maxSize = 2_000 * (1 + memutils.SystemMemoryGB()/2) 31 if maxSize > 20_000 { 32 maxSize = 20_000 33 } 34 35 var queue = &HTTPAccessLogQueue{ 36 queue: make(chan *pb.HTTPAccessLog, maxSize), 37 } 38 goman.New(func() { 39 queue.Start() 40 }) 41 42 return queue 43 } 44 45 // Start 开始处理访问日志 46 func (this *HTTPAccessLogQueue) Start() { 47 ticker := time.NewTicker(1 * time.Second) 48 for range ticker.C { 49 err := this.loop() 50 if err != nil { 51 if rpc.IsConnError(err) { 52 remotelogs.Debug("ACCESS_LOG_QUEUE", err.Error()) 53 } else { 54 remotelogs.Error("ACCESS_LOG_QUEUE", err.Error()) 55 } 56 } 57 } 58 } 59 60 // Push 加入新访问日志 61 func (this *HTTPAccessLogQueue) Push(accessLog *pb.HTTPAccessLog) { 62 select { 63 case this.queue <- accessLog: 64 default: 65 66 } 67 } 68 69 // 上传访问日志 70 func (this *HTTPAccessLogQueue) loop() error { 71 const maxLen = 2000 72 var accessLogs = make([]*pb.HTTPAccessLog, 0, maxLen) 73 var count = 0 74 75 Loop: 76 for { 77 select { 78 case accessLog := <-this.queue: 79 accessLogs = append(accessLogs, accessLog) 80 count++ 81 82 // 每次只提交 N 条访问日志,防止网络拥堵 83 if count >= maxLen { 84 break Loop 85 } 86 default: 87 break Loop 88 } 89 } 90 91 if len(accessLogs) == 0 { 92 return nil 93 } 94 95 // 发送到本地 96 if sharedHTTPAccessLogViewer.HasConns() { 97 for _, accessLog := range accessLogs { 98 sharedHTTPAccessLogViewer.Send(accessLog) 99 } 100 } 101 102 // 发送到API 103 if this.rpcClient == nil { 104 client, err := rpc.SharedRPC() 105 if err != nil { 106 return err 107 } 108 this.rpcClient = client 109 } 110 111 _, err := this.rpcClient.HTTPAccessLogRPC.CreateHTTPAccessLogs(this.rpcClient.Context(), &pb.CreateHTTPAccessLogsRequest{HttpAccessLogs: accessLogs}) 112 if err != nil { 113 // 是否包含了invalid UTF-8 114 if strings.Contains(err.Error(), "string field contains invalid UTF-8") { 115 for _, accessLog := range accessLogs { 116 this.ToValidUTF8(accessLog) 117 } 118 119 // 重新提交 120 _, err = this.rpcClient.HTTPAccessLogRPC.CreateHTTPAccessLogs(this.rpcClient.Context(), &pb.CreateHTTPAccessLogsRequest{HttpAccessLogs: accessLogs}) 121 return err 122 } 123 124 // 是否请求内容过大 125 statusCode, ok := status.FromError(err) 126 if ok && statusCode.Code() == codes.ResourceExhausted { 127 // 去除Body 128 for _, accessLog := range accessLogs { 129 accessLog.RequestBody = nil 130 } 131 132 // 重新提交 133 _, err = this.rpcClient.HTTPAccessLogRPC.CreateHTTPAccessLogs(this.rpcClient.Context(), &pb.CreateHTTPAccessLogsRequest{HttpAccessLogs: accessLogs}) 134 return err 135 } 136 137 return err 138 } 139 140 return nil 141 } 142 143 // ToValidUTF8 处理访问日志中的非UTF-8字节 144 func (this *HTTPAccessLogQueue) ToValidUTF8(accessLog *pb.HTTPAccessLog) { 145 accessLog.RemoteAddr = utils.ToValidUTF8string(accessLog.RemoteAddr) 146 accessLog.RemoteUser = utils.ToValidUTF8string(accessLog.RemoteUser) 147 accessLog.RequestURI = utils.ToValidUTF8string(accessLog.RequestURI) 148 accessLog.RequestPath = utils.ToValidUTF8string(accessLog.RequestPath) 149 accessLog.RequestFilename = utils.ToValidUTF8string(accessLog.RequestFilename) 150 accessLog.RequestBody = bytes.ToValidUTF8(accessLog.RequestBody, []byte{}) 151 accessLog.Host = utils.ToValidUTF8string(accessLog.Host) 152 accessLog.Hostname = utils.ToValidUTF8string(accessLog.Hostname) 153 154 for k, v := range accessLog.SentHeader { 155 if !utf8.ValidString(k) { 156 delete(accessLog.SentHeader, k) 157 continue 158 } 159 160 for index, s := range v.Values { 161 v.Values[index] = utils.ToValidUTF8string(s) 162 } 163 } 164 165 accessLog.Referer = utils.ToValidUTF8string(accessLog.Referer) 166 accessLog.UserAgent = utils.ToValidUTF8string(accessLog.UserAgent) 167 accessLog.Request = utils.ToValidUTF8string(accessLog.Request) 168 accessLog.ContentType = utils.ToValidUTF8string(accessLog.ContentType) 169 170 for k, c := range accessLog.Cookie { 171 if !utf8.ValidString(k) { 172 delete(accessLog.Cookie, k) 173 continue 174 } 175 accessLog.Cookie[k] = utils.ToValidUTF8string(c) 176 } 177 178 accessLog.Args = utils.ToValidUTF8string(accessLog.Args) 179 accessLog.QueryString = utils.ToValidUTF8string(accessLog.QueryString) 180 181 for k, v := range accessLog.Header { 182 if !utf8.ValidString(k) { 183 delete(accessLog.Header, k) 184 continue 185 } 186 for index, s := range v.Values { 187 v.Values[index] = utils.ToValidUTF8string(s) 188 } 189 } 190 191 for k, v := range accessLog.Errors { 192 accessLog.Errors[k] = utils.ToValidUTF8string(v) 193 } 194 }