Skip to content

Commit f3a5a3f

Browse files
authored
feat: refactor log serialization to match docs (#1169)
1 parent 736c662 commit f3a5a3f

File tree

5 files changed

+43
-45
lines changed

5 files changed

+43
-45
lines changed

interfaces.go

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func (b *Breadcrumb) MarshalJSON() ([]byte, error) {
8080

8181
if b.Timestamp.IsZero() {
8282
return json.Marshal(struct {
83-
// Embed all of the fields of Breadcrumb.
83+
// Embed all the fields of Breadcrumb.
8484
*breadcrumb
8585
// Timestamp shadows the original Timestamp field and is meant to
8686
// remain nil, triggering the omitempty behavior.
@@ -723,45 +723,18 @@ type EventHint struct {
723723
}
724724

725725
type Log struct {
726-
Timestamp time.Time `json:"timestamp,omitempty"`
727-
TraceID TraceID `json:"trace_id,omitempty"`
726+
Timestamp time.Time `json:"timestamp"`
727+
TraceID TraceID `json:"trace_id"`
728+
SpanID SpanID `json:"span_id,omitempty"`
728729
Level LogLevel `json:"level"`
729730
Severity int `json:"severity_number,omitempty"`
730-
Body string `json:"body,omitempty"`
731+
Body string `json:"body"`
731732
Attributes map[string]Attribute `json:"attributes,omitempty"`
732733
}
733734

734735
// ToEnvelopeItem converts the Log to a Sentry envelope item for batching.
735736
func (l *Log) ToEnvelopeItem() (*protocol.EnvelopeItem, error) {
736-
type logJSON struct {
737-
Timestamp *float64 `json:"timestamp,omitempty"`
738-
TraceID string `json:"trace_id,omitempty"`
739-
Level string `json:"level"`
740-
Severity int `json:"severity_number,omitempty"`
741-
Body string `json:"body,omitempty"`
742-
Attributes map[string]protocol.LogAttribute `json:"attributes,omitempty"`
743-
}
744-
745-
// Convert time.Time to seconds float if set
746-
var ts *float64
747-
if !l.Timestamp.IsZero() {
748-
sec := float64(l.Timestamp.UnixNano()) / 1e9
749-
ts = &sec
750-
}
751-
752-
attrs := make(map[string]protocol.LogAttribute, len(l.Attributes))
753-
for k, v := range l.Attributes {
754-
attrs[k] = protocol.LogAttribute{Value: v.Value, Type: string(v.Type)}
755-
}
756-
757-
logData, err := json.Marshal(logJSON{
758-
Timestamp: ts,
759-
TraceID: l.TraceID.String(),
760-
Level: string(l.Level),
761-
Severity: l.Severity,
762-
Body: l.Body,
763-
Attributes: attrs,
764-
})
737+
logData, err := json.Marshal(l)
765738
if err != nil {
766739
return nil, err
767740
}
@@ -808,3 +781,32 @@ type Attribute struct {
808781
Value any `json:"value"`
809782
Type AttrType `json:"type"`
810783
}
784+
785+
// MarshalJSON is a custom implementation that skips SpanID and timestamp when empty.
786+
func (l *Log) MarshalJSON() ([]byte, error) {
787+
type log Log
788+
789+
var spanID string
790+
if l.SpanID != zeroSpanID {
791+
spanID = l.SpanID.String()
792+
}
793+
794+
var ts json.RawMessage
795+
if !l.Timestamp.IsZero() {
796+
b, err := l.Timestamp.MarshalJSON()
797+
if err != nil {
798+
return nil, err
799+
}
800+
ts = b
801+
}
802+
803+
return json.Marshal(struct {
804+
*log
805+
SpanID string `json:"span_id,omitempty"`
806+
Timestamp json.RawMessage `json:"timestamp,omitempty"`
807+
}{
808+
log: (*log)(l),
809+
SpanID: spanID,
810+
Timestamp: ts,
811+
})
812+
}

interfaces_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -985,7 +985,7 @@ func TestLog_ToEnvelopeItem_And_Getters(t *testing.T) {
985985
}
986986

987987
var payload struct {
988-
Timestamp *float64 `json:"timestamp,omitempty"`
988+
Timestamp string `json:"timestamp,omitempty"`
989989
TraceID string `json:"trace_id,omitempty"`
990990
Level string `json:"level"`
991991
Severity int `json:"severity_number,omitempty"`
@@ -996,11 +996,11 @@ func TestLog_ToEnvelopeItem_And_Getters(t *testing.T) {
996996
t.Fatalf("failed to unmarshal payload: %v", err)
997997
}
998998

999-
if payload.Timestamp == nil {
999+
if payload.Timestamp == "" {
10001000
t.Fatal("expected timestamp to be set")
10011001
}
1002-
if *payload.Timestamp < 1.7e9 || *payload.Timestamp > 1.700000001e9 {
1003-
t.Fatalf("unexpected timestamp: %v", *payload.Timestamp)
1002+
if _, err := time.Parse(time.RFC3339, payload.Timestamp); err != nil {
1003+
t.Fatalf("invalid timestamp format: %v", err)
10041004
}
10051005
if payload.TraceID != trace.String() {
10061006
t.Fatalf("unexpected trace id: %q", payload.TraceID)

log.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,6 @@ func (l *sentryLogger) log(ctx context.Context, level LogLevel, severity int, me
167167
attrs["user.email"] = Attribute{Value: user.Email, Type: AttributeString}
168168
}
169169
}
170-
if span != nil {
171-
attrs["sentry.trace.parent_span_id"] = Attribute{Value: spanID.String(), Type: AttributeString}
172-
}
173170
if sdkIdentifier := l.client.sdkIdentifier; sdkIdentifier != "" {
174171
attrs["sentry.sdk.name"] = Attribute{Value: sdkIdentifier, Type: AttributeString}
175172
}
@@ -180,6 +177,7 @@ func (l *sentryLogger) log(ctx context.Context, level LogLevel, severity int, me
180177
log := &Log{
181178
Timestamp: time.Now(),
182179
TraceID: traceID,
180+
SpanID: spanID,
183181
Level: level,
184182
Severity: severity,
185183
Body: fmt.Sprintf(message, args...),

log_test.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -664,10 +664,8 @@ func Test_sentryLogger_TracePropagationWithTransaction(t *testing.T) {
664664
if log.TraceID != expectedTraceID {
665665
t.Errorf("unexpected TraceID: got %s, want %s", log.TraceID.String(), expectedTraceID.String())
666666
}
667-
if val, ok := log.Attributes["sentry.trace.parent_span_id"]; !ok {
668-
t.Errorf("missing sentry.trace.parent_span_id attribute")
669-
} else if val.Value != expectedSpanID.String() {
670-
t.Errorf("unexpected SpanID: got %s, want %s", val.Value, expectedSpanID.String())
667+
if log.SpanID != expectedSpanID {
668+
t.Errorf("unexpected SpanID: got %s, want %s", log.SpanID.String(), expectedSpanID.String())
671669
}
672670
}
673671

transport_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ func TestEnvelopeFromLogEvent(t *testing.T) {
292292
got := b.String()
293293
want := `{"event_id":"b81c5be4d31e48959103a1f878a1efcb","sent_at":"1970-01-01T00:00:00Z","dsn":"http://public@example.com/sentry/1","sdk":{"name":"sentry.go","version":"0.0.1"}}
294294
{"type":"log","item_count":1,"content_type":"application/vnd.sentry.items.log+json"}
295-
{"event_id":"b81c5be4d31e48959103a1f878a1efcb","sdk":{"name":"sentry.go","version":"0.0.1"},"user":{},"items":[{"timestamp":"0001-01-01T00:00:00Z","trace_id":"d49d9bf66f13450b81f65bc51cf49c03","level":"info","severity_number":9,"body":"test log message","attributes":{"key.bool":{"value":true,"type":"boolean"},"key.float":{"value":42.2,"type":"double"},"key.int":{"value":42,"type":"integer"},"key.string":{"value":"str","type":"string"},"sentry.environment":{"value":"testing","type":"string"},"sentry.release":{"value":"v1.2.3","type":"string"},"sentry.sdk.name":{"value":"sentry.go","type":"string"},"sentry.sdk.version":{"value":"0.0.1","type":"string"},"sentry.server.address":{"value":"test-server","type":"string"}}}]}
295+
{"event_id":"b81c5be4d31e48959103a1f878a1efcb","sdk":{"name":"sentry.go","version":"0.0.1"},"user":{},"items":[{"trace_id":"d49d9bf66f13450b81f65bc51cf49c03","level":"info","severity_number":9,"body":"test log message","attributes":{"key.bool":{"value":true,"type":"boolean"},"key.float":{"value":42.2,"type":"double"},"key.int":{"value":42,"type":"integer"},"key.string":{"value":"str","type":"string"},"sentry.environment":{"value":"testing","type":"string"},"sentry.release":{"value":"v1.2.3","type":"string"},"sentry.sdk.name":{"value":"sentry.go","type":"string"},"sentry.sdk.version":{"value":"0.0.1","type":"string"},"sentry.server.address":{"value":"test-server","type":"string"}}}]}
296296
`
297297
if diff := cmp.Diff(want, got); diff != "" {
298298
t.Errorf("Envelope mismatch (-want +got):\n%s", diff)

0 commit comments

Comments
 (0)