Skip to content

Commit 83d69f3

Browse files
committed
Unify Logs and Metrics implementations
* Make `LogEvent` go through `scope.apply_to_telemetry` * Populate default and user attributes in `scope.apply_to_telemetry` * Simplify LogEvent to be more simpler with serialization logic, remove all unnecessary fields
1 parent 7eda574 commit 83d69f3

File tree

11 files changed

+212
-309
lines changed

11 files changed

+212
-309
lines changed

sentry-ruby/lib/sentry/client.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def capture_event(event, scope, hint = {})
107107
# @return [LogEvent]
108108
def buffer_log_event(event, scope)
109109
return unless event.is_a?(LogEvent)
110-
@log_event_buffer.add_event(scope.apply_to_event(event))
110+
@log_event_buffer.add_event(scope.apply_to_telemetry(event))
111111
event
112112
end
113113

sentry-ruby/lib/sentry/log_event.rb

Lines changed: 22 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -1,129 +1,53 @@
11
# frozen_string_literal: true
22

3-
require "json"
3+
require "sentry/utils/telemetry_attributes"
44

55
module Sentry
66
# Event type that represents a log entry with its attributes
77
#
88
# @see https://develop.sentry.dev/sdk/telemetry/logs/#log-envelope-item-payload
99
class LogEvent
10+
include Sentry::TelemetryAttributes
11+
1012
TYPE = "log"
1113

1214
DEFAULT_PARAMETERS = [].freeze
1315
DEFAULT_ATTRIBUTES = {}.freeze
1416

15-
SERIALIZEABLE_ATTRIBUTES = %i[
16-
level
17-
body
18-
timestamp
19-
environment
20-
release
21-
server_name
22-
trace_id
23-
attributes
24-
contexts
25-
]
26-
27-
SENTRY_ATTRIBUTES = {
28-
"sentry.trace.parent_span_id" => :parent_span_id,
29-
"sentry.environment" => :environment,
30-
"sentry.release" => :release,
31-
"sentry.address" => :server_name,
32-
"sentry.sdk.name" => :sdk_name,
33-
"sentry.sdk.version" => :sdk_version,
34-
"sentry.message.template" => :template,
35-
"sentry.origin" => :origin
36-
}
37-
3817
PARAMETER_PREFIX = "sentry.message.parameter"
3918

40-
USER_ATTRIBUTES = {
41-
"user.id" => :user_id,
42-
"user.name" => :user_username,
43-
"user.email" => :user_email
44-
}
45-
4619
LEVELS = %i[trace debug info warn error fatal].freeze
4720

48-
attr_accessor :level, :body, :template, :attributes, :user, :origin
49-
50-
attr_reader :configuration, *(SERIALIZEABLE_ATTRIBUTES - %i[level body attributes])
51-
52-
SERIALIZERS = %i[
53-
attributes
54-
body
55-
level
56-
parent_span_id
57-
sdk_name
58-
sdk_version
59-
template
60-
timestamp
61-
trace_id
62-
user_id
63-
user_username
64-
user_email
65-
].map { |name| [name, :"serialize_#{name}"] }.to_h
21+
attr_accessor :level, :body, :template, :attributes, :origin, :trace_id, :span_id
22+
attr_reader :timestamp
6623

6724
TOKEN_REGEXP = /%\{(\w+)\}/
6825

69-
def initialize(configuration: Sentry.configuration, **options)
70-
@configuration = configuration
26+
def initialize(**options)
7127
@type = TYPE
72-
@server_name = configuration.server_name
73-
@environment = configuration.environment
74-
@release = configuration.release
7528
@timestamp = Sentry.utc_now
7629
@level = options.fetch(:level)
7730
@body = options[:body]
7831
@template = @body if is_template?
7932
@attributes = options[:attributes] || DEFAULT_ATTRIBUTES
80-
@user = options[:user] || {}
8133
@origin = options[:origin]
82-
@contexts = {}
34+
@trace_id = nil
35+
@span_id = nil
8336
end
8437

8538
def to_h
86-
SERIALIZEABLE_ATTRIBUTES.each_with_object({}) do |name, memo|
87-
memo[name] = serialize(name)
88-
end
39+
{
40+
level: level.to_s,
41+
timestamp: timestamp.to_f,
42+
trace_id: @trace_id,
43+
span_id: @span_id,
44+
body: serialize_body,
45+
attributes: serialize_attributes
46+
}.compact
8947
end
9048

