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 +4 -4
- data/.rspec +3 -1
- data/README.md +11 -6
- data/Rakefile +5 -6
- data/lib/sentry/background_worker.rb +2 -3
- data/lib/sentry/backpressure_monitor.rb +1 -1
- data/lib/sentry/breadcrumb.rb +5 -4
- data/lib/sentry/check_in_event.rb +2 -1
- data/lib/sentry/client.rb +82 -4
- data/lib/sentry/configuration.rb +37 -2
- data/lib/sentry/envelope/item.rb +1 -1
- data/lib/sentry/event.rb +2 -1
- data/lib/sentry/hub.rb +10 -0
- data/lib/sentry/interfaces/request.rb +1 -1
- data/lib/sentry/log_event.rb +206 -0
- data/lib/sentry/log_event_buffer.rb +75 -0
- data/lib/sentry/metrics/aggregator.rb +1 -1
- data/lib/sentry/profiler.rb +3 -2
- data/lib/sentry/propagation_context.rb +4 -4
- data/lib/sentry/scope.rb +13 -3
- data/lib/sentry/session_flusher.rb +1 -1
- data/lib/sentry/span.rb +4 -3
- data/lib/sentry/structured_logger.rb +138 -0
- data/lib/sentry/test_helper.rb +7 -0
- data/lib/sentry/threaded_periodic_worker.rb +3 -3
- data/lib/sentry/transaction.rb +1 -7
- data/lib/sentry/transport.rb +16 -8
- data/lib/sentry/utils/logging_helper.rb +10 -3
- data/lib/sentry/utils/uuid.rb +13 -0
- data/lib/sentry/vernier/profiler.rb +3 -2
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +62 -4
- metadata +13 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d7397a94c353821b827cd429f7e75cf1663f4bc3aa40e4407eef9f2f726a632f
|
4
|
+
data.tar.gz: 00efb1f46016d1db1756ff6ccdd15ae0af27d48278c5d1cec4a302a3805e80c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: de1e9f756456d324ce92be08686ae0b0101d306f539bd2c0212490d5cc3816e80fca8ed7a76f697edf58950e5445e65a4101934de05251559b3042edc3bc2a75
|
7
|
+
data.tar.gz: 2c3b81765ea431073d60c41c289204d3bfae2ad7b6754147ef80414bd40c6ecd11d79a4efbf4af3fd148439840205f6f0ca6756a40c349553d370e545d4b0d62
|
data/.rspec
CHANGED
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
|
-
| [](https://rubygems.org/gems/sentry-ruby) | [](https://rubygems.org/gems/sentry-rails) | [](https://rubygems.org/gems/sentry-sidekiq) | [](https://rubygems.org/gems/sentry-delayed_job) | [](https://rubygems.org/gems/sentry-resque) | [](https://rubygems.org/gems/sentry-opentelemetry) | [](https://rubygems.org/gems/sentry-ruby) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/tests.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby) | [](https://www.rubydoc.info/gems/sentry-ruby) |
|
19
|
+
| [](https://rubygems.org/gems/sentry-rails) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/tests.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby) | [](https://www.rubydoc.info/gems/sentry-rails) |
|
20
|
+
| [](https://rubygems.org/gems/sentry-sidekiq) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/tests.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby) | [](https://www.rubydoc.info/gems/sentry-sidekiq) |
|
21
|
+
| [](https://rubygems.org/gems/sentry-delayed_job) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/tests.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby) | [](https://www.rubydoc.info/gems/sentry-delayed_job) |
|
22
|
+
| [](https://rubygems.org/gems/sentry-resque) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/tests.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby) | [](https://www.rubydoc.info/gems/sentry-resque) |
|
23
|
+
| [](https://rubygems.org/gems/sentry-opentelemetry) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/tests.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby) | [](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.
|
13
|
-
task.exclude_pattern = "spec/isolated/**/*_spec.rb"
|
14
|
+
task.exclude_pattern = ISOLATED_SPECS
|
14
15
|
end
|
15
16
|
|
16
|
-
|
17
|
-
|
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
|
-
|
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
|
-
@
|
21
|
+
@sdk_logger = configuration.sdk_logger
|
23
22
|
@debug = configuration.debug
|
24
23
|
@shutdown_callback = nil
|
25
24
|
|
data/lib/sentry/breadcrumb.rb
CHANGED
@@ -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.
|
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 ||
|
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
|
-
@
|
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]
|
data/lib/sentry/configuration.rb
CHANGED
@@ -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 :
|
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.
|
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
|
data/lib/sentry/envelope/item.rb
CHANGED
@@ -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 =
|
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.
|
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
|