@@ -36,10 +36,10 @@ var bufferPool = sync.Pool{New: func() interface{} {
36
36
return & bytes.Buffer {}
37
37
}}
38
38
39
- type ApmServerTransportStatusType string
40
-
41
39
// Constants for the state of the transport used in
42
40
// the backoff implementation.
41
+ type ApmServerTransportStatusType string
42
+
43
43
const (
44
44
Failing ApmServerTransportStatusType = "Failing"
45
45
Pending ApmServerTransportStatusType = "Pending"
@@ -48,34 +48,45 @@ const (
48
48
49
49
// A struct to track the state and status of sending
50
50
// to the APM server. Used in the backoff implementation.
51
- type ApmServerTransportStateType struct {
51
+ type ApmServerTransport struct {
52
+ sync.Pool
52
53
sync.Mutex
54
+ ctx context.Context
55
+ config * extensionConfig
56
+ DataChannel chan AgentData
57
+ Client * http.Client
53
58
Status ApmServerTransportStatusType
54
59
ReconnectionCount int
55
60
GracePeriodTimer * time.Timer
56
61
}
57
62
58
- // The status of transport to the APM server.
59
- //
60
- // This instance of the ApmServerTransportStateType is public for use in tests.
61
- var ApmServerTransportState = ApmServerTransportStateType {
62
- Status : Healthy ,
63
- ReconnectionCount : - 1 ,
63
+ func InitApmServerTransport (ctx context.Context , config * extensionConfig ) * ApmServerTransport {
64
+ var transport ApmServerTransport
65
+ transport .DataChannel = make (chan AgentData , 100 )
66
+ transport .Client = & http.Client {
67
+ Timeout : time .Duration (config .DataForwarderTimeoutSeconds ) * time .Second ,
68
+ Transport : http .DefaultTransport .(* http.Transport ).Clone (),
69
+ }
70
+ transport .config = config
71
+ transport .ctx = ctx
72
+ transport .Status = Healthy
73
+ transport .ReconnectionCount = - 1
74
+ return & transport
64
75
}
65
76
66
- func StartBackgroundSending (ctx context. Context , agentDataChannel chan AgentData , client * http. Client , config * extensionConfig , funcDone chan struct {}, backgroundDataSendWg * sync.WaitGroup ) {
77
+ func StartBackgroundSending (transport * ApmServerTransport , funcDone chan struct {}, backgroundDataSendWg * sync.WaitGroup ) {
67
78
go func () {
68
79
defer backgroundDataSendWg .Done ()
69
- if ! IsTransportStatusHealthyOrPending () {
80
+ if transport . Status == Failing {
70
81
return
71
82
}
72
83
for {
73
84
select {
74
85
case <- funcDone :
75
86
Log .Debug ("Received signal that function has completed, not processing any more agent data" )
76
87
return
77
- case agentData := <- agentDataChannel :
78
- if err := PostToApmServer (client , agentData , config , ctx ); err != nil {
88
+ case agentData := <- transport . DataChannel :
89
+ if err := PostToApmServer (transport , agentData ); err != nil {
79
90
Log .Errorf ("Error sending to APM server, skipping: %v" , err )
80
91
return
81
92
}
@@ -89,10 +100,10 @@ func StartBackgroundSending(ctx context.Context, agentDataChannel chan AgentData
89
100
// The function compresses the APM agent data, if it's not already compressed.
90
101
// It sets the APM transport status to failing upon errors, as part of the backoff
91
102
// strategy.
92
- func PostToApmServer (client * http. Client , agentData AgentData , config * extensionConfig , ctx context. Context ) error {
103
+ func PostToApmServer (transport * ApmServerTransport , agentData AgentData ) error {
93
104
// todo: can this be a streaming or streaming style call that keeps the
94
105
// connection open across invocations?
95
- if ! IsTransportStatusHealthyOrPending () {
106
+ if transport . Status == Failing {
96
107
return errors .New ("transport status is unhealthy" )
97
108
}
98
109
@@ -122,89 +133,81 @@ func PostToApmServer(client *http.Client, agentData AgentData, config *extension
122
133
r = buf
123
134
}
124
135
125
- req , err := http .NewRequest ("POST" , config .apmServerUrl + endpointURI , r )
136
+ req , err := http .NewRequest ("POST" , transport . config .apmServerUrl + endpointURI , r )
126
137
if err != nil {
127
138
return fmt .Errorf ("failed to create a new request when posting to APM server: %v" , err )
128
139
}
129
140
req .Header .Add ("Content-Encoding" , encoding )
130
141
req .Header .Add ("Content-Type" , "application/x-ndjson" )
131
- if config .apmServerApiKey != "" {
132
- req .Header .Add ("Authorization" , "ApiKey " + config .apmServerApiKey )
133
- } else if config .apmServerSecretToken != "" {
134
- req .Header .Add ("Authorization" , "Bearer " + config .apmServerSecretToken )
142
+ if transport . config .apmServerApiKey != "" {
143
+ req .Header .Add ("Authorization" , "ApiKey " + transport . config .apmServerApiKey )
144
+ } else if transport . config .apmServerSecretToken != "" {
145
+ req .Header .Add ("Authorization" , "Bearer " + transport . config .apmServerSecretToken )
135
146
}
136
147
137
148
Log .Debug ("Sending data chunk to APM Server" )
138
- resp , err := client .Do (req )
149
+ resp , err := transport . Client .Do (req )
139
150
if err != nil {
140
- SetApmServerTransportState (Failing , ctx )
151
+ SetApmServerTransportState (transport , Failing )
141
152
return fmt .Errorf ("failed to post to APM server: %v" , err )
142
153
}
143
154
144
155
//Read the response body
145
156
defer resp .Body .Close ()
146
157
body , err := ioutil .ReadAll (resp .Body )
147
158
if err != nil {
148
- SetApmServerTransportState (Failing , ctx )
159
+ SetApmServerTransportState (transport , Failing )
149
160
return fmt .Errorf ("failed to read the response body after posting to the APM server" )
150
161
}
151
162
152
- SetApmServerTransportState (Healthy , ctx )
163
+ SetApmServerTransportState (transport , Healthy )
153
164
Log .Debug ("Transport status set to healthy" )
154
165
Log .Debugf ("APM server response body: %v" , string (body ))
155
166
Log .Debugf ("APM server response status code: %v" , resp .StatusCode )
156
167
return nil
157
168
}
158
169
159
- // IsTransportStatusHealthyOrPending returns true if the APM server transport status is
160
- // healthy or pending, and false otherwise.
161
- //
162
- // This function is public for use in tests.
163
- func IsTransportStatusHealthyOrPending () bool {
164
- return ApmServerTransportState .Status != Failing
165
- }
166
-
167
170
// SetApmServerTransportState takes a state of the APM server transport and updates
168
171
// the current state of the transport. For a change to a failing state, the grace period
169
172
// is calculated and a go routine is started that waits for that period to complete
170
173
// before changing the status to "pending". This would allow a subsequent send attempt
171
174
// to the APM server.
172
175
//
173
176
// This function is public for use in tests.
174
- func SetApmServerTransportState (status ApmServerTransportStatusType , ctx context. Context ) {
177
+ func SetApmServerTransportState (transport * ApmServerTransport , status ApmServerTransportStatusType ) {
175
178
switch status {
176
179
case Healthy :
177
- ApmServerTransportState .Lock ()
178
- ApmServerTransportState .Status = status
179
- Log .Debugf ("APM Server Transport status set to %s" , status )
180
- ApmServerTransportState .ReconnectionCount = - 1
181
- ApmServerTransportState .Unlock ()
180
+ transport .Lock ()
181
+ transport .Status = status
182
+ Log .Debugf ("APM Server Transport status set to %s" , transport . Status )
183
+ transport .ReconnectionCount = - 1
184
+ transport .Unlock ()
182
185
case Failing :
183
- ApmServerTransportState .Lock ()
184
- ApmServerTransportState .Status = status
185
- Log .Debugf ("APM Server Transport status set to %s" , status )
186
- ApmServerTransportState .ReconnectionCount ++
187
- ApmServerTransportState .GracePeriodTimer = time .NewTimer (computeGracePeriod ())
188
- Log .Debugf ("Grace period entered, reconnection count : %d" , ApmServerTransportState .ReconnectionCount )
186
+ transport .Lock ()
187
+ transport .Status = status
188
+ Log .Debugf ("APM Server Transport status set to %s" , transport . Status )
189
+ transport .ReconnectionCount ++
190
+ transport .GracePeriodTimer = time .NewTimer (computeGracePeriod (transport ))
191
+ Log .Debugf ("Grace period entered, reconnection count : %d" , transport .ReconnectionCount )
189
192
go func () {
190
193
select {
191
- case <- ApmServerTransportState .GracePeriodTimer .C :
194
+ case <- transport .GracePeriodTimer .C :
192
195
Log .Debug ("Grace period over - timer timed out" )
193
- case <- ctx .Done ():
196
+ case <- transport . ctx .Done ():
194
197
Log .Debug ("Grace period over - context done" )
195
198
}
196
- ApmServerTransportState .Status = Pending
197
- Log .Debugf ("APM Server Transport status set to %s" , status )
198
- ApmServerTransportState .Unlock ()
199
+ transport .Status = Pending
200
+ Log .Debugf ("APM Server Transport status set to %s" , transport . Status )
201
+ transport .Unlock ()
199
202
}()
200
203
default :
201
204
Log .Errorf ("Cannot set APM Server Transport status to %s" , status )
202
205
}
203
206
}
204
207
205
208
// ComputeGracePeriod https://github.com/elastic/apm/blob/main/specs/agents/transport.md#transport-errors
206
- func computeGracePeriod () time.Duration {
207
- gracePeriodWithoutJitter := math .Pow (math .Min (float64 (ApmServerTransportState .ReconnectionCount ), 6 ), 2 )
209
+ func computeGracePeriod (transport * ApmServerTransport ) time.Duration {
210
+ gracePeriodWithoutJitter := math .Pow (math .Min (float64 (transport .ReconnectionCount ), 6 ), 2 )
208
211
jitter := rand .Float64 ()/ 5 - 0.1
209
212
return time .Duration ((gracePeriodWithoutJitter + jitter * gracePeriodWithoutJitter ) * float64 (time .Second ))
210
213
}
0 commit comments