@@ -18,7 +18,6 @@ import (
1818 "runtime"
1919 "strconv"
2020 "strings"
21- "sync"
2221 "time"
2322 "unicode/utf8"
2423
@@ -318,6 +317,16 @@ type Params struct {
318317
319318 // PassiveHealthCheck defines the parameters for the healthy endpoints checker.
320319 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
321330}
322331
323332type (
@@ -387,34 +396,34 @@ type PriorityRoute interface {
387396// Proxy instances implement Skipper proxying functionality. For
388397// initializing, see the WithParams the constructor and Params.
389398type 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
418427}
419428
420429// proxyError is used to wrap errors during proxying and to indicate
@@ -801,13 +810,15 @@ func WithParams(p Params) *Proxy {
801810 endpointRegistry : p .EndpointRegistry ,
802811 }
803812 }
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+ }
811822 }
812823
813824 return & Proxy {
@@ -817,53 +828,82 @@ func WithParams(p Params) *Proxy {
817828 rnd : rand .New (loadbalancer .NewLockedSource ()),
818829 endpointRegistry : p .EndpointRegistry ,
819830 },
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 ,
844855 }
845856}
846857
847858func (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 )
859884 return
885+ } else {
886+ p .log .Infof ("FlightRecorder wrote %d bytes to trace file %q" , b .Len (), p .flightRecorderURL .Path )
860887 }
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 )
865892 }
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+ }
867907 }
868908}
869909
0 commit comments