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  }