diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a7c168cf..671620168 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Features - Support for `before_send_log` ([#2634](https://github.com/getsentry/sentry-ruby/pull/2634)) +- Default user attributes are now automatically added to logs ([#2647](https://github.com/getsentry/sentry-ruby/pull/2647)) ### Bug Fixes diff --git a/sentry-ruby/lib/sentry/log_event.rb b/sentry-ruby/lib/sentry/log_event.rb index e7981379c..83b280320 100644 --- a/sentry-ruby/lib/sentry/log_event.rb +++ b/sentry-ruby/lib/sentry/log_event.rb @@ -32,9 +32,15 @@ class LogEvent "sentry.message.template" => :template } + USER_ATTRIBUTES = { + "user.id" => :user_id, + "user.name" => :user_username, + "user.email" => :user_email + } + LEVELS = %i[trace debug info warn error fatal].freeze - attr_accessor :level, :body, :template, :attributes + attr_accessor :level, :body, :template, :attributes, :user attr_reader :configuration, *SERIALIZEABLE_ATTRIBUTES @@ -47,6 +53,9 @@ class LogEvent sdk_version timestamp trace_id + user_id + user_username + user_email ].map { |name| [name, :"serialize_#{name}"] }.to_h VALUE_TYPES = Hash.new("string").merge!({ @@ -69,6 +78,7 @@ def initialize(configuration: Sentry.configuration, **options) @body = options[:body] @template = @body if is_template? @attributes = options[:attributes] || DEFAULT_ATTRIBUTES + @user = options[:user] || {} @contexts = {} end @@ -124,6 +134,18 @@ def serialize_body end end + def serialize_user_id + user[:id] + end + + def serialize_user_username + user[:username] + end + + def serialize_user_email + user[:email] + end + def serialize_attributes hash = {} @@ -137,6 +159,12 @@ def serialize_attributes end end + USER_ATTRIBUTES.each do |key, name| + if (value = serialize(name)) + hash[key] = value + end + end + hash end diff --git a/sentry-ruby/lib/sentry/scope.rb b/sentry-ruby/lib/sentry/scope.rb index 235cbf4b5..cb34a6276 100644 --- a/sentry-ruby/lib/sentry/scope.rb +++ b/sentry-ruby/lib/sentry/scope.rb @@ -60,6 +60,10 @@ def apply_to_event(event, hint = nil) event.attachments = attachments end + if event.is_a?(LogEvent) + event.user = user.merge(event.user) + end + if span event.contexts[:trace] ||= span.get_trace_context diff --git a/sentry-ruby/spec/sentry/log_event_spec.rb b/sentry-ruby/spec/sentry/log_event_spec.rb index 331fdfa49..4d9fc3664 100644 --- a/sentry-ruby/spec/sentry/log_event_spec.rb +++ b/sentry-ruby/spec/sentry/log_event_spec.rb @@ -118,5 +118,26 @@ expect(hash[:attributes]["boolean_attr"]).to eq({ value: true, type: "boolean" }) expect(hash[:attributes]["float_attr"]).to eq({ value: 3.14, type: "double" }) end + + it "serializes user attributes correctly" do + user = { + id: 123, + username: "john_doe", + email: "john@example.com" + } + + event = described_class.new( + configuration: configuration, + level: :info, + body: "User action performed", + user: user + ) + + hash = event.to_hash + + expect(hash[:attributes]["user.id"]).to eq(123) + expect(hash[:attributes]["user.name"]).to eq("john_doe") + expect(hash[:attributes]["user.email"]).to eq("john@example.com") + end end end diff --git a/sentry-ruby/spec/sentry/scope_spec.rb b/sentry-ruby/spec/sentry/scope_spec.rb index 05e0eda17..59dec2ee1 100644 --- a/sentry-ruby/spec/sentry/scope_spec.rb +++ b/sentry-ruby/spec/sentry/scope_spec.rb @@ -207,6 +207,7 @@ let(:event) { client.event_from_message("test message") } let(:check_in_event) { client.event_from_check_in("test_slug", :ok) } + let(:log_event) { client.event_from_log("test log message", level: :info) } it "applies the contextual data to event" do subject.apply_to_event(event) @@ -238,6 +239,32 @@ expect(check_in_event.contexts).to include(:trace) end + context "with LogEvent" do + it "adds user attributes to log event" do + scope = described_class.new + scope.set_user({ id: 123, username: "john_doe", email: "john@example.com" }) + + scope.apply_to_event(log_event) + + log_hash = log_event.to_hash + + expect(log_hash[:attributes]["user.id"]).to eq(123) + expect(log_hash[:attributes]["user.name"]).to eq("john_doe") + expect(log_hash[:attributes]["user.email"]).to eq("john@example.com") + end + + it "does not add user attributes when user is empty" do + scope = described_class.new + scope.apply_to_event(log_event) + + log_hash = log_event.to_hash + + expect(log_hash[:attributes]).not_to have_key("user.id") + expect(log_hash[:attributes]).not_to have_key("user.name") + expect(log_hash[:attributes]).not_to have_key("user.email") + end + end + it "doesn't override event's pre-existing data" do event.tags = { foo: "baz" } event.user = { id: 2 }