sentry-ruby 5.23.0 → 5.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '09ca0c97390e688d58572ffe3ae022523a1003a48ccea319857b302b3e1ebb95'
4
- data.tar.gz: 28f154fb38f21c50090dc8ae3c2bc7b335f288a298e97d934a24db1b8e7ac372
3
+ metadata.gz: d7397a94c353821b827cd429f7e75cf1663f4bc3aa40e4407eef9f2f726a632f
4
+ data.tar.gz: 00efb1f46016d1db1756ff6ccdd15ae0af27d48278c5d1cec4a302a3805e80c4
5
5
  SHA512:
6
- metadata.gz: 5f170fe27327a1753f4a6f2d62b5a694f30c91f1f86c4e2caa837d70467298ea2af261475a2da1045e3513257f3a9be4bb66a0c2e0dba271e43a1478ecd42c0b
7
- data.tar.gz: 32a7f13e8eee4f7c8958dfd1a083aecf7ee306fec695703c5dbeff810e4ae5ebd84a1435e7edaef8a61d94eaf64910442422d2142007d77c5be31080ad32f244
6
+ metadata.gz: de1e9f756456d324ce92be08686ae0b0101d306f539bd2c0212490d5cc3816e80fca8ed7a76f697edf58950e5445e65a4101934de05251559b3042edc3bc2a75
7
+ data.tar.gz: 2c3b81765ea431073d60c41c289204d3bfae2ad7b6754147ef80414bd40c6ecd11d79a4efbf4af3fd148439840205f6f0ca6756a40c349553d370e545d4b0d62
data/.rspec CHANGED
@@ -1,2 +1,4 @@
1
- --format documentation
1
+ --require spec_helper
2
+ --format progress
2
3
  --color
4
+ --order rand
data/README.md CHANGED
@@ -15,12 +15,12 @@ Sentry SDK for Ruby
15
15
 
16
16
  | Current version | Build | Coverage | API doc |
17
17
  | ---------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
