Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: global tags #718

Merged
merged 7 commits into from Sep 26, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions client.go
Expand Up @@ -224,6 +224,8 @@ type ClientOptions struct {
// is not optimized for long chains either. The top-level error together with a
// stack trace is often the most useful information.
MaxErrorDepth int
// Default event tags. These are overridden by tags set on a scope.
Tags map[string]string
}

// Client is the underlying processor that is used by the main API and Hub
Expand Down Expand Up @@ -375,6 +377,7 @@ func (client *Client) setupIntegrations() {
new(modulesIntegration),
new(ignoreErrorsIntegration),
new(ignoreTransactionsIntegration),
new(globalTagsIntegration),
}

if client.options.Integrations != nil {
Expand Down
65 changes: 65 additions & 0 deletions integrations.go
Expand Up @@ -2,6 +2,7 @@

import (
"fmt"
"os"
"regexp"
"runtime"
"runtime/debug"
Expand Down Expand Up @@ -325,3 +326,67 @@
}
return frame
}

// ================================
// Global Tags Integration
// ================================

const envTagsPrefix = "SENTRY_TAGS_"

type globalTagsIntegration struct {
tags map[string]string
envTags map[string]string
}

func (ti *globalTagsIntegration) Name() string {
return "GlobalTags"
}

func (ti *globalTagsIntegration) SetupOnce(client *Client) {
ti.tags = make(map[string]string, len(client.options.Tags))
for k, v := range client.options.Tags {
ti.tags[k] = v
}

ti.envTags = loadEnvTags()

client.AddEventProcessor(ti.processor)
}

func (ti *globalTagsIntegration) processor(event *Event, hint *EventHint) *Event {
if len(ti.tags) == 0 && len(ti.envTags) == 0 {
return event
}

if event.Tags == nil {
event.Tags = make(map[string]string, len(ti.tags)+len(ti.envTags))
}

Check warning on line 363 in integrations.go

View check run for this annotation

Codecov / codecov/patch

integrations.go#L362-L363

Added lines #L362 - L363 were not covered by tests

for k, v := range ti.tags {
if _, ok := event.Tags[k]; !ok {
event.Tags[k] = v
cleptric marked this conversation as resolved.
Show resolved Hide resolved
}
}

for k, v := range ti.envTags {
if _, ok := event.Tags[k]; !ok {
event.Tags[k] = v
}
}

return event
}

func loadEnvTags() map[string]string {
tags := map[string]string{}
for _, pair := range os.Environ() {
parts := strings.Split(pair, "=")
if !strings.HasPrefix(parts[0], envTagsPrefix) {
continue
}
tag := strings.TrimPrefix(parts[0], envTagsPrefix)
tag = strings.ToLower(tag)
cleptric marked this conversation as resolved.
Show resolved Hide resolved
tags[tag] = parts[1]
}
return tags
}
48 changes: 48 additions & 0 deletions integrations_test.go
Expand Up @@ -2,6 +2,7 @@ package sentry

import (
"encoding/json"
"os"
"path/filepath"
"regexp"
"runtime/debug"
Expand Down Expand Up @@ -421,3 +422,50 @@ func TestEnvironmentIntegrationDoesNotOverrideExistingContexts(t *testing.T) {
t.Errorf(`contexts["custom"]["key"] = %#v, want "value"`, contexts["custom"]["key"])
}
}

func TestGlobalTagsIntegration(t *testing.T) {
os.Setenv("SENTRY_TAGS_FOO", "foo_value_env")
os.Setenv("SENTRY_TAGS_BAR", "bar_value_env")
os.Setenv("SENTRY_TAGS_BAZ", "baz_value_env")
defer os.Unsetenv("SENTRY_TAGS_FOO")
defer os.Unsetenv("SENTRY_TAGS_BAR")
defer os.Unsetenv("SENTRY_TAGS_BAZ")

transport := &TransportMock{}
client, err := NewClient(ClientOptions{
Transport: transport,
Tags: map[string]string{
"foo": "foo_value_client_options",
"baz": "baz_value_client_options",
},
Integrations: func([]Integration) []Integration {
return []Integration{new(globalTagsIntegration)}
},
})
if err != nil {
t.Fatal(err)
}

scope := NewScope()
scope.SetTags(map[string]string{"foo": "foo_value_scope"})

event := NewEvent()
event.Message = "event message"
client.CaptureEvent(event, nil, scope)

assertEqual(t,
transport.lastEvent.Tags["foo"],
"foo_value_scope",
"scope tag should override any global tag",
)
assertEqual(t,
transport.lastEvent.Tags["bar"],
"bar_value_env",
"env tag present if not overridden by scope or client options tags",
)
assertEqual(t,
transport.lastEvent.Tags["baz"],
"baz_value_client_options",
"client options tag present if not overridden by scope and overrides env tag",
)
}