9149
private
9250

93-
def serialize(name)
94-
serializer = SERIALIZERS[name]
95-
96-
if serializer
97-
__send__(serializer)
98-
else
99-
public_send(name)
100-
end
101-
end
102-
103-
def serialize_level
104-
level.to_s
105-
end
106-
107-
def serialize_sdk_name
108-
Sentry.sdk_meta["name"]
109-
end
110-
111-
def serialize_sdk_version
112-
Sentry.sdk_meta["version"]
113-
end
114-
115-
def serialize_timestamp
116-
timestamp.to_f
117-
end
118-
119-
def serialize_trace_id
120-
contexts.dig(:trace, :trace_id)
121-
end
122-
123-
def serialize_parent_span_id
124-
contexts.dig(:trace, :parent_span_id)
125-
end
126-
12751
def serialize_body
12852
if parameters.empty?
12953
body
@@ -134,61 +58,15 @@ def serialize_body
13458
end
13559
end
13660

137-
def serialize_user_id
138-
user[:id]
139-
end
140-
141-
def serialize_user_username
142-
user[:username]
143-
end
144-
145-
def serialize_user_email
146-
user[:email]
147-
end
148-
149-
def serialize_template
150-
template if has_parameters?
151-
end
152-
15361
def serialize_attributes
154-
hash = {}
155-
156-
attributes.each do |key, value|
157-
hash[key] = attribute_hash(value)
158-
end
159-
160-
SENTRY_ATTRIBUTES.each do |key, name|
161-
if (value = serialize(name))
162-
hash[key] = attribute_hash(value)
163-
end
164-
end
165-
166-
USER_ATTRIBUTES.each do |key, name|
167-
if (value = serialize(name))
168-
hash[key] = value
169-
end
170-
end
171-
172-
hash
62+
sentry_attributes.merge(@attributes).transform_values { |v| attribute_hash(v) }
17363
end
17464

175-
def attribute_hash(value)
176-
case value
177-
when String
178-
{ value: value, type: "string" }
179-
when TrueClass, FalseClass
180-
{ value: value, type: "boolean" }
181-
when Integer
182-
{ value: value, type: "integer" }
183-
when Float
184-
{ value: value, type: "double" }
185-
else
186-
begin
187-
{ value: JSON.generate(value), type: "string" }
188-
rescue
189-
{ value: value, type: "string" }
190-
end
191-
end
65+
def sentry_attributes
66+
sentry_attributes = {}
67+
sentry_attributes["sentry.origin"] = @origin if @origin
68+
sentry_attributes["sentry.message.template"] = template if has_parameters?
69+
sentry_attributes
19270
end
19371

19472
def parameters
Lines changed: 10 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
# frozen_string_literal: true
22

3+
require "sentry/utils/telemetry_attributes"
4+
35
module Sentry
46
class MetricEvent
5-
attr_reader :name, :type, :value, :unit, :timestamp, :trace_id, :span_id, :attributes, :user
6-
attr_writer :trace_id, :span_id, :attributes, :user
7+
include Sentry::TelemetryAttributes
8+
9+
DEFAULT_ATTRIBUTES = {}.freeze
10+
11+
attr_reader :name, :type, :value, :unit, :timestamp, :trace_id, :span_id, :attributes
12+
attr_writer :trace_id, :span_id, :attributes
713

