@@ -17,10 +17,15 @@ package jaegerexporter
1717import (
1818 "context"
1919 "fmt"
20+ "sync"
21+ "time"
2022
2123 jaegerproto "github.com/jaegertracing/jaeger/proto-gen/api_v2"
24+ "go.opencensus.io/stats"
25+ "go.opencensus.io/tag"
2226 "go.uber.org/zap"
2327 "google.golang.org/grpc"
28+ "google.golang.org/grpc/connectivity"
2429 "google.golang.org/grpc/metadata"
2530
2631 "go.opentelemetry.io/collector/component"
@@ -40,21 +45,30 @@ func newTraceExporter(cfg *Config, logger *zap.Logger) (component.TracesExporter
4045 return nil , err
4146 }
4247
43- client , err := grpc .Dial (cfg .GRPCClientSettings .Endpoint , opts ... )
48+ conn , err := grpc .Dial (cfg .GRPCClientSettings .Endpoint , opts ... )
4449 if err != nil {
4550 return nil , err
4651 }
4752
48- collectorServiceClient := jaegerproto .NewCollectorServiceClient (client )
53+ collectorServiceClient := jaegerproto .NewCollectorServiceClient (conn )
4954 s := & protoGRPCSender {
55+ name : cfg .NameVal ,
5056 logger : logger ,
5157 client : collectorServiceClient ,
5258 metadata : metadata .New (cfg .GRPCClientSettings .Headers ),
5359 waitForReady : cfg .WaitForReady ,
60+
61+ conn : conn ,
62+ connStateReporterInterval : time .Second ,
63+
64+ stopCh : make (chan (struct {})),
5465 }
66+ s .AddStateChangeCallback (s .onStateChange )
5567
5668 exp , err := exporterhelper .NewTraceExporter (
5769 cfg , logger , s .pushTraceData ,
70+ exporterhelper .WithStart (s .start ),
71+ exporterhelper .WithShutdown (s .shutdown ),
5872 exporterhelper .WithTimeout (cfg .TimeoutSettings ),
5973 exporterhelper .WithRetry (cfg .RetrySettings ),
6074 exporterhelper .WithQueue (cfg .QueueSettings ),
@@ -66,10 +80,22 @@ func newTraceExporter(cfg *Config, logger *zap.Logger) (component.TracesExporter
6680// protoGRPCSender forwards spans encoded in the jaeger proto
6781// format, to a grpc server.
6882type protoGRPCSender struct {
83+ name string
6984 logger * zap.Logger
7085 client jaegerproto.CollectorServiceClient
7186 metadata metadata.MD
7287 waitForReady bool
88+
89+ conn stateReporter
90+ connStateReporterInterval time.Duration
91+ stateChangeCallbacks []func (connectivity.State )
92+
93+ stopCh chan (struct {})
94+ stopWg sync.WaitGroup
95+ }
96+
97+ type stateReporter interface {
98+ GetState () connectivity.State
7399}
74100
75101func (s * protoGRPCSender ) pushTraceData (
@@ -100,3 +126,52 @@ func (s *protoGRPCSender) pushTraceData(
100126
101127 return 0 , nil
102128}
129+
130+ func (s * protoGRPCSender ) shutdown (context.Context ) error {
131+ close (s .stopCh )
132+ s .stopWg .Wait ()
133+ return nil
134+ }
135+
136+ func (s * protoGRPCSender ) start (context.Context , component.Host ) error {
137+ go s .startConnectionStatusReporter ()
138+ return nil
139+ }
140+
141+ func (s * protoGRPCSender ) startConnectionStatusReporter () {
142+ connState := s .conn .GetState ()
143+ s .propagateStateChange (connState )
144+
145+ ticker := time .NewTicker (s .connStateReporterInterval )
146+ for {
147+ select {
148+ case <- ticker .C :
149+ s .stopWg .Add (1 )
150+ st := s .conn .GetState ()
151+ if connState != st {
152+ // state has changed, report it
153+ connState = st
154+ s .propagateStateChange (st )
155+ }
156+ s .stopWg .Done ()
157+ case <- s .stopCh :
158+ return
159+ }
160+ }
161+ }
162+
163+ func (s * protoGRPCSender ) propagateStateChange (st connectivity.State ) {
164+ for _ , callback := range s .stateChangeCallbacks {
165+ callback (st )
166+ }
167+ }
168+
169+ func (s * protoGRPCSender ) onStateChange (st connectivity.State ) {
170+ mCtx , _ := tag .New (context .Background (), tag .Upsert (tag .MustNewKey ("exporter_name" ), s .name ))
171+ stats .Record (mCtx , mLastConnectionState .M (int64 (st )))
172+ s .logger .Info ("State of the connection with the Jaeger Collector backend" , zap .Stringer ("state" , st ))
173+ }
174+
175+ func (s * protoGRPCSender ) AddStateChangeCallback (f func (connectivity.State )) {
176+ s .stateChangeCallbacks = append (s .stateChangeCallbacks , f )
177+ }
0 commit comments