diff --git a/CHANGELOG.md b/CHANGELOG.md index e8ddfb3d0..4a7c168cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ - Support for `before_send_log` ([#2634](https://github.com/getsentry/sentry-ruby/pull/2634)) +### Bug Fixes + +- Structured logging consumes way less memory now ([#2643](https://github.com/getsentry/sentry-ruby/pull/2643)) + ## 5.24.0 ### Features diff --git a/sentry-ruby/lib/sentry/client.rb b/sentry-ruby/lib/sentry/client.rb index 9e1b93098..27dff3f92 100644 --- a/sentry-ruby/lib/sentry/client.rb +++ b/sentry-ruby/lib/sentry/client.rb @@ -195,12 +195,7 @@ def event_from_log(message, level:, **options) attributes = options.reject { |k, _| k == :level || k == :severity } - LogEvent.new( - level: level, - body: message, - timestamp: Time.now.to_f, - attributes: attributes - ) + LogEvent.new(level: level, body: message, attributes: attributes) end # Initializes an Event object with the given Transaction object. diff --git a/sentry-ruby/lib/sentry/log_event.rb b/sentry-ruby/lib/sentry/log_event.rb index a66828531..e7981379c 100644 --- a/sentry-ruby/lib/sentry/log_event.rb +++ b/sentry-ruby/lib/sentry/log_event.rb @@ -4,19 +4,22 @@ module Sentry # Event type that represents a log entry with its attributes # # @see https://develop.sentry.dev/sdk/telemetry/logs/#log-envelope-item-payload - class LogEvent < Event + class LogEvent TYPE = "log" DEFAULT_PARAMETERS = [].freeze DEFAULT_ATTRIBUTES = {}.freeze - DEFAULT_CONTEXT = {}.freeze SERIALIZEABLE_ATTRIBUTES = %i[ level body timestamp + environment + release + server_name trace_id attributes + contexts ] SENTRY_ATTRIBUTES = { @@ -33,15 +36,40 @@ class LogEvent < Event attr_accessor :level, :body, :template, :attributes - def initialize(configuration: Sentry.configuration, **options) - super(configuration: configuration) + attr_reader :configuration, *SERIALIZEABLE_ATTRIBUTES + + SERIALIZERS = %i[ + attributes + body + level + parent_span_id + sdk_name + sdk_version + timestamp + trace_id + ].map { |name| [name, :"serialize_#{name}"] }.to_h + + VALUE_TYPES = Hash.new("string").merge!({ + TrueClass => "boolean", + FalseClass => "boolean", + Integer => "integer", + Float => "double" + }).freeze + TOKEN_REGEXP = /%\{(\w+)\}/ + + def initialize(configuration: Sentry.configuration, **options) + @configuration = configuration @type = TYPE + @server_name = configuration.server_name + @environment = configuration.environment + @release = configuration.release + @timestamp = Sentry.utc_now @level = options.fetch(:level) @body = options[:body] @template = @body if is_template? @attributes = options[:attributes] || DEFAULT_ATTRIBUTES - @contexts = DEFAULT_CONTEXT + @contexts = {} end def to_hash @@ -53,9 +81,9 @@ def to_hash private def serialize(name) - serializer = :"serialize_#{name}" + serializer = SERIALIZERS[name] - if respond_to?(serializer, true) + if serializer __send__(serializer) else public_send(name) @@ -75,7 +103,7 @@ def serialize_sdk_version end def serialize_timestamp - Time.parse(timestamp).to_f + timestamp.to_f end def serialize_trace_id @@ -97,8 +125,10 @@ def serialize_body end def serialize_attributes - hash = attributes.each_with_object({}) do |(key, value), memo| - memo[key] = attribute_hash(value) + hash = {} + + attributes.each do |key, value| + hash[key] = attribute_hash(value) end SENTRY_ATTRIBUTES.each do |key, name| @@ -115,16 +145,7 @@ def attribute_hash(value) end def value_type(value) - case value - when Integer - "integer" - when TrueClass, FalseClass - "boolean" - when Float - "double" - else - "string" - end + VALUE_TYPES[value.class] end def parameters @@ -146,8 +167,6 @@ def parameters end end - TOKEN_REGEXP = /%\{(\w+)\}/ - def template_tokens @template_tokens ||= body.scan(TOKEN_REGEXP).flatten.map(&:to_sym) end diff --git a/sentry-ruby/lib/sentry/scope.rb b/sentry-ruby/lib/sentry/scope.rb index 68dfc079c..235cbf4b5 100644 --- a/sentry-ruby/lib/sentry/scope.rb +++ b/sentry-ruby/lib/sentry/scope.rb @@ -46,7 +46,7 @@ def clear # @param hint [Hash] the hint data that'll be passed to event processors. # @return [Event] def apply_to_event(event, hint = nil) - unless event.is_a?(CheckInEvent) + unless event.is_a?(CheckInEvent) || event.is_a?(LogEvent) event.tags = tags.merge(event.tags) event.user = user.merge(event.user) event.extra = extra.merge(event.extra) @@ -54,7 +54,7 @@ def apply_to_event(event, hint = nil) event.transaction = transaction_name if transaction_name event.transaction_info = { source: transaction_source } if transaction_source event.fingerprint = fingerprint - event.level = level unless event.is_a?(LogEvent) + event.level = level event.breadcrumbs = breadcrumbs event.rack_env = rack_env if rack_env event.attachments = attachments @@ -62,10 +62,16 @@ def apply_to_event(event, hint = nil) if span event.contexts[:trace] ||= span.get_trace_context - event.dynamic_sampling_context ||= span.get_dynamic_sampling_context + + if event.respond_to?(:dynamic_sampling_context) + event.dynamic_sampling_context ||= span.get_dynamic_sampling_context + end else event.contexts[:trace] ||= propagation_context.get_trace_context - event.dynamic_sampling_context ||= propagation_context.get_dynamic_sampling_context + + if event.respond_to?(:dynamic_sampling_context) + event.dynamic_sampling_context ||= propagation_context.get_dynamic_sampling_context + end end all_event_processors = self.class.global_event_processors + @event_processors