Skip to content

Commit 7367f5d

Browse files
authored
[receiver/libhoney] Handle wider variety of encoding (#45273)
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description Newer software sending msgpack data in Libhoney format is sending integers with signs now when they were unsigned in the past. This allows the receiver to accept either type of integer. <!--Describe what testing was performed and which tests were added.--> #### Testing Added test cases that fail with the old code and pass with the new code <!--Describe the documentation added.--> #### Documentation This change allows the receiver to meet the current expectations so no additional documentation is needed. <!--Please delete paragraphs that you did not use before submitting.-->
1 parent f2d8817 commit 7367f5d

File tree

7 files changed

+407
-2
lines changed

7 files changed

+407
-2
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
change_type: "bug_fix"
2+
component: "receiver/libhoney"
3+
issues: [45273]
4+
note: "Improve msgpack decoding to handle ints or uints"
5+
change_logs: [user]

receiver/libhoneyreceiver/internal/codec/codec.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,8 @@ func buildLibhoneyEventFromMap(rawEvent map[string]any) libhoneyevent.LibhoneyEv
254254
event.Samplerate = v
255255
case int64:
256256
event.Samplerate = int(v)
257+
case uint64:
258+
event.Samplerate = int(v)
257259
case float64:
258260
event.Samplerate = int(v)
259261
}

receiver/libhoneyreceiver/internal/codec/codec_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,61 @@ func TestGetEncoder(t *testing.T) {
561561
}
562562
}
563563

