@@ -18,7 +18,6 @@ import (
18
18
"runtime"
19
19
"strconv"
20
20
"strings"
21
- "sync"
22
21
"time"
23
22
"unicode/utf8"
24
23
@@ -318,6 +317,16 @@ type Params struct {
318
317
319
318
// PassiveHealthCheck defines the parameters for the healthy endpoints checker.
320
319
PassiveHealthCheck * PassiveHealthCheck
320
+
321
+ // FlightRecorder is a started instance of https://pkg.go.dev/golang.org/x/exp/trace#FlightRecorder
322
+ FlightRecorder * trace.FlightRecorder
323
+
324
+ // FlightRecorderTargetURL is the target to write the trace
325
+ // to. Supported targets are http URL and file URL.
326
+ FlightRecorderTargetURL string
327
+
328
+ // FlightRecorderProxyTookTooLong defines the threshold when to write out a trace
329
+ FlightRecorderProxyTookTooLong time.Duration
321
330
}
322
331
323
332
type (
@@ -387,34 +396,34 @@ type PriorityRoute interface {
387
396
// Proxy instances implement Skipper proxying functionality. For
388
397
// initializing, see the WithParams the constructor and Params.
389
398
type Proxy struct {
390
- experimentalUpgrade bool
391
- experimentalUpgradeAudit bool
392
- accessLogDisabled bool
393
- maxLoops int
394
- defaultHTTPStatus int
395
- routing * routing.Routing
396
- registry * routing.EndpointRegistry
397
- fadein * fadeIn
398
- heathlyEndpoints * healthyEndpoints
399
- roundTripper http.RoundTripper
400
- priorityRoutes []PriorityRoute
401
- flags Flags
402
- metrics metrics.Metrics
403
- quit chan struct {}
404
- flushInterval time.Duration
405
- breakers * circuit.Registry
406
- limiters * ratelimit.Registry
407
- log logging.Logger
408
- tracing * proxyTracing
409
- upgradeAuditLogOut io.Writer
410
- upgradeAuditLogErr io.Writer
411
- auditLogHook chan struct {}
412
- clientTLS * tls.Config
413
- hostname string
414
- onPanicSometimes rate.Sometimes
415
- flightRecorder * trace.FlightRecorder
416
- traceOnce sync. Once
417
- tooLong time.Duration
399
+ experimentalUpgrade bool
400
+ experimentalUpgradeAudit bool
401
+ accessLogDisabled bool
402
+ maxLoops int
403
+ defaultHTTPStatus int
404
+ routing * routing.Routing
405
+ registry * routing.EndpointRegistry
406
+ fadein * fadeIn
407
+ heathlyEndpoints * healthyEndpoints
408
+ roundTripper http.RoundTripper
409
+ priorityRoutes []PriorityRoute
410
+ flags Flags
411
+ metrics metrics.Metrics
412
+ quit chan struct {}
413
+ flushInterval time.Duration
414
+ breakers * circuit.Registry
415
+ limiters * ratelimit.Registry
416
+ log logging.Logger
417
+ tracing * proxyTracing
418
+ upgradeAuditLogOut io.Writer
419
+ upgradeAuditLogErr io.Writer
420
+ auditLogHook chan struct {}
421
+ clientTLS * tls.Config
422
+ hostname string
423
+ onPanicSometimes rate.Sometimes
424
+ flightRecorder * trace.FlightRecorder
425
+ flightRecorderURL * url. URL
426
+ flightRecorderProxyTookTooLong time.Duration
418
427
}
419
428
420
429
// proxyError is used to wrap errors during proxying and to indicate
@@ -801,13 +810,15 @@ func WithParams(p Params) *Proxy {
801
810
endpointRegistry : p .EndpointRegistry ,
802
811
}
803
812
}
804
- // TODO(sszuecs): expose an option to start it
805
- fr := trace .NewFlightRecorder ()
806
- //fr.SetPeriod(d)
807
- //fr.SetSize(bytes int)
808
- err := fr .Start ()
809
- if err != nil {
810
- println ("Failed to start FlightRecorder:" , err .Error ())
813
+
814
+ var frURL * url.URL
815
+ if p .FlightRecorder != nil {
816
+ var err error
817
+ frURL , err = url .Parse (p .FlightRecorderTargetURL )
818
+ if err != nil {
819
+ p .FlightRecorder .Stop ()
820
+ p .FlightRecorder = nil
821
+ }
811
822
}
812
823
813
824
return & Proxy {
@@ -817,53 +828,82 @@ func WithParams(p Params) *Proxy {
817
828
rnd : rand .New (loadbalancer .NewLockedSource ()),
818
829
endpointRegistry : p .EndpointRegistry ,
819
830
},
820
- heathlyEndpoints : healthyEndpointsChooser ,
821
- roundTripper : p .CustomHttpRoundTripperWrap (tr ),
822
- priorityRoutes : p .PriorityRoutes ,
823
- flags : p .Flags ,
824
- metrics : m ,
825
- quit : quit ,
826
- flushInterval : p .FlushInterval ,
827
- experimentalUpgrade : p .ExperimentalUpgrade ,
828
- experimentalUpgradeAudit : p .ExperimentalUpgradeAudit ,
829
- maxLoops : p .MaxLoopbacks ,
830
- breakers : p .CircuitBreakers ,
831
- limiters : p .RateLimiters ,
832
- log : & logging.DefaultLog {},
833
- defaultHTTPStatus : defaultHTTPStatus ,
834
- tracing : newProxyTracing (p .OpenTracing ),
835
- accessLogDisabled : p .AccessLogDisabled ,
836
- upgradeAuditLogOut : os .Stdout ,
837
- upgradeAuditLogErr : os .Stderr ,
838
- clientTLS : tr .TLSClientConfig ,
839
- hostname : hostname ,
840
- onPanicSometimes : rate.Sometimes {First : 3 , Interval : 1 * time .Minute },
841
- flightRecorder : fr ,
842
- traceOnce : sync. Once {} ,
843
- tooLong : 250 * time . Millisecond ,
831
+ heathlyEndpoints : healthyEndpointsChooser ,
832
+ roundTripper : p .CustomHttpRoundTripperWrap (tr ),
833
+ priorityRoutes : p .PriorityRoutes ,
834
+ flags : p .Flags ,
835
+ metrics : m ,
836
+ quit : quit ,
837
+ flushInterval : p .FlushInterval ,
838
+ experimentalUpgrade : p .ExperimentalUpgrade ,
839
+ experimentalUpgradeAudit : p .ExperimentalUpgradeAudit ,
840
+ maxLoops : p .MaxLoopbacks ,
841
+ breakers : p .CircuitBreakers ,
842
+ limiters : p .RateLimiters ,
843
+ log : & logging.DefaultLog {},
844
+ defaultHTTPStatus : defaultHTTPStatus ,
845
+ tracing : newProxyTracing (p .OpenTracing ),
846
+ accessLogDisabled : p .AccessLogDisabled ,
847
+ upgradeAuditLogOut : os .Stdout ,
848
+ upgradeAuditLogErr : os .Stderr ,
849
+ clientTLS : tr .TLSClientConfig ,
850
+ hostname : hostname ,
851
+ onPanicSometimes : rate.Sometimes {First : 3 , Interval : 1 * time .Minute },
852
+ flightRecorder : p . FlightRecorder ,
853
+ flightRecorderURL : frURL ,
854
+ flightRecorderProxyTookTooLong : p . FlightRecorderProxyTookTooLong ,
844
855
}
845
856
}
846
857
847
858
func (p * Proxy ) writeTraceIfTooSlow (ctx * context ) {
848
- p .log .Infof ("write trace if too slow: %s > %s" , time .Since (ctx .startServe ), p .tooLong )
849
- if time .Since (ctx .startServe ) > p .tooLong {
850
- p .log .Info ("too slow" )
851
- // Do it only once for simplicitly, but you can take more than one.
852
- p .traceOnce .Do (func () {
853
- p .log .Info ("write trace because we were too slow" )
854
- // Grab the snapshot.
855
- var b bytes.Buffer
856
- _ , err := p .flightRecorder .WriteTo (& b )
857
- if err != nil {
858
- p .log .Errorf ("Failed to write flightrecorder data: %v" , err )
859
+ if p .flightRecorder == nil || p .flightRecorderURL == nil {
860
+ return
861
+ }
862
+
863
+ d := p .flightRecorderProxyTookTooLong
864
+ if e , ok := ctx .StateBag ()[filters .TraceName ]; ok {
865
+ d = e .(time.Duration )
866
+ }
867
+ if d < 1 * time .Microsecond {
868
+ return
869
+ }
870
+
871
+ p .log .Infof ("write trace if too slow: %s > %s" , time .Since (ctx .startServe ), d )
872
+ if time .Since (ctx .startServe ) > d {
873
+ var b bytes.Buffer
874
+ _ , err := p .flightRecorder .WriteTo (& b )
875
+ if err != nil {
876
+ p .log .Errorf ("Failed to write flightrecorder data: %v" , err )
877
+ return
878
+ }
879
+
880
+ switch p .flightRecorderURL .Scheme {
881
+ case "file" :
882
+ if err := os .WriteFile (p .flightRecorderURL .Path , b .Bytes (), 0o644 ); err != nil {
883
+ p .log .Errorf ("Failed to write file trace.out: %v" , err )
859
884
return
885
+ } else {
886
+ p .log .Infof ("FlightRecorder wrote %d bytes to trace file %q" , b .Len (), p .flightRecorderURL .Path )
860
887
}
861
- // Write it to a file.
862
- if err := os . WriteFile ( "trace.out " , b . Bytes (), 0o755 ); err != nil {
863
- p . log . Errorf ( "Failed to write trace.out: %v" , err )
864
- return
888
+ case "http" , "https" :
889
+ req , err := http . NewRequest ( "PUT " , p . flightRecorderURL . String (), & b )
890
+ if err != nil {
891
+ p . log . Errorf ( "Failed to create request to %q to send a trace: %v" , p . flightRecorderURL . String (), err )
865
892
}
866
- })
893
+
894
+ rsp , err := http .DefaultClient .Do (req )
895
+ if err != nil {
896
+ p .log .Errorf ("Failed to write trace to %q: %v" , p .flightRecorderURL .String (), err )
897
+ }
898
+ switch rsp .StatusCode {
899
+ case 200 , 201 , 204 :
900
+ p .log .Infof ("Successful send of a trace to %q" , p .flightRecorderURL .String ())
901
+ default :
902
+ p .log .Errorf ("Failed to get successful response from %s: (%d) %s" , p .flightRecorderURL .String (), rsp .StatusCode , rsp .Status )
903
+ }
904
+ default :
905
+ p .log .Errorf ("Failed to write trace, unknown FlightRecorderURL %q" , p .flightRecorderURL .Scheme )
906
+ }
867
907
}
868
908
}
869
909
0 commit comments