18
- | [![Gem Version](https://img.shields.io/gem/v/sentry-ruby?label=sentry-ruby)](https://rubygems.org/gems/sentry-ruby) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_ruby_test.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_ruby_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![API doc](https://img.shields.io/badge/API%20doc-rubydoc.info-blue)](https://www.rubydoc.info/gems/sentry-ruby) |
19
- | [![Gem Version](https://img.shields.io/gem/v/sentry-rails?label=sentry-rails)](https://rubygems.org/gems/sentry-rails) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_rails_test.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_rails_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![API doc](https://img.shields.io/badge/API%20doc-rubydoc.info-blue)](https://www.rubydoc.info/gems/sentry-rails) |
20
- | [![Gem Version](https://img.shields.io/gem/v/sentry-sidekiq?label=sentry-sidekiq)](https://rubygems.org/gems/sentry-sidekiq) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_sidekiq_test.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_sidekiq_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![API doc](https://img.shields.io/badge/API%20doc-rubydoc.info-blue)](https://www.rubydoc.info/gems/sentry-sidekiq) |
21
- | [![Gem Version](https://img.shields.io/gem/v/sentry-delayed_job?label=sentry-delayed_job)](https://rubygems.org/gems/sentry-delayed_job) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_delayed_job_test.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_delayed_job_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![API doc](https://img.shields.io/badge/API%20doc-rubydoc.info-blue)](https://www.rubydoc.info/gems/sentry-delayed_job) |
22
- | [![Gem Version](https://img.shields.io/gem/v/sentry-resque?label=sentry-resque)](https://rubygems.org/gems/sentry-resque) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_resque_test.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_resque_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![API doc](https://img.shields.io/badge/API%20doc-rubydoc.info-blue)](https://www.rubydoc.info/gems/sentry-resque) |
23
- | [![Gem Version](https://img.shields.io/gem/v/sentry-opentelemetry?label=sentry-opentelemetry)](https://rubygems.org/gems/sentry-opentelemetry) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_opentelemetry_test.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_opentelemetry_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![API doc](https://img.shields.io/badge/API%20doc-rubydoc.info-blue)](https://www.rubydoc.info/gems/sentry-opentelemetry) |
18
+ | [![Gem Version](https://img.shields.io/gem/v/sentry-ruby?label=sentry-ruby)](https://rubygems.org/gems/sentry-ruby) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/tests.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/tests.yml) | [![codecov](https://codecov.io/gh/getsentry/sentry-ruby/graph/badge.svg?token=ZePzrpZFP6&component=sentry-ruby)](https://codecov.io/gh/getsentry/sentry-ruby) | [![API doc](https://img.shields.io/badge/API%20doc-rubydoc.info-blue)](https://www.rubydoc.info/gems/sentry-ruby) |
19
+ | [![Gem Version](https://img.shields.io/gem/v/sentry-rails?label=sentry-rails)](https://rubygems.org/gems/sentry-rails) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/tests.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/tests.yml) | [![codecov](https://codecov.io/gh/getsentry/sentry-ruby/graph/badge.svg?token=ZePzrpZFP6&component=sentry-rails)](https://codecov.io/gh/getsentry/sentry-ruby) | [![API doc](https://img.shields.io/badge/API%20doc-rubydoc.info-blue)](https://www.rubydoc.info/gems/sentry-rails) |
20
+ | [![Gem Version](https://img.shields.io/gem/v/sentry-sidekiq?label=sentry-sidekiq)](https://rubygems.org/gems/sentry-sidekiq) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/tests.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/tests.yml) | [![codecov](https://codecov.io/gh/getsentry/sentry-ruby/graph/badge.svg?token=ZePzrpZFP6&component=sentry-sidekiq)](https://codecov.io/gh/getsentry/sentry-ruby) | [![API doc](https://img.shields.io/badge/API%20doc-rubydoc.info-blue)](https://www.rubydoc.info/gems/sentry-sidekiq) |
21
+ | [![Gem Version](https://img.shields.io/gem/v/sentry-delayed_job?label=sentry-delayed_job)](https://rubygems.org/gems/sentry-delayed_job) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/tests.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/tests.yml) | [![codecov](https://codecov.io/gh/getsentry/sentry-ruby/graph/badge.svg?token=ZePzrpZFP6&component=sentry-delayed_job)](https://codecov.io/gh/getsentry/sentry-ruby) | [![API doc](https://img.shields.io/badge/API%20doc-rubydoc.info-blue)](https://www.rubydoc.info/gems/sentry-delayed_job) |
22
+ | [![Gem Version](https://img.shields.io/gem/v/sentry-resque?label=sentry-resque)](https://rubygems.org/gems/sentry-resque) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/tests.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/tests.yml) | [![codecov](https://codecov.io/gh/getsentry/sentry-ruby/graph/badge.svg?token=ZePzrpZFP6&component=sentry-resque)](https://codecov.io/gh/getsentry/sentry-ruby) | [![API doc](https://img.shields.io/badge/API%20doc-rubydoc.info-blue)](https://www.rubydoc.info/gems/sentry-resque) |
23
+ | [![Gem Version](https://img.shields.io/gem/v/sentry-opentelemetry?label=sentry-opentelemetry)](https://rubygems.org/gems/sentry-opentelemetry) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/tests.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/tests.yml) | [![codecov](https://codecov.io/gh/getsentry/sentry-ruby/graph/badge.svg?token=ZePzrpZFP6&component=sentry-opentelemetry)](https://codecov.io/gh/getsentry/sentry-ruby) | [![API doc](https://img.shields.io/badge/API%20doc-rubydoc.info-blue)](https://www.rubydoc.info/gems/sentry-opentelemetry) |
24
24
 
25
25
 
26
26
 
@@ -59,6 +59,8 @@ gem "sentry-opentelemetry"
59
59
 
60
60
  You need to use Sentry.init to initialize and configure your SDK:
61
61
  ```ruby
62
+ require "sentry-ruby"
63
+
62
64
  Sentry.init do |config|
63
65
  config.dsn = "MY_DSN"
64
66
  end
@@ -116,3 +118,6 @@ Thanks to everyone who has contributed to this project so far.
116
118
  <a href="https://github.com/getsentry/sentry-ruby/graphs/contributors">
117
119
  <img src="https://contributors-img.web.app/image?repo=getsentry/sentry-ruby" />
118
120
  </a>
121
+
122
+ > [!WARNING]
123
+ > Example and sample code in sentry-rails/examples and sentry-rails/spec/dummy is unmaintained. Sample code may contain security vulnerabilities, should never be used in production, and exists only for illustrative purposes.
data/Rakefile CHANGED
@@ -8,15 +8,14 @@ Bundler::GemHelper.install_tasks(name: "sentry-ruby")
8
8
 
9
9
  require "rspec/core/rake_task"
10
10
 
11
+ ISOLATED_SPECS = "spec/isolated/**/*_spec.rb"
12
+
11
13
  RSpec::Core::RakeTask.new(:spec).tap do |task|
12
- task.rspec_opts = "--order rand"
13
- task.exclude_pattern = "spec/isolated/**/*_spec.rb"
14
+ task.exclude_pattern = ISOLATED_SPECS
14
15
  end
15
16
 
16
- task :isolated_specs do
17
- Dir["spec/isolated/*"].each do |file|
18
- sh "bundle exec rspec #{file}"
19
- end
17
+ RSpec::Core::RakeTask.new(:isolated_specs).tap do |task|
18
+ task.pattern = ISOLATED_SPECS
20
19
  end
21
20
 
22
21
  task default: [:spec, :isolated_specs]
@@ -9,8 +9,7 @@ module Sentry
9
9
  include LoggingHelper
10
10
 
11
11
  attr_reader :max_queue, :number_of_threads
12
- # @deprecated Use Sentry.logger to retrieve the current logger instead.
13
- attr_reader :logger
12
+
14
13
  attr_accessor :shutdown_timeout
15
14
 
16
15
  DEFAULT_MAX_QUEUE = 30
@@ -19,7 +18,7 @@ module Sentry
19
18
  @shutdown_timeout = 1
20
19
  @number_of_threads = configuration.background_worker_threads
21
20
  @max_queue = configuration.background_worker_max_queue
22
- @logger = configuration.logger
21
+ @sdk_logger = configuration.sdk_logger
23
22
  @debug = configuration.debug
24
23
  @shutdown_callback = nil
25
24
 
@@ -6,7 +6,7 @@ module Sentry
6
6
  MAX_DOWNSAMPLE_FACTOR = 10
7
7
 
8
8
  def initialize(configuration, client, interval: DEFAULT_INTERVAL)
9
- super(configuration.logger, interval)
9
+ super(configuration.sdk_logger, interval)
10
10
  @client = client
11
11
 
12
12
  @healthy = true
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Sentry
4
4
  class Breadcrumb
5
+ MAX_NESTING = 10
5
6
  DATA_SERIALIZATION_ERROR_MESSAGE = "[data were removed due to serialization issues]"
6
7
 
7
8
  # @return [String, nil]
@@ -47,7 +48,7 @@ module Sentry
47
48
  # @param message [String]
48
49
  # @return [void]
49
50
  def message=(message)
50
- @message = message ? message.byteslice(0..Event::MAX_MESSAGE_SIZE_IN_BYTES) : ""
51
+ @message = message && Utils::EncodingHelper.valid_utf_8?(message) ? message.byteslice(0..Event::MAX_MESSAGE_SIZE_IN_BYTES) : ""
51
52
  end
52
53
 
53
54
  # @param level [String]
@@ -60,16 +61,16 @@ module Sentry
60
61
 
61
62
  def serialized_data
62
63
  begin
63
- ::JSON.parse(::JSON.generate(@data))
64
+ ::JSON.parse(::JSON.generate(@data, max_nesting: MAX_NESTING))
64
65
  rescue Exception => e
65
- Sentry.logger.debug(LOGGER_PROGNAME) do
66
+ Sentry.sdk_logger.debug(LOGGER_PROGNAME) do
66
67
  <<~MSG
67
68
  can't serialize breadcrumb data because of error: #{e}
68
69
  data: #{@data}
69
70
  MSG
70
71
  end
71
72
 
72
- DATA_SERIALIZATION_ERROR_MESSAGE
73
+ { error: DATA_SERIALIZATION_ERROR_MESSAGE }
73
74
  end
74
75
  end
75
76
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "securerandom"
4
4
  require "sentry/cron/monitor_config"
5
+ require "sentry/utils/uuid"
5
6
 
6
7
  module Sentry
7
8
  class CheckInEvent < Event
@@ -43,7 +44,7 @@ module Sentry
43
44
  self.status = status
44
45
  self.duration = duration
45
46
  self.monitor_config = monitor_config
46
- self.check_in_id = check_in_id || SecureRandom.uuid.delete("-")
47
+ self.check_in_id = check_in_id || Utils.uuid
47
48
  end
48
49
 
49
50
  # @return [Hash]
data/lib/sentry/client.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "sentry/transport"
4
+ require "sentry/log_event"
5
+ require "sentry/log_event_buffer"
6
+ require "sentry/utils/uuid"
4
7
 
5
8
  module Sentry
6
9
  class Client
@@ -14,16 +17,16 @@ module Sentry
14
17
  # @return [SpotlightTransport, nil]
15
18
  attr_reader :spotlight_transport
16
19
 
20
+ # @!visibility private
21
+ attr_reader :log_event_buffer
22
+
17
23
  # @!macro configuration
18
24
  attr_reader :configuration
19
25
 
20
- # @deprecated Use Sentry.logger to retrieve the current logger instead.
21
- attr_reader :logger
22
-
23
26
  # @param configuration [Configuration]
24
27
  def initialize(configuration)
25
28
  @configuration = configuration
26
- @logger = configuration.logger
29
+ @sdk_logger = configuration.sdk_logger
27
30
 
28
31
  if transport_class = configuration.transport.transport_class
29
32
  @transport = transport_class.new(configuration)
@@ -38,6 +41,8 @@ module Sentry
38
41
  end
39
42
 
40
43
  @spotlight_transport = SpotlightTransport.new(configuration) if configuration.spotlight
44
+
45
+ @log_event_buffer = LogEventBuffer.new(configuration, self).start
41
46
  end
42
47
 
43
48
  # Applies the given scope's data to the event and sends it to Sentry.
@@ -88,6 +93,15 @@ module Sentry
88
93
  nil
89
94
  end
90
95
 
96
+ # Buffer a log event to be sent later with other logs in a single envelope
97
+ # @param event [LogEvent] the log event to be buffered
98
+ # @return [LogEvent]
99
+ def buffer_log_event(event, scope)
100
+ return unless event.is_a?(LogEvent)
101
+ @log_event_buffer.add_event(scope.apply_to_event(event))
102
+ event
103
+ end
104
+
91
105
  # Capture an envelope directly.
92
106
  # @param envelope [Envelope] the envelope to be captured.
93
107
  # @return [void]
@@ -100,6 +114,7 @@ module Sentry
100
114
  def flush
101
115
  transport.flush if configuration.sending_to_dsn_allowed?
102
116
  spotlight_transport.flush if spotlight_transport
117
+ @log_event_buffer.flush
103
118
  end
104
119
 
105
120
  # Initializes an Event object with the given exception. Returns `nil` if the exception's class is excluded from reporting.
@@ -167,6 +182,22 @@ module Sentry
167
182
  )
168
183
  end
169
184
 
185
+ # Initializes a LogEvent object with the given message and options
186
+ #
187
+ # @param message [String] the log message
188
+ # @param level [Symbol] the log level (:trace, :debug, :info, :warn, :error, :fatal)
189
+ # @param options [Hash] additional options
190
+ # @option options [Array] :parameters Array of values to replace template tokens in the message
191
+ #
192
+ # @return [LogEvent] the created log event
193
+ def event_from_log(message, level:, **options)
194
+ return unless configuration.sending_allowed?
195
+
196
+ attributes = options.reject { |k, _| k == :level || k == :severity }
197
+
198
+ LogEvent.new(level: level, body: message, attributes: attributes)
199
+ end
200
+
170
201
  # Initializes an Event object with the given Transaction object.
171
202
  # @param transaction [Transaction] the transaction to be recorded.
172
203
  # @return [TransactionEvent]
@@ -237,6 +268,53 @@ module Sentry
237
268
  raise
238
269
  end
239
270
 
271
+ # Send an envelope with batched logs
272
+ # @param log_events [Array<LogEvent>] the log events to be sent
273
+ # @api private
274
+ # @return [void]
275
+ def send_logs(log_events)
276
+ envelope = Envelope.new(
277
+ event_id: Sentry::Utils.uuid,
278
+ sent_at: Sentry.utc_now.iso8601,
279
+ dsn: configuration.dsn,
280
+ sdk: Sentry.sdk_meta
281
+ )
282
+
283
+ discarded_count = 0
284
+ envelope_items = []
285
+
286
+ if configuration.before_send_log
287
+ log_events.each do |log_event|
288
+ processed_log_event = configuration.before_send_log.call(log_event)
289
+
290
+ if processed_log_event
291
+ envelope_items << processed_log_event.to_hash
292
+ else
293
+ discarded_count += 1
294
+ end
295
+ end
296
+
297
+ envelope_items
298
+ else
299
+ envelope_items = log_events.map(&:to_hash)
300
+ end
301
+
302
+ envelope.add_item(
303
+ {
304
+ type: "log",
305
+ item_count: envelope_items.size,
306
+ content_type: "application/vnd.sentry.items.log+json"
307
+ },
308
+ { items: envelope_items }
309
+ )
310
+
311
+ send_envelope(envelope)
312
+
313
+ unless discarded_count.zero?
314
+ transport.record_lost_event(:before_send, "log_item", num: discarded_count)
315
+ end
316
+ end
317
+
240
318
  # Send an envelope directly to Sentry.
241
319
  # @param envelope [Envelope] the envelope to be sent.
242
320
  # @return [void]
@@ -12,6 +12,8 @@ require "sentry/cron/configuration"
12
12
  require "sentry/metrics/configuration"
13
13
  require "sentry/linecache"
14
14
  require "sentry/interfaces/stacktrace_builder"
15
+ require "sentry/logger"
16
+ require "sentry/log_event_buffer"
15
17
 
16
18
  module Sentry
17
19
  class Configuration
@@ -99,6 +101,15 @@ module Sentry
99
101
  # @return [Proc]
100
102
  attr_reader :before_send_transaction
101
103
 
104
+ # Optional Proc, called before sending an event to the server
105
+ # @example
106
+ # config.before_send_log = lambda do |log|
107
+ # log.attributes["sentry"] = true
108
+ # log
109
+ # end
110
+ # @return [Proc]
111
+ attr_accessor :before_send_log
112
+
102
113
  # An array of breadcrumbs loggers to be used. Available options are:
103
114
  # - :sentry_logger
104
115
  # - :http_logger
@@ -183,7 +194,19 @@ module Sentry
183
194
  # Logger used by Sentry. In Rails, this is the Rails logger, otherwise
184
195
  # Sentry provides its own Sentry::Logger.
185
196
  # @return [Logger]
186
- attr_accessor :logger
197
+ attr_accessor :sdk_logger
198
+
199
+ # @deprecated Use {#sdk_logger=} instead.
200
+ def logger=(logger)
201
+ warn "[sentry] `config.logger=` is deprecated. Please use `config.sdk_logger=` instead."
202
+ self.sdk_logger = logger
203
+ end
204
+
205
+ # @deprecated Use {#sdk_logger} instead.
206
+ def logger
207
+ warn "[sentry] `config.logger` is deprecated. Please use `config.sdk_logger` instead."
208
+ self.sdk_logger
209
+ end
187
210
 
188
211
  # Project directory root for in_app detection. Could be Rails root, etc.
189
212
  # Set automatically for Rails.
@@ -262,6 +285,10 @@ module Sentry
262
285
  # @return [Proc]
263
286
  attr_accessor :traces_sampler
264
287
 
288
+ # Enable Structured Logging
289
+ # @return [Boolean]
290
+ attr_accessor :enable_logs
291
+
265
292
  # Easier way to use performance tracing
266
293
  # If set to true, will set traces_sample_rate to 1.0
267
294
  # @deprecated It will be removed in the next major release.
@@ -307,6 +334,10 @@ module Sentry
307
334
  # @return [Array<Symbol>]
308
335
  attr_accessor :enabled_patches
309
336
 
337
+ # Maximum number of log events to buffer before sending
338
+ # @return [Integer]
339
+ attr_accessor :max_log_events
340
+
310
341
  # these are not config options
311
342
  # @!visibility private
312
343
  attr_reader :errors, :gem_specs
@@ -419,7 +450,7 @@ module Sentry
419
450
  self.excluded_exceptions = IGNORE_DEFAULT + PUMA_IGNORE_DEFAULT
420
451
  self.inspect_exception_causes_for_exclusion = true
421
452
  self.linecache = ::Sentry::LineCache.new
422
- self.logger = ::Sentry::Logger.new(STDOUT)
453
+ self.sdk_logger = ::Sentry::Logger.new(STDOUT)
423
454
  self.project_root = Dir.pwd
424
455
  self.propagate_traces = true
425
456
 
@@ -443,9 +474,11 @@ module Sentry
443
474
 
444
475
  self.before_send = nil
445
476
  self.before_send_transaction = nil
477
+ self.before_send_log = nil
446
478
  self.rack_env_whitelist = RACK_ENV_WHITELIST_DEFAULT
447
479
  self.traces_sampler = nil
448
480
  self.enable_tracing = nil
481
+ self.enable_logs = false
449
482
 
450
483
  self.profiler_class = Sentry::Profiler
451
484
 
@@ -455,6 +488,8 @@ module Sentry
455
488
  @gem_specs = Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
456
489
 
457
490
  run_post_initialization_callbacks
491
+
492
+ self.max_log_events = LogEventBuffer::DEFAULT_MAX_EVENTS
458
493
  end
459
494
 
460
495
  def validate
@@ -15,7 +15,7 @@ module Sentry
15
15
  # rate limits and client reports use the data_category rather than envelope item type
16
16
  def self.data_category(type)
17
17
  case type
18
- when "session", "attachment", "transaction", "profile", "span" then type
18
+ when "session", "attachment", "transaction", "profile", "span", "log" then type
19
19
  when "sessions" then "session"
20
20
  when "check_in" then "monitor"
21
21
  when "statsd", "metric_meta" then "metric_bucket"
data/lib/sentry/event.rb CHANGED
@@ -7,6 +7,7 @@ require "sentry/backtrace"
7
7
  require "sentry/utils/real_ip"
8
8
  require "sentry/utils/request_id"
9
9
  require "sentry/utils/custom_inspection"
10
+ require "sentry/utils/uuid"
10
11
 
11
12
  module Sentry
12
13
  # This is an abstract class that defines the shared attributes of an event.
@@ -50,7 +51,7 @@ module Sentry
50
51
  # @param message [String, nil]
51
52
  def initialize(configuration:, integration_meta: nil, message: nil)
52
53
  # Set some simple default values
53
- @event_id = SecureRandom.uuid.delete("-")
54
+ @event_id = Utils.uuid
54
55
  @timestamp = Sentry.utc_now.iso8601
55
56
  @platform = :ruby
56
57
  @type = self.class::TYPE
data/lib/sentry/hub.rb CHANGED
@@ -216,6 +216,16 @@ module Sentry
216
216
  event.check_in_id
217
217
  end
218
218
 
219
+ def capture_log_event(message, **options)
220
+ return unless current_client
221
+
222
+ event = current_client.event_from_log(message, **options)
223
+
224
+ return unless event
225
+
226
+ current_client.buffer_log_event(event, current_scope)
227
+ end
228
+
219
229
  def capture_event(event, **options, &block)
220
230
  check_argument_type!(event, Sentry::Event)
221
231
 
@@ -99,7 +99,7 @@ module Sentry
99
99
  # Rails adds objects to the Rack env that can sometimes raise exceptions
100
100
  # when `to_s` is called.
101
101
  # See: https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/remote_ip.rb#L134
102
- Sentry.logger.warn(LOGGER_PROGNAME) { "Error raised while formatting headers: #{e.message}" }
102
+ Sentry.sdk_logger.warn(LOGGER_PROGNAME) { "Error raised while formatting headers: #{e.message}" }
103
103
  next
104
104
  end
105
105
  end
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ # Event type that represents a log entry with its attributes
5
+ #
6
+ # @see https://develop.sentry.dev/sdk/telemetry/logs/#log-envelope-item-payload
7
+ class LogEvent
8
+ TYPE = "log"
9
+
10
+ DEFAULT_PARAMETERS = [].freeze
11
+ DEFAULT_ATTRIBUTES = {}.freeze
12
+
13
+ SERIALIZEABLE_ATTRIBUTES = %i[
14
+ level
15
+ body
16
+ timestamp
17
+ environment
18
+ release
19
+ server_name
20
+ trace_id
21
+ attributes
22
+ contexts
23
+ ]
24
+
25
+ SENTRY_ATTRIBUTES = {
26
+ "sentry.trace.parent_span_id" => :parent_span_id,
27
+ "sentry.environment" => :environment,
28
+ "sentry.release" => :release,
29
+ "sentry.address" => :server_name,
30
+ "sentry.sdk.name" => :sdk_name,
31
+ "sentry.sdk.version" => :sdk_version,
32
+ "sentry.message.template" => :template
33
+ }
34
+
35
+ USER_ATTRIBUTES = {
36
+ "user.id" => :user_id,
37
+ "user.name" => :user_username,
38
+ "user.email" => :user_email
39
+ }
40
+
41
+ LEVELS = %i[trace debug info warn error fatal].freeze
42
+
43
+ attr_accessor :level, :body, :template, :attributes, :user
44
+
45
+ attr_reader :configuration, *SERIALIZEABLE_ATTRIBUTES
46
+
47
+ SERIALIZERS = %i[
48
+ attributes
49
+ body
50
+ level
51
+ parent_span_id
52
+ sdk_name
53
+ sdk_version
54
+ timestamp
55
+ trace_id
56
+ user_id
57
+ user_username
58
+ user_email
59
+ ].map { |name| [name, :"serialize_#{name}"] }.to_h
60
+
61
+ VALUE_TYPES = Hash.new("string").merge!({
62
+ TrueClass => "boolean",
63
+ FalseClass => "boolean",
64
+ Integer => "integer",
65
+ Float => "double"
66
+ }).freeze
67
+
68
+ TOKEN_REGEXP = /%\{(\w+)\}/
69
+
70
+ def initialize(configuration: Sentry.configuration, **options)
71
+ @configuration = configuration
72
+ @type = TYPE
73
+ @server_name = configuration.server_name
74
+ @environment = configuration.environment
75
+ @release = configuration.release
76
+ @timestamp = Sentry.utc_now
77
+ @level = options.fetch(:level)
78
+ @body = options[:body]
79
+ @template = @body if is_template?
80
+ @attributes = options[:attributes] || DEFAULT_ATTRIBUTES
81
+ @user = options[:user] || {}
82
+ @contexts = {}
83
+ end
84
+
85
+ def to_hash
86
+ SERIALIZEABLE_ATTRIBUTES.each_with_object({}) do |name, memo|
87
+ memo[name] = serialize(name)
88
+ end
89
+ end
90
+
91
+ private
92
+
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
+
127
+ def serialize_body
128
+ if parameters.empty?
129
+ body
130
+ elsif parameters.is_a?(Hash)
131
+ body % parameters
132
+ else
133
+ sprintf(body, *parameters)
134
+ end
135
+ end
136
+
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_attributes
150
+ hash = {}
151
+
152
+ attributes.each do |key, value|
153
+ hash[key] = attribute_hash(value)
154
+ end
155
+
156
+ SENTRY_ATTRIBUTES.each do |key, name|
157
+ if (value = serialize(name))
158
+ hash[key] = attribute_hash(value)
159
+ end
160
+ end
161
+
162
+ USER_ATTRIBUTES.each do |key, name|
163
+ if (value = serialize(name))
164
+ hash[key] = value
165
+ end
166
+ end
167
+
168
+ hash
169
+ end
170
+
171
+ def attribute_hash(value)
172
+ { value: value, type: value_type(value) }
173
+ end
174
+
175
+ def value_type(value)
176
+ VALUE_TYPES[value.class]
177
+ end
178
+
179
+ def parameters
180
+ @parameters ||= begin
181
+ return DEFAULT_PARAMETERS unless template
182
+
183
+ parameters = template_tokens.empty? ?
184
+ attributes.fetch(:parameters, DEFAULT_PARAMETERS) : attributes.slice(*template_tokens)
185
+
186
+ if parameters.is_a?(Hash)
187
+ parameters.each do |key, value|
188
+ attributes["sentry.message.parameter.#{key}"] = value
189
+ end
190
+ else
191
+ parameters.each_with_index do |param, index|
192
+ attributes["sentry.message.parameter.#{index}"] = param
193
+ end
194
+ end
195
+ end
196
+ end
197
+
198
+ def template_tokens
199
+ @template_tokens ||= body.scan(TOKEN_REGEXP).flatten.map(&:to_sym)
200
+ end
201
+
202
+ def is_template?
203
+ body.include?("%s") || TOKEN_REGEXP.match?(body)
204
+ end
205
+ end
206
+ end