564+
func TestBuildLibhoneyEventFromMap_SamplerateTypes(t *testing.T) {
565+
tests := []struct {
566+
name string
567+
rawEvent map[string]any
568+
expectedSampleRate int
569+
}{
570+
{
571+
name: "samplerate as int",
572+
rawEvent: map[string]any{
573+
"data": map[string]any{"field1": "value1"},
574+
"samplerate": int(10),
575+
},
576+
expectedSampleRate: 10,
577+
},
578+
{
579+
name: "samplerate as int64",
580+
rawEvent: map[string]any{
581+
"data": map[string]any{"field1": "value1"},
582+
"samplerate": int64(10),
583+
},
584+
expectedSampleRate: 10,
585+
},
586+
{
587+
name: "samplerate as uint64",
588+
rawEvent: map[string]any{
589+
"data": map[string]any{"field1": "value1"},
590+
"samplerate": uint64(10),
591+
},
592+
expectedSampleRate: 10,
593+
},
594+
{
595+
name: "samplerate as float64",
596+
rawEvent: map[string]any{
597+
"data": map[string]any{"field1": "value1"},
598+
"samplerate": float64(10.0),
599+
},
600+
expectedSampleRate: 10,
601+
},
602+
{
603+
name: "no samplerate defaults to 1",
604+
rawEvent: map[string]any{
605+
"data": map[string]any{"field1": "value1"},
606+
},
607+
expectedSampleRate: 1,
608+
},
609+
}
610+
611+
for _, tt := range tests {
612+
t.Run(tt.name, func(t *testing.T) {
613+
event := buildLibhoneyEventFromMap(tt.rawEvent)
614+
assert.Equal(t, tt.expectedSampleRate, event.Samplerate)
615+
})
616+
}
617+
}
618+
564619
func TestEncoder_RoundTrip(t *testing.T) {
565620
tests := []struct {
566621
name string

receiver/libhoneyreceiver/internal/libhoneyevent/libhoneyevent.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,13 @@ func (l *LibhoneyEvent) ToPLogRecord(newLog *plog.LogRecord, alreadyUsedFields *
290290
newLog.SetTimestamp(pcommon.Timestamp(timeNs))
291291

292292
if logSevCode, ok := l.Data["severity_code"]; ok {
293-
logSevInt := int32(logSevCode.(int64))
293+
var logSevInt int32
294+
switch v := logSevCode.(type) {
295+
case int64:
296+
logSevInt = int32(v)
297+
case uint64:
298+
logSevInt = int32(v)
299+
}
294300
newLog.SetSeverityNumber(plog.SeverityNumber(logSevInt))
295301
}
296302

@@ -299,7 +305,13 @@ func (l *LibhoneyEvent) ToPLogRecord(newLog *plog.LogRecord, alreadyUsedFields *
299305
}
300306

301307
if logFlags, ok := l.Data["flags"]; ok {
302-
logFlagsUint := uint32(logFlags.(uint64))
308+
var logFlagsUint uint32
309+
switch v := logFlags.(type) {
310+
case int64:
311+
logFlagsUint = uint32(v)
312+
case uint64:
313+
logFlagsUint = uint32(v)
314+
}
303315
newLog.SetFlags(plog.LogRecordFlags(logFlagsUint))
304316
}
305317

@@ -326,6 +338,8 @@ func (l *LibhoneyEvent) ToPLogRecord(newLog *plog.LogRecord, alreadyUsedFields *
326338
case int64, int16, int32:
327339
intv := v.(int64)
328340
newLog.Attributes().PutInt(k, intv)
341+
case uint64:
342+
newLog.Attributes().PutInt(k, int64(v))
329343
case float64:
330344
newLog.Attributes().PutDouble(k, v)
331345
case bool:
@@ -473,6 +487,8 @@ func (l *LibhoneyEvent) ToPTraceSpan(newSpan *ptrace.Span, alreadyUsedFields *[]
473487
case int64, int16, int32:
474488
intv := v.(int64)
475489
newSpan.Attributes().PutInt(k, intv)
490+
case uint64:
491+
newSpan.Attributes().PutInt(k, int64(v))
476492
case float64:
477493
newSpan.Attributes().PutDouble(k, v)
478494
case bool:

receiver/libhoneyreceiver/internal/libhoneyevent/libhoneyevent_test.go

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,198 @@ func TestGetParentID(t *testing.T) {
612612
}
613613
}
614614

615+
func TestToPLogRecord_IntegerTypeHandling(t *testing.T) {
616+
logger := zap.NewNop()
617+
now := time.Now()
618+
619+
tests := []struct {
620+
name string
621+
event LibhoneyEvent
622+
alreadyUsedFields []string
623+
checkFunc func(t *testing.T, lr plog.LogRecord)
624+
}{
625+
{
626+
name: "severity_code as int64",
627+
event: LibhoneyEvent{
628+
Samplerate: 1,
629+
MsgPackTimestamp: &now,
630+
Data: map[string]any{
631+
"severity_code": int64(5),
632+
},
633+
},
634+
checkFunc: func(t *testing.T, lr plog.LogRecord) {
635+
assert.Equal(t, plog.SeverityNumber(5), lr.SeverityNumber())
636+
},
637+
},
638+
{
639+
name: "severity_code as uint64",
640+
event: LibhoneyEvent{
641+
Samplerate: 1,
642+
MsgPackTimestamp: &now,
643+
Data: map[string]any{
644+
"severity_code": uint64(5),
645+
},
646+
},
647+
checkFunc: func(t *testing.T, lr plog.LogRecord) {
648+
assert.Equal(t, plog.SeverityNumber(5), lr.SeverityNumber())
649+
},
650+
},
651+
{
652+
name: "flags as int64",
653+
event: LibhoneyEvent{
654+
Samplerate: 1,
655+
MsgPackTimestamp: &now,
656+
Data: map[string]any{
657+
"flags": int64(1),
658+
},
659+
},
660+
checkFunc: func(t *testing.T, lr plog.LogRecord) {
661+
assert.Equal(t, plog.LogRecordFlags(1), lr.Flags())
662+
},
663+
},
664+
{
665+
name: "flags as uint64",
666+
event: LibhoneyEvent{
667+
Samplerate: 1,
668+
MsgPackTimestamp: &now,
669+
Data: map[string]any{
670+
"flags": uint64(1),
671+
},
672+
},
673+
checkFunc: func(t *testing.T, lr plog.LogRecord) {
674+
assert.Equal(t, plog.LogRecordFlags(1), lr.Flags())
675+
},
676+
},
677+
{
678+
name: "attribute as int64",
679+
event: LibhoneyEvent{
680+
Samplerate: 1,
681+
MsgPackTimestamp: &now,
682+
Data: map[string]any{
683+
"my_int_attr": int64(42),
684+
},
685+
},
686+
checkFunc: func(t *testing.T, lr plog.LogRecord) {
687+
val, ok := lr.Attributes().Get("my_int_attr")
688+
assert.True(t, ok)
689+
assert.Equal(t, int64(42), val.Int())
690+
},
691+
},
692+
{
693+
name: "attribute as uint64",
694+
event: LibhoneyEvent{
695+
Samplerate: 1,
696+
MsgPackTimestamp: &now,
697+
Data: map[string]any{
698+
"my_uint_attr": uint64(42),
699+
},
700+
},
701+
checkFunc: func(t *testing.T, lr plog.LogRecord) {
702+
val, ok := lr.Attributes().Get("my_uint_attr")
703+
assert.True(t, ok)
704+
assert.Equal(t, int64(42), val.Int())
705+
},
706+
},
707+
{
708+
name: "large uint64 attribute",
709+
event: LibhoneyEvent{
710+
Samplerate: 1,
711+
MsgPackTimestamp: &now,
712+
Data: map[string]any{
713+
"large_uint": uint64(18446744073709551615), // max uint64
714+
},
715+
},
716+
checkFunc: func(t *testing.T, lr plog.LogRecord) {
717+
val, ok := lr.Attributes().Get("large_uint")
718+
assert.True(t, ok)
719+
// When converted to int64, this will overflow but should not panic
720+
assert.Equal(t, int64(-1), val.Int())
721+
},
722+
},
723+
}
724+
725+
for _, tt := range tests {
726+
t.Run(tt.name, func(t *testing.T) {
727+
newLog := plog.NewLogRecord()
728+
err := tt.event.ToPLogRecord(&newLog, &tt.alreadyUsedFields, *logger)
729+
require.NoError(t, err)
730+
tt.checkFunc(t, newLog)
731+
})
732+
}
733+
}
734+
735+
func TestToPTraceSpan_IntegerTypeHandling(t *testing.T) {
736+
now := time.Now()
737+
alreadyUsedFields := []string{"name", "trace.span_id", "trace.trace_id", "duration_ms"}
738+
testCfg := FieldMapConfig{
739+
Attributes: AttributesConfig{
740+
Name: "name",
741+
TraceID: "trace.trace_id",
742+
SpanID: "trace.span_id",
743+
ParentID: "trace.parent_id",
744+
Error: "error",
745+
SpanKind: "kind",
746+
DurationFields: []string{"duration_ms"},
747+
},
748+
}
749+
750+
tests := []struct {
751+
name string
752+
event LibhoneyEvent
753+
checkFunc func(t *testing.T, span ptrace.Span)
754+
}{
755+
{
756+
name: "attribute as int64",
757+
event: LibhoneyEvent{
758+
Time: now.Format(time.RFC3339),
759+
MsgPackTimestamp: &now,
760+
Samplerate: 1,
761+
Data: map[string]any{
762+
"name": "test-span",
763+
"trace.span_id": "1234567890abcdef",
764+
"trace.trace_id": "1234567890abcdef1234567890abcdef",
765+
"duration_ms": 100.0,
766+
"my_int_attr": int64(42),
767+
},
768+
},
769+
checkFunc: func(t *testing.T, span ptrace.Span) {
770+
val, ok := span.Attributes().Get("my_int_attr")
771+
assert.True(t, ok)
772+
assert.Equal(t, int64(42), val.Int())
773+
},
774+
},
775+
{
776+
name: "attribute as uint64",
777+
event: LibhoneyEvent{
778+
Time: now.Format(time.RFC3339),
779+
MsgPackTimestamp: &now,
780+
Samplerate: 1,
781+
Data: map[string]any{
782+
"name": "test-span",
783+
"trace.span_id": "1234567890abcdef",
784+
"trace.trace_id": "1234567890abcdef1234567890abcdef",
785+
"duration_ms": 100.0,
786+
"my_uint_attr": uint64(42),
787+
},
788+
},
789+
checkFunc: func(t *testing.T, span ptrace.Span) {
790+
val, ok := span.Attributes().Get("my_uint_attr")
791+
assert.True(t, ok)
792+
assert.Equal(t, int64(42), val.Int())
793+
},
794+
},
795+
}
796+
797+
for _, tt := range tests {
798+
t.Run(tt.name, func(t *testing.T) {
799+
span := ptrace.NewSpan()
800+
err := tt.event.ToPTraceSpan(&span, &alreadyUsedFields, testCfg, *zap.NewNop())
801+
require.NoError(t, err)
802+
tt.checkFunc(t, span)
803+
})
804+
}
805+
}
806+
615807
func TestLibhoneyEvent_UnmarshalMsgpack(t *testing.T) {
616808
tests := []struct {
617809
name string

receiver/libhoneyreceiver/internal/parser/parser.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ func addSpanEventsToSpan(sp ptrace.Span, events []libhoneyevent.LibhoneyEvent, a
197197
case int64, int16, int32:
198198
intv := lval.(int64)
199199
newEvent.Attributes().PutInt(lkey, intv)
200+
case uint64:
201+
newEvent.Attributes().PutInt(lkey, int64(lval))
200202
case float64:
201203
newEvent.Attributes().PutDouble(lkey, lval)
202204
case bool:
@@ -283,6 +285,8 @@ func addSpanLinksToSpan(sp ptrace.Span, links []libhoneyevent.LibhoneyEvent, alr
283285
case int64, int16, int32:
284286
intv := lval.(int64)
285287
newLink.Attributes().PutInt(lkey, intv)
288+
case uint64:
289+
newLink.Attributes().PutInt(lkey, int64(lval))
286290
case float64:
287291
newLink.Attributes().PutDouble(lkey, lval)
288292
case bool:

0 commit comments

Comments
 (0)