gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/grpc/internal/binarylog/method_logger.go (about) 1 /* 2 * 3 * Copyright 2018 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package binarylog 20 21 import ( 22 "google.golang.org/protobuf/types/known/durationpb" 23 "google.golang.org/protobuf/types/known/timestamppb" 24 "net" 25 "strings" 26 "sync/atomic" 27 "time" 28 29 pb "gitee.com/ks-custle/core-gm/grpc/binarylog/grpc_binarylog_v1" 30 "gitee.com/ks-custle/core-gm/grpc/metadata" 31 "gitee.com/ks-custle/core-gm/grpc/status" 32 "github.com/golang/protobuf/proto" 33 ) 34 35 type callIDGenerator struct { 36 id uint64 37 } 38 39 func (g *callIDGenerator) next() uint64 { 40 id := atomic.AddUint64(&g.id, 1) 41 return id 42 } 43 44 // reset is for testing only, and doesn't need to be thread safe. 45 func (g *callIDGenerator) reset() { 46 g.id = 0 47 } 48 49 var idGen callIDGenerator 50 51 // MethodLogger is the sub-logger for each method. 52 type MethodLogger struct { 53 headerMaxLen, messageMaxLen uint64 54 55 callID uint64 56 idWithinCallGen *callIDGenerator 57 58 sink Sink // TODO(blog): make this plugable. 59 } 60 61 func newMethodLogger(h, m uint64) *MethodLogger { 62 return &MethodLogger{ 63 headerMaxLen: h, 64 messageMaxLen: m, 65 66 callID: idGen.next(), 67 idWithinCallGen: &callIDGenerator{}, 68 69 sink: DefaultSink, // TODO(blog): make it plugable. 70 } 71 } 72 73 // Log creates a proto binary log entry, and logs it to the sink. 74 func (ml *MethodLogger) Log(c LogEntryConfig) { 75 m := c.toProto() 76 // ptypes.TimestampProto is deprecated, call the timestamppb.New function instead. 77 //timestamp, _ := ptypes.TimestampProto(time.Now()) 78 timestamp := timestamppb.New(time.Now()) 79 m.Timestamp = timestamp 80 m.CallId = ml.callID 81 m.SequenceIdWithinCall = ml.idWithinCallGen.next() 82 83 switch pay := m.Payload.(type) { 84 case *pb.GrpcLogEntry_ClientHeader: 85 m.PayloadTruncated = ml.truncateMetadata(pay.ClientHeader.GetMetadata()) 86 case *pb.GrpcLogEntry_ServerHeader: 87 m.PayloadTruncated = ml.truncateMetadata(pay.ServerHeader.GetMetadata()) 88 case *pb.GrpcLogEntry_Message: 89 m.PayloadTruncated = ml.truncateMessage(pay.Message) 90 } 91 92 _ = ml.sink.Write(m) 93 } 94 95 func (ml *MethodLogger) truncateMetadata(mdPb *pb.Metadata) (truncated bool) { 96 if ml.headerMaxLen == maxUInt { 97 return false 98 } 99 var ( 100 bytesLimit = ml.headerMaxLen 101 index int 102 ) 103 // At the end of the loop, index will be the first entry where the total 104 // size is greater than the limit: 105 // 106 // len(entry[:index]) <= ml.hdr && len(entry[:index+1]) > ml.hdr. 107 for ; index < len(mdPb.Entry); index++ { 108 entry := mdPb.Entry[index] 109 if entry.Key == "grpc-trace-bin" { 110 // "grpc-trace-bin" is a special key. It's kept in the log entry, 111 // but not counted towards the size limit. 112 continue 113 } 114 currentEntryLen := uint64(len(entry.Value)) 115 if currentEntryLen > bytesLimit { 116 break 117 } 118 bytesLimit -= currentEntryLen 119 } 120 truncated = index < len(mdPb.Entry) 121 mdPb.Entry = mdPb.Entry[:index] 122 return truncated 123 } 124 125 func (ml *MethodLogger) truncateMessage(msgPb *pb.Message) (truncated bool) { 126 if ml.messageMaxLen == maxUInt { 127 return false 128 } 129 if ml.messageMaxLen >= uint64(len(msgPb.Data)) { 130 return false 131 } 132 msgPb.Data = msgPb.Data[:ml.messageMaxLen] 133 return true 134 } 135 136 // LogEntryConfig represents the configuration for binary log entry. 137 type LogEntryConfig interface { 138 toProto() *pb.GrpcLogEntry 139 } 140 141 // ClientHeader configs the binary log entry to be a ClientHeader entry. 142 type ClientHeader struct { 143 OnClientSide bool 144 Header metadata.MD 145 MethodName string 146 Authority string 147 Timeout time.Duration 148 // PeerAddr is required only when it's on server side. 149 PeerAddr net.Addr 150 } 151 152 func (c *ClientHeader) toProto() *pb.GrpcLogEntry { 153 // This function doesn't need to set all the fields (e.g. seq ID). The Log 154 // function will set the fields when necessary. 155 clientHeader := &pb.ClientHeader{ 156 Metadata: mdToMetadataProto(c.Header), 157 MethodName: c.MethodName, 158 Authority: c.Authority, 159 } 160 if c.Timeout > 0 { 161 // ptypes.DurationProto is deprecated, call the durationpb.New function instead. 162 //clientHeader.Timeout = ptypes.DurationProto(c.Timeout) 163 clientHeader.Timeout = durationpb.New(c.Timeout) 164 } 165 ret := &pb.GrpcLogEntry{ 166 Type: pb.GrpcLogEntry_EVENT_TYPE_CLIENT_HEADER, 167 Payload: &pb.GrpcLogEntry_ClientHeader{ 168 ClientHeader: clientHeader, 169 }, 170 } 171 if c.OnClientSide { 172 ret.Logger = pb.GrpcLogEntry_LOGGER_CLIENT 173 } else { 174 ret.Logger = pb.GrpcLogEntry_LOGGER_SERVER 175 } 176 if c.PeerAddr != nil { 177 ret.Peer = addrToProto(c.PeerAddr) 178 } 179 return ret 180 } 181 182 // ServerHeader configs the binary log entry to be a ServerHeader entry. 183 type ServerHeader struct { 184 OnClientSide bool 185 Header metadata.MD 186 // PeerAddr is required only when it's on client side. 187 PeerAddr net.Addr 188 } 189 190 func (c *ServerHeader) toProto() *pb.GrpcLogEntry { 191 ret := &pb.GrpcLogEntry{ 192 Type: pb.GrpcLogEntry_EVENT_TYPE_SERVER_HEADER, 193 Payload: &pb.GrpcLogEntry_ServerHeader{ 194 ServerHeader: &pb.ServerHeader{ 195 Metadata: mdToMetadataProto(c.Header), 196 }, 197 }, 198 } 199 if c.OnClientSide { 200 ret.Logger = pb.GrpcLogEntry_LOGGER_CLIENT 201 } else { 202 ret.Logger = pb.GrpcLogEntry_LOGGER_SERVER 203 } 204 if c.PeerAddr != nil { 205 ret.Peer = addrToProto(c.PeerAddr) 206 } 207 return ret 208 } 209 210 // ClientMessage configs the binary log entry to be a ClientMessage entry. 211 type ClientMessage struct { 212 OnClientSide bool 213 // Message can be a proto.Message or []byte. Other messages formats are not 214 // supported. 215 Message interface{} 216 } 217 218 func (c *ClientMessage) toProto() *pb.GrpcLogEntry { 219 var ( 220 data []byte 221 err error 222 ) 223 if m, ok := c.Message.(proto.Message); ok { 224 data, err = proto.Marshal(m) 225 if err != nil { 226 grpclogLogger.Infof("binarylogging: failed to marshal proto message: %v", err) 227 } 228 } else if b, ok := c.Message.([]byte); ok { 229 data = b 230 } else { 231 grpclogLogger.Infof("binarylogging: message to log is neither proto.message nor []byte") 232 } 233 ret := &pb.GrpcLogEntry{ 234 Type: pb.GrpcLogEntry_EVENT_TYPE_CLIENT_MESSAGE, 235 Payload: &pb.GrpcLogEntry_Message{ 236 Message: &pb.Message{ 237 Length: uint32(len(data)), 238 Data: data, 239 }, 240 }, 241 } 242 if c.OnClientSide { 243 ret.Logger = pb.GrpcLogEntry_LOGGER_CLIENT 244 } else { 245 ret.Logger = pb.GrpcLogEntry_LOGGER_SERVER 246 } 247 return ret 248 } 249 250 // ServerMessage configs the binary log entry to be a ServerMessage entry. 251 type ServerMessage struct { 252 OnClientSide bool 253 // Message can be a proto.Message or []byte. Other messages formats are not 254 // supported. 255 Message interface{} 256 } 257 258 func (c *ServerMessage) toProto() *pb.GrpcLogEntry { 259 var ( 260 data []byte 261 err error 262 ) 263 if m, ok := c.Message.(proto.Message); ok { 264 data, err = proto.Marshal(m) 265 if err != nil { 266 grpclogLogger.Infof("binarylogging: failed to marshal proto message: %v", err) 267 } 268 } else if b, ok := c.Message.([]byte); ok { 269 data = b 270 } else { 271 grpclogLogger.Infof("binarylogging: message to log is neither proto.message nor []byte") 272 } 273 ret := &pb.GrpcLogEntry{ 274 Type: pb.GrpcLogEntry_EVENT_TYPE_SERVER_MESSAGE, 275 Payload: &pb.GrpcLogEntry_Message{ 276 Message: &pb.Message{ 277 Length: uint32(len(data)), 278 Data: data, 279 }, 280 }, 281 } 282 if c.OnClientSide { 283 ret.Logger = pb.GrpcLogEntry_LOGGER_CLIENT 284 } else { 285 ret.Logger = pb.GrpcLogEntry_LOGGER_SERVER 286 } 287 return ret 288 } 289 290 // ClientHalfClose configs the binary log entry to be a ClientHalfClose entry. 291 type ClientHalfClose struct { 292 OnClientSide bool 293 } 294 295 func (c *ClientHalfClose) toProto() *pb.GrpcLogEntry { 296 ret := &pb.GrpcLogEntry{ 297 Type: pb.GrpcLogEntry_EVENT_TYPE_CLIENT_HALF_CLOSE, 298 Payload: nil, // No payload here. 299 } 300 if c.OnClientSide { 301 ret.Logger = pb.GrpcLogEntry_LOGGER_CLIENT 302 } else { 303 ret.Logger = pb.GrpcLogEntry_LOGGER_SERVER 304 } 305 return ret 306 } 307 308 // ServerTrailer configs the binary log entry to be a ServerTrailer entry. 309 type ServerTrailer struct { 310 OnClientSide bool 311 Trailer metadata.MD 312 // Err is the status error. 313 Err error 314 // PeerAddr is required only when it's on client side and the RPC is trailer 315 // only. 316 PeerAddr net.Addr 317 } 318 319 func (c *ServerTrailer) toProto() *pb.GrpcLogEntry { 320 st, ok := status.FromError(c.Err) 321 if !ok { 322 grpclogLogger.Info("binarylogging: error in trailer is not a status error") 323 } 324 var ( 325 detailsBytes []byte 326 err error 327 ) 328 stProto := st.Proto() 329 if stProto != nil && len(stProto.Details) != 0 { 330 detailsBytes, err = proto.Marshal(stProto) 331 if err != nil { 332 grpclogLogger.Infof("binarylogging: failed to marshal status proto: %v", err) 333 } 334 } 335 ret := &pb.GrpcLogEntry{ 336 Type: pb.GrpcLogEntry_EVENT_TYPE_SERVER_TRAILER, 337 Payload: &pb.GrpcLogEntry_Trailer{ 338 Trailer: &pb.Trailer{ 339 Metadata: mdToMetadataProto(c.Trailer), 340 StatusCode: uint32(st.Code()), 341 StatusMessage: st.Message(), 342 StatusDetails: detailsBytes, 343 }, 344 }, 345 } 346 if c.OnClientSide { 347 ret.Logger = pb.GrpcLogEntry_LOGGER_CLIENT 348 } else { 349 ret.Logger = pb.GrpcLogEntry_LOGGER_SERVER 350 } 351 if c.PeerAddr != nil { 352 ret.Peer = addrToProto(c.PeerAddr) 353 } 354 return ret 355 } 356 357 // Cancel configs the binary log entry to be a Cancel entry. 358 type Cancel struct { 359 OnClientSide bool 360 } 361 362 func (c *Cancel) toProto() *pb.GrpcLogEntry { 363 ret := &pb.GrpcLogEntry{ 364 Type: pb.GrpcLogEntry_EVENT_TYPE_CANCEL, 365 Payload: nil, 366 } 367 if c.OnClientSide { 368 ret.Logger = pb.GrpcLogEntry_LOGGER_CLIENT 369 } else { 370 ret.Logger = pb.GrpcLogEntry_LOGGER_SERVER 371 } 372 return ret 373 } 374 375 // metadataKeyOmit returns whether the metadata entry with this key should be 376 // omitted. 377 func metadataKeyOmit(key string) bool { 378 switch key { 379 case "lb-token", ":path", ":authority", "content-encoding", "content-type", "user-agent", "te": 380 return true 381 case "grpc-trace-bin": // grpc-trace-bin is special because it's visiable to users. 382 return false 383 } 384 return strings.HasPrefix(key, "grpc-") 385 } 386 387 func mdToMetadataProto(md metadata.MD) *pb.Metadata { 388 ret := &pb.Metadata{} 389 for k, vv := range md { 390 if metadataKeyOmit(k) { 391 continue 392 } 393 for _, v := range vv { 394 ret.Entry = append(ret.Entry, 395 &pb.MetadataEntry{ 396 Key: k, 397 Value: []byte(v), 398 }, 399 ) 400 } 401 } 402 return ret 403 } 404 405 func addrToProto(addr net.Addr) *pb.Address { 406 ret := &pb.Address{} 407 switch a := addr.(type) { 408 case *net.TCPAddr: 409 if a.IP.To4() != nil { 410 ret.Type = pb.Address_TYPE_IPV4 411 } else if a.IP.To16() != nil { 412 ret.Type = pb.Address_TYPE_IPV6 413 } else { 414 ret.Type = pb.Address_TYPE_UNKNOWN 415 // Do not set address and port fields. 416 break 417 } 418 ret.Address = a.IP.String() 419 ret.IpPort = uint32(a.Port) 420 case *net.UnixAddr: 421 ret.Type = pb.Address_TYPE_UNIX 422 ret.Address = a.String() 423 default: 424 ret.Type = pb.Address_TYPE_UNKNOWN 425 } 426 return ret 427 }