Skip to content

Commit 44970f8

Browse files
authored
Introducing the instana.Collector (#414)
* Introduced SensorLogger interface with methods: Tracer() ot.Tracer, Logger() LeveledLogger, SetLogger(l LeveledLogger) * Introduced TracerLogger interface. It embeds instana.Tracer instana.LeveledLogger and instana.SensorLogger * instana.Tracer interface added StartSpanWithOptions method to match tracerS * Introduced instana.Collector that implements TracerLogger * instana.Sensor now implements TracerLogger * New Option Recorder
1 parent d93d72c commit 44970f8

File tree

10 files changed

+445
-6
lines changed

10 files changed

+445
-6
lines changed

adapters.go

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ import (
1414
otlog "github.com/opentracing/opentracing-go/log"
1515
)
1616

17+
var _ TracerLogger = (*Sensor)(nil)
18+
19+
type SensorLogger interface {
20+
Tracer() ot.Tracer
21+
Logger() LeveledLogger
22+
SetLogger(l LeveledLogger)
23+
}
24+
1725
// SpanSensitiveFunc is a function executed within a span context
1826
//
1927
// Deprecated: use instana.ContextWithSpan() and instana.SpanFromContext() to inject and retrieve spans
@@ -32,6 +40,8 @@ type Tracer interface {
3240
Options() TracerOptions
3341
// Flush sends all finished spans to the agent
3442
Flush(context.Context) error
43+
// StartSpanWithOptions starts a span with the given options and return the span reference
44+
StartSpanWithOptions(string, ot.StartSpanOptions) ot.Span
3545
}
3646

3747
// Sensor is used to inject tracing information into requests
@@ -40,7 +50,7 @@ type Sensor struct {
4050
logger LeveledLogger
4151
}
4252

43-
// NewSensor creates a new instana.Sensor
53+
// NewSensor creates a new [Sensor]
4454
func NewSensor(serviceName string) *Sensor {
4555
return NewSensorWithTracer(NewTracerWithOptions(
4656
&Options{
@@ -49,7 +59,7 @@ func NewSensor(serviceName string) *Sensor {
4959
))
5060
}
5161

52-
// NewSensorWithTracer returns a new instana.Sensor that uses provided tracer to report spans
62+
// NewSensorWithTracer returns a new [Sensor] that uses provided tracer to report spans
5363
func NewSensorWithTracer(tracer ot.Tracer) *Sensor {
5464
return &Sensor{
5565
tracer: tracer,
@@ -168,3 +178,88 @@ func (s *Sensor) WithTracingContext(name string, w http.ResponseWriter, req *htt
168178
f(span, ContextWithSpan(req.Context(), span))
169179
})
170180
}
181+
182+
// Compliance with TracerLogger
183+
184+
// Extract() returns a SpanContext instance given `format` and `carrier`. It matches [opentracing.Tracer.Extract].
185+
func (s *Sensor) Extract(format interface{}, carrier interface{}) (ot.SpanContext, error) {
186+
return s.tracer.Extract(format, carrier)
187+
}
188+
189+
// Inject() takes the `sm` SpanContext instance and injects it for
190+
// propagation within `carrier`. The actual type of `carrier` depends on
191+
// the value of `format`. It matches [opentracing.Tracer.Inject]
192+
func (s *Sensor) Inject(sm ot.SpanContext, format interface{}, carrier interface{}) error {
193+
return s.tracer.Inject(sm, format, carrier)
194+
}
195+
196+
// Create, start, and return a new Span with the given `operationName` and
197+
// incorporate the given StartSpanOption `opts`. (Note that `opts` borrows
198+
// from the "functional options" pattern, per
199+
// http://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis)
200+
//
201+
// It matches [opentracing.Tracer.StartSpan].
202+
func (s *Sensor) StartSpan(operationName string, opts ...ot.StartSpanOption) ot.Span {
203+
return s.tracer.StartSpan(operationName, opts...)
204+
}
205+
206+
// StartSpanWithOptions creates and starts a span by setting Instana relevant data within the span.
207+
// It matches [instana.Tracer.StartSpanWithOptions].
208+
func (s *Sensor) StartSpanWithOptions(operationName string, opts ot.StartSpanOptions) ot.Span {
209+
if t, ok := s.tracer.(Tracer); ok {
210+
return t.StartSpanWithOptions(operationName, opts)
211+
}
212+
213+
s.logger.Warn("Sensor.StartSpanWithOptions() not implemented by interface: ", s.tracer, " - returning nil")
214+
215+
return nil
216+
}
217+
218+
// Options gets the current tracer options
219+
// It matches [instana.Tracer.Options].
220+
func (s *Sensor) Options() TracerOptions {
221+
if t, ok := s.tracer.(Tracer); ok {
222+
return t.Options()
223+
}
224+
225+
s.logger.Warn("Sensor.Options() not implemented by interface: ", s.tracer, " - returning DefaultTracerOptions()")
226+
227+
return DefaultTracerOptions()
228+
}
229+
230+
// Flush sends all finished spans to the agent
231+
// It matches [instana.Tracer.Flush].
232+
func (s *Sensor) Flush(ctx context.Context) error {
233+
if t, ok := s.tracer.(Tracer); ok {
234+
return t.Flush(ctx)
235+
}
236+
237+
s.logger.Warn("Sensor.Flush() not implemented by interface: ", s.tracer, " - returning nil")
238+
239+
return nil
240+
}
241+
242+
// Debug logs a debug message by calling [LeveledLogger] underneath
243+
func (s *Sensor) Debug(v ...interface{}) {
244+
s.logger.Debug(v...)
245+
}
246+
247+
// Info logs an info message by calling [LeveledLogger] underneath
248+
func (s *Sensor) Info(v ...interface{}) {
249+
s.logger.Info(v...)
250+
}
251+
252+
// Warn logs a warning message by calling [LeveledLogger] underneath
253+
func (s *Sensor) Warn(v ...interface{}) {
254+
s.logger.Warn(v...)
255+
}
256+
257+
// Error logs a error message by calling [LeveledLogger] underneath
258+
func (s *Sensor) Error(v ...interface{}) {
259+
s.logger.Error(v...)
260+
}
261+
262+
// LegacySensor returns a reference to [Sensor].
263+
func (s *Sensor) LegacySensor() *Sensor {
264+
return s
265+
}

agent_communicator.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ func (a *agentCommunicator) sendDataToAgent(suffix string, data interface{}) err
199199
if resp != nil {
200200
respCode := resp.StatusCode
201201
if respCode < 200 || respCode >= 300 {
202-
a.l.Debug("Sending data to agent: response code: ", resp.StatusCode, "-", resp.Status)
202+
a.l.Debug("Sending data to agent: response code: ", resp.StatusCode, "-", resp.Status, "; ", url)
203203
}
204204

205205
io.CopyN(ioutil.Discard, resp.Body, 256<<10)

azure_agent_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ func TestAzureAgent_SpanDetails(t *testing.T) {
106106
"functionname": "testfunction",
107107
"triggername": "HTTP",
108108
"runtime": "custom"
109-
} }`, string(spans[0]["data"]))
109+
}, "service": "instana.test"}`, string(spans[0]["data"]))
110110
}
111111

112112
func setupAzureFunctionEnv() func() {

collector.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// (c) Copyright IBM Corp. 2023
2+
3+
package instana
4+
5+
import (
6+
"context"
7+
8+
ot "github.com/opentracing/opentracing-go"
9+
)
10+
11+
// TracerLogger represents the Instana Go collector and is composed by a tracer, a logger and a reference to the legacy sensor.
12+
type TracerLogger interface {
13+
Tracer
14+
LeveledLogger
15+
LegacySensor() *Sensor
16+
SensorLogger
17+
}
18+
19+
// Collector is used to inject tracing information into requests
20+
type Collector struct {
21+
t Tracer
22+
LeveledLogger
23+
*Sensor
24+
}
25+
26+
var _ TracerLogger = (*Collector)(nil)
27+
28+
// InitCollector creates a new [Collector]
29+
func InitCollector(opts *Options) TracerLogger {
30+
31+
// if instana.C is already an instance of Collector, we just return
32+
if _, ok := C.(*Collector); ok {
33+
C.Warn("InitCollector was previously called. instana.C is reused")
34+
return C
35+
}
36+
37+
if opts == nil {
38+
opts = &Options{
39+
Recorder: NewRecorder(),
40+
}
41+
}
42+
43+
if opts.Recorder == nil {
44+
opts.Recorder = NewRecorder()
45+
}
46+
47+
StartMetrics(opts)
48+
49+
tracer := &tracerS{
50+
recorder: opts.Recorder,
51+
}
52+
53+
C = &Collector{
54+
t: tracer,
55+
LeveledLogger: defaultLogger,
56+
Sensor: NewSensorWithTracer(tracer),
57+
}
58+
59+
return C
60+
}
61+
62+
// Extract() returns a SpanContext instance given `format` and `carrier`. It matches [opentracing.Tracer.Extract].
63+
func (c *Collector) Extract(format interface{}, carrier interface{}) (ot.SpanContext, error) {
64+
return c.t.Extract(format, carrier)
65+
}
66+
67+
// Inject() takes the `sm` SpanContext instance and injects it for
68+
// propagation within `carrier`. The actual type of `carrier` depends on
69+
// the value of `format`. It matches [opentracing.Tracer.Inject]
70+
func (c *Collector) Inject(sm ot.SpanContext, format interface{}, carrier interface{}) error {
71+
return c.t.Inject(sm, format, carrier)
72+
}
73+
74+
// Create, start, and return a new Span with the given `operationName` and
75+
// incorporate the given StartSpanOption `opts`. (Note that `opts` borrows
76+
// from the "functional options" pattern, per
77+
// http://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis)
78+
//
79+
// It matches [opentracing.Tracer.StartSpan].
80+
func (c *Collector) StartSpan(operationName string, opts ...ot.StartSpanOption) ot.Span {
81+
return c.t.StartSpan(operationName, opts...)
82+
}
83+
84+
// StartSpanWithOptions creates and starts a span by setting Instana relevant data within the span.
85+
// It matches [instana.Tracer.StartSpanWithOptions].
86+
func (c *Collector) StartSpanWithOptions(operationName string, opts ot.StartSpanOptions) ot.Span {
87+
return c.t.StartSpanWithOptions(operationName, opts)
88+
}
89+
90+
// Options gets the current tracer options
91+
// It matches [instana.Tracer.Options].
92+
func (c *Collector) Options() TracerOptions {
93+
return c.t.Options()
94+
}
95+
96+
// Flush sends all finished spans to the agent
97+
// It matches [instana.Tracer.Flush].
98+
func (c *Collector) Flush(ctx context.Context) error {
99+
return c.t.Flush(ctx)
100+
}
101+
102+
// Debug logs a debug message by calling [LeveledLogger] underneath
103+
func (c *Collector) Debug(v ...interface{}) {
104+
c.LeveledLogger.Debug(v...)
105+
}
106+
107+
// Info logs an info message by calling [LeveledLogger] underneath
108+
func (c *Collector) Info(v ...interface{}) {
109+
c.LeveledLogger.Info(v...)
110+
}
111+
112+
// Warn logs a warning message by calling [LeveledLogger] underneath
113+
func (c *Collector) Warn(v ...interface{}) {
114+
c.LeveledLogger.Warn(v...)
115+
}
116+
117+
// Error logs a error message by calling [LeveledLogger] underneath
118+
func (c *Collector) Error(v ...interface{}) {
119+
c.LeveledLogger.Error(v...)
120+
}
121+
122+
// LegacySensor returns a reference to [Sensor] that can be used for old instrumentations that still require it.
123+
//
124+
// Example:
125+
//
126+
// // Instrumenting HTTP incoming calls
127+
// c := instana.InitCollector("my-service")
128+
// http.HandleFunc("/", instana.TracingNamedHandlerFunc(c.LegacySensor(), "", "/{name}", handle))
129+
func (c *Collector) LegacySensor() *Sensor {
130+
return c.Sensor
131+
}

collector_noop.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// (c) Copyright IBM Corp. 2023
2+
3+
package instana
4+
5+
import (
6+
"context"
7+
"errors"
8+
9+
ot "github.com/opentracing/opentracing-go"
10+
)
11+
12+
var (
13+
_ TracerLogger = (*noopCollector)(nil)
14+
noopCollectorErrMsg string = "collector not initialized. make sure to initialize the Collector. eg: instana.InitCollector"
15+
noopCollectorErr error = errors.New(noopCollectorErrMsg)
16+
)
17+
18+
type noopCollector struct {
19+
l LeveledLogger
20+
}
21+
22+
func newNoopCollector() TracerLogger {
23+
c := &noopCollector{
24+
l: defaultLogger,
25+
}
26+
return c
27+
}
28+
29+
func (c *noopCollector) Extract(format interface{}, carrier interface{}) (ot.SpanContext, error) {
30+
c.l.Error(noopCollectorErrMsg)
31+
return nil, noopCollectorErr
32+
}
33+
34+
func (c *noopCollector) Inject(sm ot.SpanContext, format interface{}, carrier interface{}) error {
35+
c.l.Error(noopCollectorErrMsg)
36+
return noopCollectorErr
37+
}
38+
39+
func (c *noopCollector) StartSpan(operationName string, opts ...ot.StartSpanOption) ot.Span {
40+
c.l.Error(noopCollectorErrMsg)
41+
return nil
42+
}
43+
44+
func (c *noopCollector) StartSpanWithOptions(operationName string, opts ot.StartSpanOptions) ot.Span {
45+
c.l.Error(noopCollectorErrMsg)
46+
return nil
47+
}
48+
49+
func (c *noopCollector) Options() TracerOptions {
50+
return TracerOptions{}
51+
}
52+
53+
func (c *noopCollector) Flush(ctx context.Context) error {
54+
return noopCollectorErr
55+
}
56+
57+
func (c *noopCollector) Debug(v ...interface{}) {
58+
c.l.Error(noopCollectorErrMsg)
59+
}
60+
61+
func (c *noopCollector) Info(v ...interface{}) {
62+
c.l.Error(noopCollectorErrMsg)
63+
}
64+
65+
func (c *noopCollector) Warn(v ...interface{}) {
66+
c.l.Error(noopCollectorErrMsg)
67+
}
68+
69+
func (c *noopCollector) Error(v ...interface{}) {
70+
c.l.Error(noopCollectorErrMsg)
71+
}
72+
73+
func (c *noopCollector) LegacySensor() *Sensor {
74+
c.l.Error(noopCollectorErrMsg)
75+
return nil
76+
}
77+
78+
func (c *noopCollector) Tracer() ot.Tracer {
79+
c.l.Error(noopCollectorErrMsg)
80+
return nil
81+
}
82+
83+
func (c *noopCollector) Logger() LeveledLogger {
84+
c.l.Error(noopCollectorErrMsg)
85+
return nil
86+
}
87+
88+
// SetLogger sets the logger
89+
func (c *noopCollector) SetLogger(l LeveledLogger) {}

0 commit comments

Comments
 (0)