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

ddtrace/tracer: add SetUser function and userTagOption #1196

Merged
merged 4 commits into from Mar 7, 2022
Merged
Show file tree
Hide file tree
Changes from 3 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
38 changes: 38 additions & 0 deletions ddtrace/tracer/option.go
Expand Up @@ -829,3 +829,41 @@ func StackFrames(n, skip uint) FinishOption {
cfg.SkipStackFrames = skip
}
}

// UserMonitoringOption represents a function that can be provided as a parameter to SetUser.
type UserMonitoringOption func(Span)

// WithUserEmail returns the option setting the email of the authenticated user.
func WithUserEmail(email string) UserMonitoringOption {
return func(s Span) {
s.SetTag("usr.email", email)
}
}

// WithUserName returns the option setting the name of the authenticated user.
func WithUserName(name string) UserMonitoringOption {
return func(s Span) {
s.SetTag("usr.name", name)
}
}

// WithUserSessionID returns the option setting the session ID of the authenticated user.
func WithUserSessionID(sessionID string) UserMonitoringOption {
return func(s Span) {
s.SetTag("usr.session_id", sessionID)
}
}

// WithUserRole returns the option setting the role of the authenticated user.
func WithUserRole(role string) UserMonitoringOption {
return func(s Span) {
s.SetTag("usr.role", role)
}
}

// WithUserScope returns the option setting the scope (authorizations) of the authenticated user
func WithUserScope(scope string) UserMonitoringOption {
return func(s Span) {
s.SetTag("usr.scope", scope)
}
}
17 changes: 17 additions & 0 deletions ddtrace/tracer/tracer.go
Expand Up @@ -153,6 +153,23 @@ func Inject(ctx ddtrace.SpanContext, carrier interface{}) error {
return internal.GetGlobalTracer().Inject(ctx, carrier)
}

// SetUser associates user information to the current trace which the
// provided span belongs to. The options can be used to tune which user
// bit of information gets monitored.
func SetUser(s Span, id string, opts ...UserMonitoringOption) {
if s == nil {
return
}
if span, ok := s.(*span); ok && span.context != nil {
span = span.context.trace.root
s = span
}
s.SetTag("usr.id", id)
for _, fn := range opts {
fn(s)
}
}

// payloadQueueSize is the buffer size of the trace channel.
const payloadQueueSize = 1000

Expand Down
41 changes: 41 additions & 0 deletions ddtrace/tracer/tracer_test.go
Expand Up @@ -1694,6 +1694,47 @@ func TestTakeStackTrace(t *testing.T) {
})
}

func TestUserMonitoring(t *testing.T) {
const id = "john.doe#12345"
const name = "John Doe"
const email = "john.doe@hostname.com"
const scope = "read:message, write:files"
const role = "admin"
const sessionID = "session#12345"
expected := []struct{ key, value string }{
{key: "usr.id", value: id},
{key: "usr.name", value: name},
{key: "usr.email", value: email},
{key: "usr.scope", value: scope},
{key: "usr.role", value: role},
{key: "usr.session_id", value: sessionID},
}
tr := newTracer()
defer tr.Stop()

t.Run("root", func(t *testing.T) {
s := tr.newRootSpan("root", "test", "test")
SetUser(s, id, WithUserEmail(email), WithUserName(name), WithUserScope(scope),
WithUserRole(role), WithUserSessionID(sessionID))
s.Finish()
for _, pair := range expected {
assert.Equal(t, pair.value, s.Meta[pair.key])
}
})

t.Run("nested", func(t *testing.T) {
root := tr.newRootSpan("root", "test", "test")
child := tr.newChildSpan("child", root)
SetUser(child, id, WithUserEmail(email), WithUserName(name), WithUserScope(scope),
WithUserRole(role), WithUserSessionID(sessionID))
child.Finish()
root.Finish()
for _, pair := range expected {
assert.Equal(t, pair.value, root.Meta[pair.key])
}
})
}

// BenchmarkTracerStackFrames tests the performance of taking stack trace.
func BenchmarkTracerStackFrames(b *testing.B) {
tracer, _, _, stop := startTestTracer(b, WithSampler(NewRateSampler(0)))
Expand Down