diff --git a/integrations.go b/integrations.go index eb11aa977..1c2f5d6d5 100644 --- a/integrations.go +++ b/integrations.go @@ -70,27 +70,49 @@ func (ei *environmentIntegration) SetupOnce(client *Client) { } func (ei *environmentIntegration) processor(event *Event, hint *EventHint) *Event { + // Initialize maps as necessary. if event.Contexts == nil { event.Contexts = make(map[string]interface{}) } - - event.Contexts["device"] = map[string]interface{}{ - "arch": runtime.GOARCH, - "num_cpu": runtime.NumCPU(), + for _, name := range []string{"device", "os", "runtime"} { + if event.Contexts[name] == nil { + event.Contexts[name] = make(map[string]interface{}) + } } - event.Contexts["os"] = map[string]interface{}{ - "name": runtime.GOOS, + // Set contextual information preserving existing data. For each context, if + // the existing value is not of type map[string]interface{}, then no + // additional information is added. + if deviceContext, ok := event.Contexts["device"].(map[string]interface{}); ok { + if _, ok := deviceContext["arch"]; !ok { + deviceContext["arch"] = runtime.GOARCH + } + if _, ok := deviceContext["num_cpu"]; !ok { + deviceContext["num_cpu"] = runtime.NumCPU() + } } - - event.Contexts["runtime"] = map[string]interface{}{ - "name": "go", - "version": runtime.Version(), - "go_numroutines": runtime.NumGoroutine(), - "go_maxprocs": runtime.GOMAXPROCS(0), - "go_numcgocalls": runtime.NumCgoCall(), + if osContext, ok := event.Contexts["os"].(map[string]interface{}); ok { + if _, ok := osContext["name"]; !ok { + osContext["name"] = runtime.GOOS + } + } + if runtimeContext, ok := event.Contexts["runtime"].(map[string]interface{}); ok { + if _, ok := runtimeContext["name"]; !ok { + runtimeContext["name"] = "go" + } + if _, ok := runtimeContext["version"]; !ok { + runtimeContext["version"] = runtime.Version() + } + if _, ok := runtimeContext["go_numroutines"]; !ok { + runtimeContext["go_numroutines"] = runtime.NumGoroutine() + } + if _, ok := runtimeContext["go_maxprocs"]; !ok { + runtimeContext["go_maxprocs"] = runtime.GOMAXPROCS(0) + } + if _, ok := runtimeContext["go_numcgocalls"]; !ok { + runtimeContext["go_numcgocalls"] = runtime.NumCgoCall() + } } - return event } diff --git a/integrations_test.go b/integrations_test.go index 4f130a7ef..83d2b4a3a 100644 --- a/integrations_test.go +++ b/integrations_test.go @@ -1,6 +1,7 @@ package sentry import ( + "encoding/json" "path/filepath" "regexp" "runtime/debug" @@ -342,3 +343,48 @@ func TestExtractModules(t *testing.T) { }) } } + +func TestEnvironmentIntegrationDoesNotOverrideExistingContexts(t *testing.T) { + transport := &TransportMock{} + client, err := NewClient(ClientOptions{ + Transport: transport, + Integrations: func([]Integration) []Integration { + return []Integration{new(environmentIntegration)} + }, + }) + if err != nil { + t.Fatal(err) + } + scope := NewScope() + + scope.contexts["device"] = map[string]interface{}{ + "foo": "bar", + } + scope.contexts["os"] = map[string]interface{}{ + "name": "test", + } + scope.contexts["custom"] = "value" + hub := NewHub(client, scope) + hub.CaptureMessage("test event") + + events := transport.Events() + if len(events) != 1 { + b, err := json.MarshalIndent(events, "", " ") + if err != nil { + t.Fatal(err) + } + t.Fatalf("events = %s\ngot %d events, want 1", b, len(events)) + } + + contexts := events[0].Contexts + + if contexts["device"].(map[string]interface{})["foo"] != "bar" { + t.Errorf(`contexts["device"] = %#v, want contexts["device"]["foo"] == "bar"`, contexts["device"]) + } + if contexts["os"].(map[string]interface{})["name"] != "test" { + t.Errorf(`contexts["os"] = %#v, want contexts["os"]["name"] == "test"`, contexts["os"]) + } + if contexts["custom"] != "value" { + t.Errorf(`contexts["custom"] = %#v, want "value"`, contexts["custom"]) + } +}