814
def initialize(
915
name:,
@@ -16,18 +22,14 @@ def initialize(
1622
@type = type
1723
@value = value
1824
@unit = unit
19-
@attributes = attributes || {}
25+
@attributes = attributes || DEFAULT_ATTRIBUTES
2026

2127
@timestamp = Sentry.utc_now
2228
@trace_id = nil
2329
@span_id = nil
24-
@user = {}
2530
end
2631

2732
def to_h
28-
populate_default_attributes!
29-
populate_user_attributes!
30-
3133
{
3234
name: @name,
3335
type: @type,
@@ -42,44 +44,8 @@ def to_h
4244

4345
private
4446

45-
def populate_default_attributes!
46-
configuration = Sentry.configuration
47-
return unless configuration
48-
49-
default_attributes = {
50-
"sentry.environment" => configuration.environment,
51-
"sentry.release" => configuration.release,
52-
"sentry.sdk.name" => Sentry.sdk_meta["name"],
53-
"sentry.sdk.version" => Sentry.sdk_meta["version"],
54-
"server.address" => configuration.server_name
55-
}.compact
56-
57-
@attributes = default_attributes.merge(@attributes)
58-
end
59-
60-
def populate_user_attributes!
61-
return unless @user
62-
return unless Sentry.initialized? && Sentry.configuration.send_default_pii
63-
64-
user_attributes = {
65-
"user.id" => @user[:id],
66-
"user.name" => @user[:username],
67-
"user.email" => @user[:email]
68-
}.compact
69-
70-
@attributes = user_attributes.merge(@attributes)
71-
end
72-
7347
def serialize_attributes
74-
@attributes.transform_values do |v|
75-
case v
76-
when Integer then { type: "integer", value: v }
77-
when Float then { type: "double", value: v }
78-
when TrueClass, FalseClass then { type: "boolean", value: v }
79-
when String then { type: "string", value: v }
80-
else { type: "string", value: v.to_s }
81-
end
82-
end
48+
@attributes.transform_values { |v| attribute_hash(v) }
8349
end
8450
end
8551
end

sentry-ruby/lib/sentry/scope.rb

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,18 +92,39 @@ def apply_to_event(event, hint = nil)
9292
# A leaner version of apply_to_event that applies to
9393
# lightweight payloads like Logs and Metrics.
9494
#
95-
# Only adds trace_id, span_id and user from the scope.
95+
# Adds trace_id, span_id, user from the scope and default attributes from configuration.
9696
#
97-
# @param telemetry [MetricEvent]
98-
# @return [MetricEvent]
97+
# @param telemetry [MetricEvent, LogEvent] the telemetry event to apply scope context to
98+
# @return [MetricEvent, LogEvent] the telemetry event with scope context applied
9999
def apply_to_telemetry(telemetry)
100100
# TODO-neel when new scope set_attribute api is added: add them here
101-
telemetry.user = user.merge(telemetry.user)
102-
103101
trace_context = span ? span.get_trace_context : propagation_context.get_trace_context
104102
telemetry.trace_id = trace_context[:trace_id]
105103
telemetry.span_id = trace_context[:span_id]
106104

105+
configuration = Sentry.configuration
106+
return telemetry unless configuration
107+
108+
default_attributes = {
109+
"sentry.environment" => configuration.environment,
110+
"sentry.release" => configuration.release,
111+
"sentry.sdk.name" => Sentry.sdk_meta["name"],
112+
"sentry.sdk.version" => Sentry.sdk_meta["version"],
113+
"server.address" => configuration.server_name
114+
}.compact
115+
116+
telemetry.attributes = default_attributes.merge(telemetry.attributes)
117+
118+
if configuration.send_default_pii && !user.empty?
119+
user_attributes = {
120+
"user.id" => user[:id],
121+
"user.name" => user[:username],
122+
"user.email" => user[:email]
123+
}.compact
124+
125+
telemetry.attributes = user_attributes.merge(telemetry.attributes)
126+
end
127+
107128
telemetry
108129
end
109130

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# frozen_string_literal: true
2+
3+
require "json"
4+
5+
module Sentry
6+
module TelemetryAttributes
7+
private
8+
9+
def attribute_hash(value)
10+
case value
11+
when String
12+
{ value: value, type: "string" }
13+
when TrueClass, FalseClass
14+
{ value: value, type: "boolean" }
15+
when Integer
16+
{ value: value, type: "integer" }
17+
when Float
18+
{ value: value, type: "double" }
19+
else
20+
begin
21+
{ value: JSON.generate(value), type: "string" }
22+
rescue
23+
{ value: value, type: "string" }
24+
end
25+
end
26+
end
27+
end
28+
end

sentry-ruby/spec/sentry/log_event_buffer_spec.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
let(:client) { double(Sentry::Client) }
99
let(:log_event) do
1010
Sentry::LogEvent.new(
11-
configuration: Sentry.configuration,
1211
level: :info,
1312
body: "Test message"
1413
)

0 commit comments

Comments
 (0)