diff --git a/interfaces.go b/interfaces.go index 5bdaa49bb..91c5e476e 100644 --- a/interfaces.go +++ b/interfaces.go @@ -241,6 +241,34 @@ type TransactionInfo struct { Source TransactionSource `json:"source,omitempty"` } +// The DebugMeta interface is not used in Golang apps, but may be populated +// when proxying Events from other platforms, like iOS, Android, and the +// Web. (See: https://develop.sentry.dev/sdk/event-payloads/debugmeta/ ). +type DebugMeta struct { + SdkInfo *DebugMetaSdkInfo `json:"sdk_info,omitempty"` + Images []DebugMetaImage `json:"images,omitempty"` +} + +type DebugMetaSdkInfo struct { + SdkName string `json:"sdk_name,omitempty"` + VersionMajor int `json:"version_major,omitempty"` + VersionMinor int `json:"version_minor,omitempty"` + VersionPatchlevel int `json:"version_patchlevel,omitempty"` +} + +type DebugMetaImage struct { + Type string `json:"type,omitempty"` // all + ImageAddr string `json:"image_addr,omitempty"` // macho,elf,pe + ImageSize int `json:"image_size,omitempty"` // macho,elf,pe + DebugID string `json:"debug_id,omitempty"` // macho,elf,pe,wasm,sourcemap + DebugFile string `json:"debug_file,omitempty"` // macho,elf,pe,wasm + CodeID string `json:"code_id,omitempty"` // macho,elf,pe,wasm + CodeFile string `json:"code_file,omitempty"` // macho,elf,pe,wasm,sourcemap + ImageVmaddr string `json:"image_vmaddr,omitempty"` // macho,elf,pe + Arch string `json:"arch,omitempty"` // macho,elf,pe + UUID string `json:"uuid,omitempty"` // proguard +} + // EventID is a hexadecimal string representing a unique uuid4 for an Event. // An EventID must be 32 characters long, lowercase and not have any dashes. type EventID string @@ -271,6 +299,7 @@ type Event struct { Modules map[string]string `json:"modules,omitempty"` Request *Request `json:"request,omitempty"` Exception []Exception `json:"exception,omitempty"` + DebugMeta *DebugMeta `json:"debug_meta,omitempty"` // The fields below are only relevant for transactions. diff --git a/interfaces_test.go b/interfaces_test.go index 9cb89284b..0feb5b2fe 100644 --- a/interfaces_test.go +++ b/interfaces_test.go @@ -160,6 +160,61 @@ func TestEventMarshalJSON(t *testing.T) { } } +func TestEventWithDebugMetaMarshalJSON(t *testing.T) { + event := NewEvent() + event.DebugMeta = &DebugMeta{ + SdkInfo: &DebugMetaSdkInfo{ + SdkName: "test", + VersionMajor: 1, + VersionMinor: 2, + VersionPatchlevel: 3, + }, + Images: []DebugMetaImage{ + { + Type: "macho", + ImageAddr: "0xabcd0000", + ImageSize: 32768, + DebugID: "42DB5B96-5144-4079-BE09-45E2142CA3E5", + DebugFile: "foo.dSYM", + CodeID: "A7AF6477-9130-4EB7-ADFE-AD0F57001DBD", + CodeFile: "foo.dylib", + ImageVmaddr: "0x0", + Arch: "arm64", + }, + { + Type: "proguard", + UUID: "982E62D4-6493-4E43-864B-6523C79C7064", + }, + }, + } + + got, err := json.Marshal(event) + if err != nil { + t.Fatal(err) + } + + want := `{"sdk":{},"user":{},` + + `"debug_meta":{` + + `"sdk_info":{"sdk_name":"test","version_major":1,"version_minor":2,"version_patchlevel":3},` + + `"images":[` + + `{"type":"macho",` + + `"image_addr":"0xabcd0000",` + + `"image_size":32768,` + + `"debug_id":"42DB5B96-5144-4079-BE09-45E2142CA3E5",` + + `"debug_file":"foo.dSYM",` + + `"code_id":"A7AF6477-9130-4EB7-ADFE-AD0F57001DBD",` + + `"code_file":"foo.dylib",` + + `"image_vmaddr":"0x0",` + + `"arch":"arm64"` + + `},` + + `{"type":"proguard","uuid":"982E62D4-6493-4E43-864B-6523C79C7064"}` + + `]}}` + + if diff := cmp.Diff(want, string(got)); diff != "" { + t.Errorf("Event mismatch (-want +got):\n%s", diff) + } +} + func TestMechanismMarshalJSON(t *testing.T) { mechanism := &Mechanism{ Type: "some type", diff --git a/stacktrace.go b/stacktrace.go index 8c256ec98..c3c5f5d55 100644 --- a/stacktrace.go +++ b/stacktrace.go @@ -166,13 +166,7 @@ type Frame struct { Symbol string `json:"symbol,omitempty"` // Module is, despite the name, the Sentry protocol equivalent of a Go // package's import path. - Module string `json:"module,omitempty"` - // Package is not used for Go stack trace frames. In other platforms it - // refers to a container where the Module can be found. For example, a - // Java JAR, a .NET Assembly, or a native dynamic library. - // It exists for completeness, allowing the construction and reporting - // of custom event payloads. - Package string `json:"package,omitempty"` + Module string `json:"module,omitempty"` Filename string `json:"filename,omitempty"` AbsPath string `json:"abs_path,omitempty"` Lineno int `json:"lineno,omitempty"` @@ -182,6 +176,18 @@ type Frame struct { PostContext []string `json:"post_context,omitempty"` InApp bool `json:"in_app,omitempty"` Vars map[string]interface{} `json:"vars,omitempty"` + // Package and the below are not used for Go stack trace frames. In + // other platforms it refers to a container where the Module can be + // found. For example, a Java JAR, a .NET Assembly, or a native + // dynamic library. They exists for completeness, allowing the + // construction and reporting of custom event payloads. + Package string `json:"package,omitempty"` + InstructionAddr string `json:"instruction_addr,omitempty"` + AddrMode string `json:"addr_mode,omitempty"` + SymbolAddr string `json:"symbol_addr,omitempty"` + ImageAddr string `json:"image_addr,omitempty"` + Platform string `json:"platform,omitempty"` + StackStart bool `json:"stack_start,omitempty"` } // NewFrame assembles a stacktrace frame out of runtime.Frame. diff --git a/stacktrace_test.go b/stacktrace_test.go index c49650a72..2a4a9c20a 100644 --- a/stacktrace_test.go +++ b/stacktrace_test.go @@ -1,6 +1,7 @@ package sentry import ( + "encoding/json" "errors" "testing" @@ -169,3 +170,75 @@ func TestExtractXErrorsPC(t *testing.T) { t.Errorf("got %#v, want nil", got) } } + +func TestEventWithExceptionStacktraceMarshalJSON(t *testing.T) { + event := NewEvent() + event.Exception = []Exception{ + { + Stacktrace: &Stacktrace{ + Frames: []Frame{ + { + Function: "gofunc", + Symbol: "gosym", + Module: "gopkg/gopath", + Filename: "foo.go", + AbsPath: "/something/foo.go", + Lineno: 35, + Colno: 72, + PreContext: []string{"pre", "context"}, + ContextLine: "contextline", + PostContext: []string{"post", "context"}, + InApp: true, + Vars: map[string]interface{}{ + "foostr": "bar", + "fooint": 25, + }, + }, + { + Symbol: "nativesym", + Package: "my.dylib", + InstructionAddr: "0xabcd0010", + AddrMode: "abs", + SymbolAddr: "0xabcd0000", + ImageAddr: "0xabc00000", + Platform: "native", + StackStart: false, + }, + }, + }, + }, + } + + got, err := json.Marshal(event) + if err != nil { + t.Fatal(err) + } + + want := `{"sdk":{},"user":{},` + + `"exception":[{"stacktrace":{"frames":[` + + `{"function":"gofunc",` + + `"symbol":"gosym",` + + `"module":"gopkg/gopath",` + + `"filename":"foo.go",` + + `"abs_path":"/something/foo.go",` + + `"lineno":35,` + + `"colno":72,` + + `"pre_context":["pre","context"],` + + `"context_line":"contextline",` + + `"post_context":["post","context"],` + + `"in_app":true,` + + `"vars":{"fooint":25,"foostr":"bar"}` + + `},{` + + `"symbol":"nativesym",` + + `"package":"my.dylib",` + + `"instruction_addr":"0xabcd0010",` + + `"addr_mode":"abs",` + + `"symbol_addr":"0xabcd0000",` + + `"image_addr":"0xabc00000",` + + `"platform":"native"` + + `}]}}]}` + + if diff := cmp.Diff(want, string(got)); diff != "" { + t.Errorf("Event mismatch (-want +got):\n%s", diff) + } +}