trace.go

  1/*
  2 *
  3 * Copyright 2015 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
 19package grpc
 20
 21import (
 22	"bytes"
 23	"fmt"
 24	"io"
 25	"net"
 26	"strings"
 27	"sync"
 28	"time"
 29)
 30
 31// EnableTracing controls whether to trace RPCs using the golang.org/x/net/trace package.
 32// This should only be set before any RPCs are sent or received by this program.
 33var EnableTracing bool
 34
 35// methodFamily returns the trace family for the given method.
 36// It turns "/pkg.Service/GetFoo" into "pkg.Service".
 37func methodFamily(m string) string {
 38	m = strings.TrimPrefix(m, "/") // remove leading slash
 39	if i := strings.Index(m, "/"); i >= 0 {
 40		m = m[:i] // remove everything from second slash
 41	}
 42	return m
 43}
 44
 45// traceEventLog mirrors golang.org/x/net/trace.EventLog.
 46//
 47// It exists in order to avoid importing x/net/trace on grpcnotrace builds.
 48type traceEventLog interface {
 49	Printf(format string, a ...any)
 50	Errorf(format string, a ...any)
 51	Finish()
 52}
 53
 54// traceLog mirrors golang.org/x/net/trace.Trace.
 55//
 56// It exists in order to avoid importing x/net/trace on grpcnotrace builds.
 57type traceLog interface {
 58	LazyLog(x fmt.Stringer, sensitive bool)
 59	LazyPrintf(format string, a ...any)
 60	SetError()
 61	SetRecycler(f func(any))
 62	SetTraceInfo(traceID, spanID uint64)
 63	SetMaxEvents(m int)
 64	Finish()
 65}
 66
 67// traceInfo contains tracing information for an RPC.
 68type traceInfo struct {
 69	tr        traceLog
 70	firstLine firstLine
 71}
 72
 73// firstLine is the first line of an RPC trace.
 74// It may be mutated after construction; remoteAddr specifically may change
 75// during client-side use.
 76type firstLine struct {
 77	mu         sync.Mutex
 78	client     bool // whether this is a client (outgoing) RPC
 79	remoteAddr net.Addr
 80	deadline   time.Duration // may be zero
 81}
 82
 83func (f *firstLine) SetRemoteAddr(addr net.Addr) {
 84	f.mu.Lock()
 85	f.remoteAddr = addr
 86	f.mu.Unlock()
 87}
 88
 89func (f *firstLine) String() string {
 90	f.mu.Lock()
 91	defer f.mu.Unlock()
 92
 93	var line bytes.Buffer
 94	io.WriteString(&line, "RPC: ")
 95	if f.client {
 96		io.WriteString(&line, "to")
 97	} else {
 98		io.WriteString(&line, "from")
 99	}
100	fmt.Fprintf(&line, " %v deadline:", f.remoteAddr)
101	if f.deadline != 0 {
102		fmt.Fprint(&line, f.deadline)
103	} else {
104		io.WriteString(&line, "none")
105	}
106	return line.String()
107}
108
109const truncateSize = 100
110
111func truncate(x string, l int) string {
112	if l > len(x) {
113		return x
114	}
115	return x[:l]
116}
117
118// payload represents an RPC request or response payload.
119type payload struct {
120	sent bool // whether this is an outgoing payload
121	msg  any  // e.g. a proto.Message
122	// TODO(dsymonds): add stringifying info to codec, and limit how much we hold here?
123}
124
125func (p payload) String() string {
126	if p.sent {
127		return truncate(fmt.Sprintf("sent: %v", p.msg), truncateSize)
128	}
129	return truncate(fmt.Sprintf("recv: %v", p.msg), truncateSize)
130}
131
132type fmtStringer struct {
133	format string
134	a      []any
135}
136
137func (f *fmtStringer) String() string {
138	return fmt.Sprintf(f.format, f.a...)
139}
140
141type stringer string
142
143func (s stringer) String() string { return string(s) }