Skip to content

Commit

Permalink
ddtrace/tracer: add user monitoring to tracer (#1196)
Browse files Browse the repository at this point in the history
  • Loading branch information
Hellzy committed Mar 7, 2022
1 parent fad83bc commit 94d830e
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 0 deletions.
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

0 comments on commit 94d830e

Please sign in to comment.