Skip to content

Commit 3497570

Browse files
authored
Fix panic in newrelicexporter if service name is empty (#969)
Resolves #964 Validate the Node and ServiceInfo are not nil pointers before dereferencing them. Additionally, add tests to prevent regression of this bug.
1 parent 3cc0d37 commit 3497570

File tree

3 files changed

+129
-26
lines changed

3 files changed

+129
-26
lines changed

exporter/newrelicexporter/newrelic.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,14 @@ func (e exporter) pushTraceData(ctx context.Context, td pdata.Traces) (int, erro
8484

8585
octds := internaldata.TraceDataToOC(td)
8686
for _, octd := range octds {
87+
88+
var srv string
89+
if octd.Node != nil && octd.Node.ServiceInfo != nil {
90+
srv = octd.Node.ServiceInfo.Name
91+
}
92+
8793
transform := &transformer{
88-
ServiceName: octd.Node.ServiceInfo.Name,
94+
ServiceName: srv,
8995
Resource: octd.Resource,
9096
}
9197

@@ -115,9 +121,14 @@ func (e exporter) pushMetricData(ctx context.Context, md pdata.Metrics) (int, er
115121

116122
ocmds := internaldata.MetricsToOC(md)
117123
for _, ocmd := range ocmds {
124+
var srv string
125+
if ocmd.Node != nil && ocmd.Node.ServiceInfo != nil {
126+
srv = ocmd.Node.ServiceInfo.Name
127+
}
128+
118129
transform := &transformer{
119130
DeltaCalculator: e.deltaCalculator,
120-
ServiceName: ocmd.Node.ServiceInfo.Name,
131+
ServiceName: srv,
121132
Resource: ocmd.Resource,
122133
}
123134

exporter/newrelicexporter/newrelic_test.go

Lines changed: 113 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,52 @@ func TestLogWriter(t *testing.T) {
5050
assert.Len(t, messages, 2)
5151
}
5252

53-
func TestExportTraceData(t *testing.T) {
53+
func testTraceData(t *testing.T, expected []Span, td consumerdata.TraceData) {
5454
ctx, cancel := context.WithCancel(context.Background())
5555
defer cancel()
5656

57-
m := &Mock{make([]Data, 0, 3)}
57+
m := &Mock{make([]Data, 0, 1)}
5858
srv := m.Server()
5959
defer srv.Close()
6060

61+
f := NewFactory()
62+
c := f.CreateDefaultConfig().(*Config)
63+
c.APIKey, c.SpansURLOverride = "1", srv.URL
64+
params := component.ExporterCreateParams{Logger: zap.NewNop()}
65+
exp, err := f.CreateTraceExporter(context.Background(), params, c)
66+
require.NoError(t, err)
67+
require.NoError(t, exp.ConsumeTraces(ctx, internaldata.OCToTraceData(td)))
68+
require.NoError(t, exp.Shutdown(ctx))
69+
assert.Equal(t, expected, m.Spans())
70+
}
71+
72+
func TestExportTraceDataMinimum(t *testing.T) {
73+
td := consumerdata.TraceData{
74+
Spans: []*tracepb.Span{
75+
{
76+
TraceId: []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
77+
SpanId: []byte{0, 0, 0, 0, 0, 0, 0, 1},
78+
Name: &tracepb.TruncatableString{Value: "root"},
79+
},
80+
},
81+
}
82+
83+
expected := []Span{
84+
{
85+
ID: "0000000000000001",
86+
TraceID: "01010101010101010101010101010101",
87+
Attributes: map[string]interface{}{
88+
"collector.name": name,
89+
"collector.version": version,
90+
"name": "root",
91+
},
92+
},
93+
}
94+
95+
testTraceData(t, expected, td)
96+
}
97+
98+
func TestExportTraceDataFullTrace(t *testing.T) {
6199
td := consumerdata.TraceData{
62100
Node: &commonpb.Node{
63101
ServiceInfo: &commonpb.ServiceInfo{Name: "test-service"},
@@ -91,15 +129,6 @@ func TestExportTraceData(t *testing.T) {
91129
},
92130
}
93131

94-
f := NewFactory()
95-
c := f.CreateDefaultConfig().(*Config)
96-
c.APIKey, c.SpansURLOverride = "1", srv.URL
97-
params := component.ExporterCreateParams{Logger: zap.NewNop()}
98-
exp, err := f.CreateTraceExporter(context.Background(), params, c)
99-
require.NoError(t, err)
100-
require.NoError(t, exp.ConsumeTraces(ctx, internaldata.OCToTraceData(td)))
101-
require.NoError(t, exp.Shutdown(ctx))
102-
103132
expected := []Span{
104133
{
105134
ID: "0000000000000001",
@@ -138,17 +167,87 @@ func TestExportTraceData(t *testing.T) {
138167
},
139168
}
140169

141-
assert.Equal(t, expected, m.Spans())
170+
testTraceData(t, expected, td)
142171
}
143172

144-
func TestExportMetricData(t *testing.T) {
173+
func testExportMetricData(t *testing.T, expected []Metric, md consumerdata.MetricsData) {
145174
ctx, cancel := context.WithCancel(context.Background())
146175
defer cancel()
147176

148177
m := &Mock{make([]Data, 0, 3)}
149178
srv := m.Server()
150179
defer srv.Close()
151180

181+
f := NewFactory()
182+
c := f.CreateDefaultConfig().(*Config)
183+
c.APIKey, c.MetricsURLOverride = "1", srv.URL
184+
params := component.ExporterCreateParams{Logger: zap.NewNop()}
185+
exp, err := f.CreateMetricsExporter(context.Background(), params, c)
186+
require.NoError(t, err)
187+
require.NoError(t, exp.ConsumeMetrics(ctx, internaldata.OCToMetrics(md)))
188+
require.NoError(t, exp.Shutdown(ctx))
189+
assert.Equal(t, expected, m.Metrics())
190+
}
191+
192+
func TestExportMetricDataMinimal(t *testing.T) {
193+
desc := "physical property of matter that quantitatively expresses hot and cold"
194+
unit := "K"
195+
md := consumerdata.MetricsData{
196+
Metrics: []*metricspb.Metric{
197+
{
198+
MetricDescriptor: &metricspb.MetricDescriptor{
199+
Name: "temperature",
200+
Description: desc,
201+
Unit: unit,
202+
Type: metricspb.MetricDescriptor_GAUGE_DOUBLE,
203+
LabelKeys: []*metricspb.LabelKey{
204+
{Key: "location"},
205+
{Key: "elevation"},
206+
},
207+
},
208+
Timeseries: []*metricspb.TimeSeries{
209+
{
210+
LabelValues: []*metricspb.LabelValue{
211+
{Value: "Portland", HasValue: true},
212+
{Value: "0", HasValue: true},
213+
},
214+
Points: []*metricspb.Point{
215+
{
216+
Timestamp: &timestamppb.Timestamp{
217+
Seconds: 100,
218+
},
219+
Value: &metricspb.Point_DoubleValue{
220+
DoubleValue: 293.15,
221+
},
222+
},
223+
},
224+
},
225+
},
226+
},
227+
},
228+
}
229+
230+
expected := []Metric{
231+
{
232+
Name: "temperature",
233+
Type: "gauge",
234+
Value: 293.15,
235+
Timestamp: int64(100 * time.Microsecond),
236+
Attributes: map[string]interface{}{
237+
"collector.name": name,
238+
"collector.version": version,
239+
"description": desc,
240+
"unit": unit,
241+
"location": "Portland",
242+
"elevation": "0",
243+
},
244+
},
245+
}
246+
247+
testExportMetricData(t, expected, md)
248+
}
249+
250+
func TestExportMetricDataFull(t *testing.T) {
152251
desc := "physical property of matter that quantitatively expresses hot and cold"
153252
unit := "K"
154253
md := consumerdata.MetricsData{
@@ -234,15 +333,6 @@ func TestExportMetricData(t *testing.T) {
234333
},
235334
}
236335

237-
f := NewFactory()
238-
c := f.CreateDefaultConfig().(*Config)
239-
c.APIKey, c.MetricsURLOverride = "1", srv.URL
240-
params := component.ExporterCreateParams{Logger: zap.NewNop()}
241-
exp, err := f.CreateMetricsExporter(context.Background(), params, c)
242-
require.NoError(t, err)
243-
require.NoError(t, exp.ConsumeMetrics(ctx, internaldata.OCToMetrics(md)))
244-
require.NoError(t, exp.Shutdown(ctx))
245-
246336
expected := []Metric{
247337
{
248338
Name: "temperature",
@@ -326,5 +416,5 @@ func TestExportMetricData(t *testing.T) {
326416
},
327417
}
328418

329-
assert.Equal(t, expected, m.Metrics())
419+
testExportMetricData(t, expected, md)
330420
}

exporter/newrelicexporter/transformer.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,9 @@ func (t *transformer) MetricAttributes(metric *metricspb.Metric) map[string]inte
228228

229229
attrs[collectorNameKey] = name
230230
attrs[collectorVersionKey] = version
231-
attrs[serviceNameKey] = t.ServiceName
231+
if t.ServiceName != "" {
232+
attrs[serviceNameKey] = t.ServiceName
233+
}
232234

233235
return attrs
234236
}

0 commit comments

Comments
 (0)