Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: unit test failures while using instana.InitCollector() API #1012

Merged
merged 19 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 35 additions & 22 deletions collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package instana

import (
"context"
"sync"

ot "github.com/opentracing/opentracing-go"
)
Expand All @@ -25,38 +26,50 @@ type Collector struct {

var _ TracerLogger = (*Collector)(nil)

var (
once sync.Once
muCollector sync.Mutex
)

// InitCollector creates a new [Collector]
func InitCollector(opts *Options) TracerLogger {

// if instana.C is already an instance of Collector, we just return
if _, ok := C.(*Collector); ok {
C.Warn("InitCollector was previously called. instana.C is reused")
return C
}
once.Do(func() {
if opts == nil {
opts = &Options{
Recorder: NewRecorder(),
}
}

if opts.Recorder == nil {
opts.Recorder = NewRecorder()
}

StartMetrics(opts)

if opts == nil {
opts = &Options{
Recorder: NewRecorder(),
tracer := &tracerS{
recorder: opts.Recorder,
}
}

if opts.Recorder == nil {
opts.Recorder = NewRecorder()
}
muCollector.Lock()
defer muCollector.Unlock()

StartMetrics(opts)
c = &Collector{
t: tracer,
LeveledLogger: defaultLogger,
Sensor: NewSensorWithTracer(tracer),
}

tracer := &tracerS{
recorder: opts.Recorder,
}
})

C = &Collector{
t: tracer,
LeveledLogger: defaultLogger,
Sensor: NewSensorWithTracer(tracer),
}
return c
Angith marked this conversation as resolved.
Show resolved Hide resolved
}

return C
// GetC return the instance of instana Collector
func GetC() TracerLogger {
Angith marked this conversation as resolved.
Show resolved Hide resolved
muCollector.Lock()
defer muCollector.Unlock()
return c
}

// Extract() returns a SpanContext instance given `format` and `carrier`. It matches [opentracing.Tracer.Extract].
Expand Down
43 changes: 22 additions & 21 deletions collector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,22 @@ import (
)

func Test_Collector_Noop(t *testing.T) {
assert.NotNil(t, instana.C, "instana.C should never be nil and be initialized as noop")
assert.NotNil(t, instana.GetC(), "instana collector should never be nil and be initialized as noop")

sc, err := instana.C.Extract(nil, nil)
sc, err := instana.GetC().Extract(nil, nil)
assert.Nil(t, sc)
assert.Error(t, err)
assert.Nil(t, instana.C.StartSpan(""))
assert.Nil(t, instana.C.LegacySensor())
assert.Nil(t, instana.GetC().StartSpan(""))
assert.Nil(t, instana.GetC().LegacySensor())
}

func Test_Collector_LegacySensor(t *testing.T) {
recorder := instana.NewTestRecorder()
c := instana.InitCollector(&instana.Options{AgentClient: alwaysReadyClient{}, Recorder: recorder})
s := c.LegacySensor()
defer instana.ShutdownSensor()
defer instana.ShutdownCollector()

assert.NotNil(t, instana.C.LegacySensor())
assert.NotNil(t, instana.GetC().LegacySensor())

h := instana.TracingHandlerFunc(s, "/{action}", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintln(w, "Ok")
Expand All @@ -41,30 +41,31 @@ func Test_Collector_LegacySensor(t *testing.T) {

h.ServeHTTP(httptest.NewRecorder(), req)

assert.Len(t, recorder.GetQueuedSpans(), 1, "Instrumentations should still work fine with instana.C.LegacySensor()")
assert.Len(t, recorder.GetQueuedSpans(), 1, "Instrumentations should still work fine with instana.GetC().LegacySensor()")
}

func Test_Collector_Singleton(t *testing.T) {
instana.C = nil
var ok bool
var instance instana.TracerLogger

_, ok = instana.C.(*instana.Collector)
assert.False(t, ok, "instana.C is noop before InitCollector is called")
defer instana.ShutdownCollector()

_, ok = instana.GetC().(*instana.Collector)
assert.False(t, ok, "instana collector is noop before InitCollector is called")

instana.InitCollector(instana.DefaultOptions())

instance, ok = instana.C.(*instana.Collector)
assert.True(t, ok, "instana.C is of type instana.Collector after InitCollector is called")
instance, ok = instana.GetC().(*instana.Collector)
assert.True(t, ok, "instana collector is of type instana.Collector after InitCollector is called")

instana.InitCollector(instana.DefaultOptions())

assert.Equal(t, instana.C, instance, "instana.C is singleton and should not be reassigned if InitCollector is called again")
assert.Equal(t, instana.GetC(), instance, "instana collector is singleton and should not be reassigned if InitCollector is called again")
}

func Test_Collector_EmbeddedTracer(t *testing.T) {
instana.C = nil
c := instana.InitCollector(nil)
defer instana.ShutdownCollector()

sp := c.StartSpan("my-span")

Expand Down Expand Up @@ -92,18 +93,18 @@ func Test_Collector_EmbeddedTracer(t *testing.T) {
}

func Test_Collector_Logger(t *testing.T) {
instana.C = nil
instana.InitCollector(nil)
defer instana.ShutdownCollector()

l := &mylogger{}

instana.C.SetLogger(l)
instana.GetC().SetLogger(l)

instana.C.Debug()
instana.C.Info()
instana.C.Warn()
instana.C.Error()
instana.C.Error()
instana.GetC().Debug()
instana.GetC().Info()
instana.GetC().Warn()
instana.GetC().Error()
instana.GetC().Error()

assert.Equal(t, 1, l.counter["debug"])
assert.Equal(t, 1, l.counter["info"])
Expand Down
5 changes: 3 additions & 2 deletions instrumentation_http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,17 @@ func BenchmarkTracingNamedHandlerFunc(b *testing.B) {
}

func TestTracingNamedHandlerFunc_Write(t *testing.T) {
recorder := instana.NewTestRecorder()
opts := &instana.Options{
Service: "go-sensor-test",
Tracer: instana.TracerOptions{
CollectableHTTPHeaders: []string{"x-custom-header-1", "x-custom-header-2"},
},
AgentClient: alwaysReadyClient{},
Recorder: recorder,
}

recorder := instana.NewTestRecorder()
s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(opts, recorder))
s := instana.InitCollector(opts)
defer instana.ShutdownSensor()

h := instana.TracingNamedHandlerFunc(s, "action", "/{action}", func(w http.ResponseWriter, req *http.Request) {
Expand Down
19 changes: 17 additions & 2 deletions sensor.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,11 @@ var (
muSensor sync.Mutex
binaryName = filepath.Base(os.Args[0])
processStartedAt = time.Now()
C TracerLogger
c TracerLogger
)

func init() {
C = newNoopCollector()
c = newNoopCollector()
}

func newSensor(options *Options) *sensorS {
Expand Down Expand Up @@ -276,6 +276,8 @@ func Flush(ctx context.Context) error {

// ShutdownSensor cleans up the internal global sensor reference. The next time that instana.InitSensor is called,
// directly or indirectly, the internal sensor will be reinitialized.
//
// Deprecated: Use [ShutdownCollector] instead.
func ShutdownSensor() {
muSensor.Lock()
Angith marked this conversation as resolved.
Show resolved Hide resolved
if sensor != nil {
Expand All @@ -284,6 +286,19 @@ func ShutdownSensor() {
muSensor.Unlock()
Angith marked this conversation as resolved.
Show resolved Hide resolved
}

// ShutdownCollector cleans up the collector and sensor reference.
// It will also reset the singleton as the next time that instana.InitCollector API is called,
// collector and sensor will be reinitialized.
func ShutdownCollector() {
muCollector.Lock()
defer muCollector.Unlock()
if sensor != nil {
sensor = nil
}
c = nil
once = sync.Once{}
}

func newServerlessAgent(serviceName, agentEndpoint, agentKey string,
client *http.Client, logger LeveledLogger) AgentClient {

Expand Down
Loading