77 "fmt"
88 "io"
99 "strings"
10+ "time"
1011
1112 "github.com/go-logfmt/logfmt"
1213 smmmodel "github.com/grafana/synthetic-monitoring-agent/internal/model"
@@ -147,7 +148,7 @@ var (
147148 ErrFromRunner = errors .New ("runner reported an error" )
148149)
149150
150- func (r Processor ) Run (ctx context.Context , registry * prometheus.Registry , logger logger.Logger , internalLogger zerolog.Logger , secretStore SecretStore ) (bool , error ) {
151+ func (r Processor ) Run (ctx context.Context , registry * prometheus.Registry , logger logger.Logger , internalLogger zerolog.Logger , secretStore SecretStore ) (bool , time. Duration , error ) {
151152 k6runner := r .runner .WithLogger (& internalLogger )
152153
153154 // TODO: This error message is okay to be Debug for local k6 execution, but should be Error for remote runners.
@@ -156,15 +157,15 @@ func (r Processor) Run(ctx context.Context, registry *prometheus.Registry, logge
156157 internalLogger .Debug ().
157158 Err (err ).
158159 Msg ("k6 script exited with error code" )
159- return false , err
160+ return false , 0 , err
160161 }
161162
162163 // If only one of Error and ErrorCode are non-empty, the proxy is misbehaving.
163164 switch {
164165 case result .Error == "" && result .ErrorCode != "" :
165166 fallthrough
166167 case result .Error != "" && result .ErrorCode == "" :
167- return false , fmt .Errorf (
168+ return false , 0 , fmt .Errorf (
168169 "%w: only one of error (%q) and errorCode (%q) is non-empty" ,
169170 ErrBuggyRunner , result .Error , result .ErrorCode ,
170171 )
@@ -194,40 +195,41 @@ func (r Processor) Run(ctx context.Context, registry *prometheus.Registry, logge
194195 internalLogger .Debug ().
195196 Err (err ).
196197 Msg ("cannot load logs to logger" )
197- return false , err
198+ return false , 0 , err
198199 }
199200
200201 var (
201- collector sampleCollector
202- resultCollector checkResultCollector
202+ collector sampleCollector
203+ resultCollector checkResultCollector
204+ durationCollector probeDurationCollector
203205 )
204206
205- if err := extractMetricSamples (result .Metrics , internalLogger , collector .process , resultCollector .process ); err != nil {
207+ if err := extractMetricSamples (result .Metrics , internalLogger , collector .process , resultCollector .process , durationCollector . process ); err != nil {
206208 internalLogger .Debug ().
207209 Err (err ).
208210 Msg ("cannot extract metric samples" )
209- return false , err
211+ return false , 0 , err
210212 }
211213
212214 if err := registry .Register (& collector .collector ); err != nil {
213215 internalLogger .Error ().
214216 Err (err ).
215217 Msg ("cannot register collector" )
216- return false , err
218+ return false , 0 , err
217219 }
218220
219221 // https://github.com/grafana/sm-k6-runner/blob/b811839d444a7e69fd056b0a4e6ccf7e914197f3/internal/mq/runner.go#L51
220222 switch result .ErrorCode {
221223 case "" :
222224 // No error, all good.
223- return true , nil
225+ return true , durationCollector . duration , nil
224226 // TODO: Remove "user" from this list, which has been renamed to "aborted".
225227 case "timeout" , "killed" , "user" , "failed" , "aborted" :
226228 // These are user errors. The probe failed, but we don't return an error.
227- return false , nil
229+ return false , durationCollector . duration , nil
228230 default :
229231 // We got an "unknown" error, or some other code we do not recognize. Return it so we log it.
230- return false , fmt .Errorf ("%w: %s: %s" , ErrFromRunner , result .ErrorCode , result .Error )
232+ return false , durationCollector . duration , fmt .Errorf ("%w: %s: %s" , ErrFromRunner , result .ErrorCode , result .Error )
231233 }
232234}
233235
@@ -302,6 +304,21 @@ func (rc *checkResultCollector) process(mf *dto.MetricFamily, sample *model.Samp
302304 return nil
303305}
304306
307+ type probeDurationCollector struct {
308+ duration time.Duration
309+ }
310+
311+ func (dc * probeDurationCollector ) process (_ * dto.MetricFamily , sample * model.Sample ) error {
312+ if sample .Metric [model .MetricNameLabel ] != "probe_script_duration_seconds" {
313+ return nil
314+ }
315+
316+ // TODO: Is there a better way to convert from float seconds to time?
317+ dc .duration = time .Duration (sample .Value * 1000 ) * time .Millisecond // This truncates to the nearest millisecond.
318+
319+ return nil
320+ }
321+
305322func extractMetricSamples (metrics []byte , logger zerolog.Logger , processors ... sampleProcessorFunc ) error {
306323 promDecoder := expfmt .NewDecoder (bytes .NewBuffer (metrics ), expfmt .NewFormat (expfmt .TypeTextPlain ))
307324 decoderOpts := expfmt.DecodeOptions {Timestamp : model .Now ()}
0 commit comments