go.temporal.io/server/common/nexus/trace.go

180 LOC · 5 covered · 175 uncovered · 1 ranges · 64 concepts · 1 introducers · 12 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 package nexus
2
3 import (
4 "crypto/tls"
5 "net/http/httptrace"
6 "time"
7
8 "go.temporal.io/server/common/dynamicconfig"
9 "go.temporal.io/server/common/log"
10 "go.temporal.io/server/common/log/tag"
11 )
12
13 type HTTPClientTraceProvider interface {
14 // NewTrace returns a *httptrace.ClientTrace which provides hooks to invoke at each point in the HTTP request
15 // lifecycle. This trace must be added to the HTTP request context using httptrace.WithClientTrace for the hooks to
16 // be invoked. The provided logger should already be tagged with relevant request information
17 // e.g. using log.With(logger, tag.RequestID(id), tag.Operation(op), ...).
18 NewTrace(attempt int32, logger log.Logger) *httptrace.ClientTrace
19 // NewForwardingTrace functions the same as NewTrace but forwarded requests do not have an associated attempt count,
20 // so all forwarded requests will be traced if enabled.
21 NewForwardingTrace(logger log.Logger) *httptrace.ClientTrace
22 }
23
24 // HTTPTraceConfig is the dynamic config for controlling Nexus HTTP request tracing behavior.
25 var HTTPTraceConfig = dynamicconfig.NewGlobalTypedSettingWithConverter(
26 "system.nexusHTTPTraceConfig",
27 convertHTTPClientTraceConfig,
28 defaultHTTPClientTraceConfig,
29 `Configuration options for controlling additional tracing for Nexus HTTP requests. Fields: Enabled, ForwardingEnabled, MinAttempt, MaxAttempt, Hooks. See HTTPClientTraceConfig comments for more detail.`,
30 )
31
32 type HTTPClientTraceConfig struct {
33 // Enabled controls whether any additional tracing will be invoked. Default false.
34 Enabled bool
35 // ForwardingEnabled controls whether any additional tracing will be invoked for forwarded requests. Default false. Forwarded requests do not have an attempt count, so MinAttempt and MaxAttempt are ignored for these requests.
36 ForwardingEnabled bool
37 // MinAttempt is the first operation attempt to include additional tracing. Default 2. Setting to 0 or 1 will add tracing to all requests and may be expensive.
38 MinAttempt int32
39 // MaxAttempt is the maximum operation attempt to include additional tracing. Default 2. Setting to 0 means no maximum.
40 MaxAttempt int32
41 // Hooks is the list of method names to invoke with extra tracing. See httptrace.ClientTrace for more detail.
42 // Defaults to all implemented hooks: GetConn, GotConn, ConnectStart, ConnectDone, DNSStart, DNSDone, TLSHandshakeStart, TLSHandshakeDone, WroteRequest, GotFirstResponseByte.
43 Hooks []string
44 }
45
46 var defaultHTTPClientTraceConfig = HTTPClientTraceConfig{
47 Enabled: false,
48 ForwardingEnabled: false,
49 MinAttempt: 2,
50 MaxAttempt: 2,
51 // use separate default for Hooks so that users can override with a smaller set of hooks
52 Hooks: nil,
53 }
54
55 var convertDefaultHTTPClientTraceConfig = dynamicconfig.ConvertStructure(defaultHTTPClientTraceConfig)
56
57 var defaultHTTPClientTraceHooks = []string{"GetConn", "GotConn", "ConnectStart", "ConnectDone", "DNSStart", "DNSDone", "TLSHandshakeStart", "TLSHandshakeDone", "WroteRequest", "GotFirstResponseByte"}
58
59 func convertHTTPClientTraceConfig(in any) (HTTPClientTraceConfig, error) {
60 cfg, err := convertDefaultHTTPClientTraceConfig(in)
61 if err != nil {
62 cfg = defaultHTTPClientTraceConfig
63 }
64 if len(cfg.Hooks) == 0 {
65 cfg.Hooks = defaultHTTPClientTraceHooks
66 }
67 return cfg, nil
68 }
69
70 type LoggedHTTPClientTraceProvider struct {
71 Config dynamicconfig.TypedPropertyFn[HTTPClientTraceConfig]
72 }
73
74 > func NewLoggedHTTPClientTraceProvider(dc *dynamicconfig.Collection) HTTPClientTraceProvider { fx.go ×44
75 > return &LoggedHTTPClientTraceProvider{
76 > Config: HTTPTraceConfig.Get(dc),
77 > }
78 > }
79
80 func (p *LoggedHTTPClientTraceProvider) NewTrace(attempt int32, logger log.Logger) *httptrace.ClientTrace {
81 config := p.Config()
82 if !config.Enabled {
83 return nil
84 }
85 if attempt < config.MinAttempt {
86 return nil
87 }
88 if config.MaxAttempt > 0 && attempt > config.MaxAttempt {
89 return nil
90 }
91
92 return p.newClientTrace(logger, config.Hooks)
93 }
94
95 func (p *LoggedHTTPClientTraceProvider) NewForwardingTrace(logger log.Logger) *httptrace.ClientTrace {
96 config := p.Config()
97 if !config.Enabled || !config.ForwardingEnabled {
98 return nil
99 }
100
101 return p.newClientTrace(logger, config.Hooks)
102 }
103
104 //nolint:revive // cognitive complexity (> 25 max) but is just adding a logging function for each method in the list.
105 func (p *LoggedHTTPClientTraceProvider) newClientTrace(logger log.Logger, hooks []string) *httptrace.ClientTrace {
106 clientTrace := &httptrace.ClientTrace{}
107 for _, h := range hooks {
108 switch h {
109 case "GetConn":
110 clientTrace.GetConn = func(hostPort string) {
111 logger.Info("attempting to get HTTP connection for Nexus request",
112 tag.Timestamp(time.Now().UTC()),
113 tag.Address(hostPort))
114 }
115 case "GotConn":
116 clientTrace.GotConn = func(info httptrace.GotConnInfo) {
117 logger.Info("got HTTP connection for Nexus request",
118 tag.Timestamp(time.Now().UTC()),
119 tag.Bool("reused", info.Reused),
120 tag.Bool("was-idle", info.WasIdle),
121 tag.Duration("idle-time", info.IdleTime))
122 }
123 case "ConnectStart":
124 clientTrace.ConnectStart = func(network, addr string) {
125 logger.Info("starting dial for new connection for Nexus request",
126 tag.Timestamp(time.Now().UTC()),
127 tag.Address(addr),
128 tag.String("network", network))
129 }
130 case "ConnectDone":
131 clientTrace.ConnectDone = func(network, addr string, err error) {
132 logger.Info("finished dial for new connection for Nexus request",
133 tag.Timestamp(time.Now().UTC()),
134 tag.Address(addr),
135 tag.String("network", network),
136 tag.Error(err))
137 }
138 case "DNSStart":
139 clientTrace.DNSStart = func(info httptrace.DNSStartInfo) {
140 logger.Info("starting DNS lookup for Nexus request",
141 tag.Timestamp(time.Now().UTC()),
142 tag.Host(info.Host))
143 }
144 case "DNSDone":
145 clientTrace.DNSDone = func(info httptrace.DNSDoneInfo) {
146 addresses := make([]string, len(info.Addrs))
147 for i, a := range info.Addrs {
148 addresses[i] = a.String()
149 }
150 logger.Info("finished DNS lookup for Nexus request",
151 tag.Timestamp(time.Now().UTC()),
152 tag.Addresses(addresses),
153 tag.Error(info.Err),
154 tag.Bool("coalesced", info.Coalesced))
155 }
156 case "TLSHandshakeStart":
157 clientTrace.TLSHandshakeStart = func() {
158 logger.Info("starting TLS handshake for Nexus request", tag.Timestamp(time.Now().UTC()))
159 }
160 case "TLSHandshakeDone":
161 clientTrace.TLSHandshakeDone = func(state tls.ConnectionState, err error) {
162 logger.Info("finished TLS handshake for Nexus request",
163 tag.Timestamp(time.Now().UTC()),
164 tag.Bool("handshake-complete", state.HandshakeComplete),
165 tag.Error(err))
166 }
167 case "WroteRequest":
168 clientTrace.WroteRequest = func(info httptrace.WroteRequestInfo) {
169 logger.Info("finished writing Nexus HTTP request",
170 tag.Timestamp(time.Now().UTC()),
171 tag.Error(info.Err))
172 }
173 case "GotFirstResponseByte":
174 clientTrace.GotFirstResponseByte = func() {
175 logger.Info("got response to Nexus HTTP request", tag.AttemptEnd(time.Now().UTC()))
176 }
177 }
178 }
179 return clientTrace
180 }