Files
Hayley DeckersandGitHub 72e44e76d6 feat: add support for more trace exception events (#791)
## Pull Request

### Summary
Replaces matching on `even.Name == "exception"` with an
`is_exception(event.Name)` function which classifies `"exception"` or
anything ending in `".exception"` as one.

### Related Issues
<!-- Reference any related issues (e.g. Fixes #123, Closes #456) -->
Closes #790 

### Resource Impact
**Will this change affect the application's resource usage?**
- [ ] Yes
- [x] No

### tested?

Tested with a local build of Signoz. I did have to update the
`bytedance/sonic` dependency to `1.15.0` in order to get it to build (on
Windows). I did not include those changes to keep this MR focused but I
thought I should mention it.
2026-04-02 05:58:50 +00:00

102 lines
3.4 KiB
Go

package traces
import (
"crypto/md5"
"fmt"
"strings"
"github.com/goccy/go-json"
"github.com/google/uuid"
"go.opentelemetry.io/collector/pdata/pcommon"
"go.opentelemetry.io/collector/pdata/ptrace"
"go.uber.org/zap/zapcore"
)
// IsExceptionEvent reports whether the given span event name represents an
// exception event per the OpenTelemetry semantic conventions.
//
// Recognized event names:
//
// - "exception" (stable)
// https://opentelemetry.io/docs/specs/semconv/exceptions/exceptions-spans/
//
// - "http.client.request.exception", "http.server.request.exception" (development)
// https://opentelemetry.io/docs/specs/semconv/http/http-exceptions/
//
// - "db.client.operation.exception" (development)
// https://opentelemetry.io/docs/specs/semconv/db/database-exceptions/
//
// - "rpc.client.call.exception", "rpc.server.call.exception" (development)
// https://opentelemetry.io/docs/specs/semconv/rpc/rpc-exceptions/
//
// The OTel semconv naming pattern for domain-specific exception events follows
// the pattern: "{domain}.{component}.{operation}.exception",
// we match on the suffix ".exception" to be forward compatible with new conventions.
func IsExceptionEvent(eventName string) bool {
return eventName == "exception" || strings.HasSuffix(eventName, ".exception")
}
// This has been copied from the exporter. The exporter needs to read from here.
type Event struct {
Name string `json:"name,omitempty"`
TimeUnixNano uint64 `json:"timeUnixNano,omitempty"`
AttributeMap map[string]string `json:"attributeMap,omitempty"`
IsError bool `json:"isError,omitempty"`
}
func (e *Event) MarshalLogObject(enc zapcore.ObjectEncoder) error {
enc.AddString("name", e.Name)
enc.AddUint64("timeUnixNano", e.TimeUnixNano)
enc.AddBool("isError", e.IsError)
enc.AddString("attributeMap", fmt.Sprintf("%v", e.AttributeMap))
return nil
}
// This has been copied from the exporter. The exporter needs to read from here.
type ErrorEvent struct {
Event Event `json:"errorEvent,omitempty"`
ErrorID string `json:"errorID,omitempty"`
ErrorGroupID string `json:"errorGroupID,omitempty"`
}
func NewEventsAndErrorEvents(input ptrace.SpanEventSlice, serviceName string, lowCardinalExceptionGrouping bool) ([]string, []ErrorEvent) {
events := make([]string, 0)
errorEvents := make([]ErrorEvent, 0)
for i := 0; i < input.Len(); i++ {
event := Event{
Name: input.At(i).Name(),
TimeUnixNano: uint64(input.At(i).Timestamp()),
AttributeMap: map[string]string{},
IsError: false,
}
input.At(i).Attributes().Range(func(k string, v pcommon.Value) bool {
event.AttributeMap[k] = v.AsString()
return true
})
errorEvent := ErrorEvent{}
if IsExceptionEvent(event.Name) {
event.IsError = true
errorEvent.Event = event
uuidWithHyphen := uuid.New()
uuid := strings.Replace(uuidWithHyphen.String(), "-", "", -1)
errorEvent.ErrorID = uuid
var hash [16]byte
if lowCardinalExceptionGrouping {
hash = md5.Sum([]byte(serviceName + errorEvent.Event.AttributeMap["exception.type"]))
} else {
hash = md5.Sum([]byte(serviceName + errorEvent.Event.AttributeMap["exception.type"] + errorEvent.Event.AttributeMap["exception.message"]))
}
errorEvent.ErrorGroupID = fmt.Sprintf("%x", hash)
}
stringEvent, _ := json.Marshal(event)
events = append(events, string(stringEvent))
errorEvents = append(errorEvents, errorEvent)
}
return events, errorEvents
}