sentry-ruby-core 5.22.4 → 5.24.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/Gemfile +4 -3
- data/lib/sentry/background_worker.rb +2 -3
- data/lib/sentry/backpressure_monitor.rb +1 -1
- data/lib/sentry/backtrace.rb +1 -1
- data/lib/sentry/breadcrumb.rb +5 -4
- data/lib/sentry/check_in_event.rb +2 -1
- data/lib/sentry/client.rb +39 -4
- data/lib/sentry/configuration.rb +34 -2
- data/lib/sentry/envelope/item.rb +1 -1
- data/lib/sentry/event.rb +2 -1
- data/lib/sentry/hub.rb +41 -1
- data/lib/sentry/interfaces/request.rb +1 -1
- data/lib/sentry/linecache.rb +3 -3
- data/lib/sentry/log_event.rb +159 -0
- data/lib/sentry/log_event_buffer.rb +97 -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 +1 -1
- data/lib/sentry/session_flusher.rb +1 -1
- data/lib/sentry/span.rb +3 -2
- data/lib/sentry/structured_logger.rb +138 -0
- data/lib/sentry/test_helper.rb +20 -0
- data/lib/sentry/threaded_periodic_worker.rb +3 -3
- data/lib/sentry/transaction.rb +9 -9
- data/lib/sentry/transaction_event.rb +4 -1
- data/lib/sentry/transport/http_transport.rb +0 -1
- data/lib/sentry/transport.rb +16 -8
- data/lib/sentry/utils/http_tracing.rb +15 -1
- data/lib/sentry/utils/logging_helper.rb +10 -3
- data/lib/sentry/utils/uuid.rb +13 -0
- data/lib/sentry/vernier/profiler.rb +6 -4
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +70 -4
- metadata +10 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ea57af962e29f8ae1d85f7a1cdc2106f1865e8afcdce4a2fd24b653c67ac10f5
|
4
|
+
data.tar.gz: c086418c80e60b60d1e099701fdb6b9ae1a9136bd464213dd9ff9cc17c582b75
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b9d195dc52ed15d1da607ecdc532f0bf87036a3e3670753d4694919b402d38dd9200319406714d91575186287cd40abfa857408a432867cf734f6c32b6c3749
|
7
|
+
data.tar.gz: 86ad04ab139eb703950d206a1a2a029f42da4fdf43896a342fa961ec8f7cd607de27270b49ec4ba7a635e2d34018387990e1a56d26287af39bf2708aee004a23
|
data/Gemfile
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
git_source(:github) { |name| "https://github.com/#{name}.git" }
|
5
5
|
|
6
|
+
eval_gemfile "../Gemfile"
|
7
|
+
|
6
8
|
gem "sentry-ruby", path: "./"
|
7
9
|
|
8
10
|
rack_version = ENV["RACK_VERSION"]
|
@@ -27,9 +29,8 @@ gem "benchmark_driver"
|
|
27
29
|
gem "benchmark-ipsa"
|
28
30
|
gem "benchmark-memory"
|
29
31
|
|
30
|
-
gem "yard"
|
32
|
+
gem "yard"
|
31
33
|
gem "webrick"
|
32
34
|
gem "faraday"
|
33
35
|
gem "excon"
|
34
|
-
|
35
|
-
eval_gemfile File.expand_path("../Gemfile", __dir__)
|
36
|
+
gem "webmock"
|
@@ -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/backtrace.rb
CHANGED
@@ -16,7 +16,7 @@ module Sentry
|
|
16
16
|
/x
|
17
17
|
|
18
18
|
# org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:170)
|
19
|
-
JAVA_INPUT_FORMAT = /^(
|
19
|
+
JAVA_INPUT_FORMAT = /^([\w$.]+)\.([\w$]+)\(([\w$.]+):(\d+)\)$/
|
20
20
|
|
21
21
|
# The file portion of the line (such as app/models/user.rb)
|
22
22
|
attr_reader :file
|
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
|
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,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "sentry/transport"
|
4
|
+
require "sentry/log_event"
|
5
|
+
require "sentry/log_event_buffer"
|
4
6
|
|
5
7
|
module Sentry
|
6
8
|
class Client
|
@@ -14,16 +16,16 @@ module Sentry
|
|
14
16
|
# @return [SpotlightTransport, nil]
|
15
17
|
attr_reader :spotlight_transport
|
16
18
|
|
19
|
+
# @!visibility private
|
20
|
+
attr_reader :log_event_buffer
|
21
|
+
|
17
22
|
# @!macro configuration
|
18
23
|
attr_reader :configuration
|
19
24
|
|
20
|
-
# @deprecated Use Sentry.logger to retrieve the current logger instead.
|
21
|
-
attr_reader :logger
|
22
|
-
|
23
25
|
# @param configuration [Configuration]
|
24
26
|
def initialize(configuration)
|
25
27
|
@configuration = configuration
|
26
|
-
@
|
28
|
+
@sdk_logger = configuration.sdk_logger
|
27
29
|
|
28
30
|
if transport_class = configuration.transport.transport_class
|
29
31
|
@transport = transport_class.new(configuration)
|
@@ -38,6 +40,8 @@ module Sentry
|
|
38
40
|
end
|
39
41
|
|
40
42
|
@spotlight_transport = SpotlightTransport.new(configuration) if configuration.spotlight
|
43
|
+
|
44
|
+
@log_event_buffer = LogEventBuffer.new(configuration, self).start
|
41
45
|
end
|
42
46
|
|
43
47
|
# Applies the given scope's data to the event and sends it to Sentry.
|
@@ -88,6 +92,15 @@ module Sentry
|
|
88
92
|
nil
|
89
93
|
end
|
90
94
|
|
95
|
+
# Buffer a log event to be sent later with other logs in a single envelope
|
96
|
+
# @param event [LogEvent] the log event to be buffered
|
97
|
+
# @return [LogEvent]
|
98
|
+
def buffer_log_event(event, scope)
|
99
|
+
return unless event.is_a?(LogEvent)
|
100
|
+
@log_event_buffer.add_event(scope.apply_to_event(event))
|
101
|
+
event
|
102
|
+
end
|
103
|
+
|
91
104
|
# Capture an envelope directly.
|
92
105
|
# @param envelope [Envelope] the envelope to be captured.
|
93
106
|
# @return [void]
|
@@ -100,6 +113,7 @@ module Sentry
|
|
100
113
|
def flush
|
101
114
|
transport.flush if configuration.sending_to_dsn_allowed?
|
102
115
|
spotlight_transport.flush if spotlight_transport
|
116
|
+
@log_event_buffer.flush
|
103
117
|
end
|
104
118
|
|
105
119
|
# Initializes an Event object with the given exception. Returns `nil` if the exception's class is excluded from reporting.
|
@@ -167,6 +181,27 @@ module Sentry
|
|
167
181
|
)
|
168
182
|
end
|
169
183
|
|
184
|
+
# Initializes a LogEvent object with the given message and options
|
185
|
+
#
|
186
|
+
# @param message [String] the log message
|
187
|
+
# @param level [Symbol] the log level (:trace, :debug, :info, :warn, :error, :fatal)
|
188
|
+
# @param options [Hash] additional options
|
189
|
+
# @option options [Array] :parameters Array of values to replace template tokens in the message
|
190
|
+
#
|
191
|
+
# @return [LogEvent] the created log event
|
192
|
+
def event_from_log(message, level:, **options)
|
193
|
+
return unless configuration.sending_allowed?
|
194
|
+
|
195
|
+
attributes = options.reject { |k, _| k == :level || k == :severity }
|
196
|
+
|
197
|
+
LogEvent.new(
|
198
|
+
level: level,
|
199
|
+
body: message,
|
200
|
+
timestamp: Time.now.to_f,
|
201
|
+
attributes: attributes
|
202
|
+
)
|
203
|
+
end
|
204
|
+
|
170
205
|
# Initializes an Event object with the given Transaction object.
|
171
206
|
# @param transaction [Transaction] the transaction to be recorded.
|
172
207
|
# @return [TransactionEvent]
|
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
|
@@ -183,7 +185,19 @@ module Sentry
|
|
183
185
|
# Logger used by Sentry. In Rails, this is the Rails logger, otherwise
|
184
186
|
# Sentry provides its own Sentry::Logger.
|
185
187
|
# @return [Logger]
|
186
|
-
attr_accessor :
|
188
|
+
attr_accessor :sdk_logger
|
189
|
+
|
190
|
+
# @deprecated Use {#sdk_logger=} instead.
|
191
|
+
def logger=(logger)
|
192
|
+
warn "[sentry] `config.logger=` is deprecated. Please use `config.sdk_logger=` instead."
|
193
|
+
self.sdk_logger = logger
|
194
|
+
end
|
195
|
+
|
196
|
+
# @deprecated Use {#sdk_logger} instead.
|
197
|
+
def logger
|
198
|
+
warn "[sentry] `config.logger` is deprecated. Please use `config.sdk_logger` instead."
|
199
|
+
self.sdk_logger
|
200
|
+
end
|
187
201
|
|
188
202
|
# Project directory root for in_app detection. Could be Rails root, etc.
|
189
203
|
# Set automatically for Rails.
|
@@ -262,8 +276,13 @@ module Sentry
|
|
262
276
|
# @return [Proc]
|
263
277
|
attr_accessor :traces_sampler
|
264
278
|
|
279
|
+
# Enable Structured Logging
|
280
|
+
# @return [Boolean]
|
281
|
+
attr_accessor :enable_logs
|
282
|
+
|
265
283
|
# Easier way to use performance tracing
|
266
284
|
# If set to true, will set traces_sample_rate to 1.0
|
285
|
+
# @deprecated It will be removed in the next major release.
|
267
286
|
# @return [Boolean, nil]
|
268
287
|
attr_reader :enable_tracing
|
269
288
|
|
@@ -306,6 +325,10 @@ module Sentry
|
|
306
325
|
# @return [Array<Symbol>]
|
307
326
|
attr_accessor :enabled_patches
|
308
327
|
|
328
|
+
# Maximum number of log events to buffer before sending
|
329
|
+
# @return [Integer]
|
330
|
+
attr_accessor :max_log_events
|
331
|
+
|
309
332
|
# these are not config options
|
310
333
|
# @!visibility private
|
311
334
|
attr_reader :errors, :gem_specs
|
@@ -418,7 +441,7 @@ module Sentry
|
|
418
441
|
self.excluded_exceptions = IGNORE_DEFAULT + PUMA_IGNORE_DEFAULT
|
419
442
|
self.inspect_exception_causes_for_exclusion = true
|
420
443
|
self.linecache = ::Sentry::LineCache.new
|
421
|
-
self.
|
444
|
+
self.sdk_logger = ::Sentry::Logger.new(STDOUT)
|
422
445
|
self.project_root = Dir.pwd
|
423
446
|
self.propagate_traces = true
|
424
447
|
|
@@ -445,6 +468,7 @@ module Sentry
|
|
445
468
|
self.rack_env_whitelist = RACK_ENV_WHITELIST_DEFAULT
|
446
469
|
self.traces_sampler = nil
|
447
470
|
self.enable_tracing = nil
|
471
|
+
self.enable_logs = false
|
448
472
|
|
449
473
|
self.profiler_class = Sentry::Profiler
|
450
474
|
|
@@ -454,6 +478,8 @@ module Sentry
|
|
454
478
|
@gem_specs = Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
|
455
479
|
|
456
480
|
run_post_initialization_callbacks
|
481
|
+
|
482
|
+
self.max_log_events = LogEventBuffer::DEFAULT_MAX_EVENTS
|
457
483
|
end
|
458
484
|
|
459
485
|
def validate
|
@@ -542,6 +568,12 @@ module Sentry
|
|
542
568
|
end
|
543
569
|
|
544
570
|
def enable_tracing=(enable_tracing)
|
571
|
+
unless enable_tracing.nil?
|
572
|
+
log_warn <<~MSG
|
573
|
+
`enable_tracing` is now deprecated in favor of `traces_sample_rate = 1.0`.
|
574
|
+
MSG
|
575
|
+
end
|
576
|
+
|
545
577
|
@enable_tracing = enable_tracing
|
546
578
|
@traces_sample_rate ||= 1.0 if enable_tracing
|
547
579
|
end
|
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
@@ -8,12 +8,42 @@ module Sentry
|
|
8
8
|
class Hub
|
9
9
|
include ArgumentCheckingHelper
|
10
10
|
|
11
|
+
MUTEX = Mutex.new
|
12
|
+
|
11
13
|
attr_reader :last_event_id
|
12
14
|
|
15
|
+
attr_reader :current_profiler
|
16
|
+
|
13
17
|
def initialize(client, scope)
|
14
18
|
first_layer = Layer.new(client, scope)
|
15
19
|
@stack = [first_layer]
|
16
20
|
@last_event_id = nil
|
21
|
+
@current_profiler = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
# This is an internal private method
|
25
|
+
# @api private
|
26
|
+
def start_profiler!(transaction)
|
27
|
+
MUTEX.synchronize do
|
28
|
+
transaction.start_profiler!
|
29
|
+
@current_profiler[transaction.__id__] = transaction.profiler
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# This is an internal private method
|
34
|
+
# @api private
|
35
|
+
def stop_profiler!(transaction)
|
36
|
+
MUTEX.synchronize do
|
37
|
+
@current_profiler.delete(transaction.__id__)&.stop
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# This is an internal private method
|
42
|
+
# @api private
|
43
|
+
def profiler_running?
|
44
|
+
MUTEX.synchronize do
|
45
|
+
!@current_profiler.empty?
|
46
|
+
end
|
17
47
|
end
|
18
48
|
|
19
49
|
def new_from_top
|
@@ -96,7 +126,7 @@ module Sentry
|
|
96
126
|
sampling_context.merge!(custom_sampling_context)
|
97
127
|
transaction.set_initial_sample_decision(sampling_context: sampling_context)
|
98
128
|
|
99
|
-
|
129
|
+
start_profiler!(transaction)
|
100
130
|
|
101
131
|
transaction
|
102
132
|
end
|
@@ -186,6 +216,16 @@ module Sentry
|
|
186
216
|
event.check_in_id
|
187
217
|
end
|
188
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
|
+
|
189
229
|
def capture_event(event, **options, &block)
|
190
230
|
check_argument_type!(event, Sentry::Event)
|
191
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
|
data/lib/sentry/linecache.rb
CHANGED
@@ -0,0 +1,159 @@
|
|
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 < Event
|
8
|
+
TYPE = "log"
|
9
|
+
|
10
|
+
DEFAULT_PARAMETERS = [].freeze
|
11
|
+
DEFAULT_ATTRIBUTES = {}.freeze
|
12
|
+
DEFAULT_CONTEXT = {}.freeze
|
13
|
+
|
14
|
+
SERIALIZEABLE_ATTRIBUTES = %i[
|
15
|
+
level
|
16
|
+
body
|
17
|
+
timestamp
|
18
|
+
trace_id
|
19
|
+
attributes
|
20
|
+
]
|
21
|
+
|
22
|
+
SENTRY_ATTRIBUTES = {
|
23
|
+
"sentry.trace.parent_span_id" => :parent_span_id,
|
24
|
+
"sentry.environment" => :environment,
|
25
|
+
"sentry.release" => :release,
|
26
|
+
"sentry.address" => :server_name,
|
27
|
+
"sentry.sdk.name" => :sdk_name,
|
28
|
+
"sentry.sdk.version" => :sdk_version,
|
29
|
+
"sentry.message.template" => :template
|
30
|
+
}
|
31
|
+
|
32
|
+
LEVELS = %i[trace debug info warn error fatal].freeze
|
33
|
+
|
34
|
+
attr_accessor :level, :body, :template, :attributes
|
35
|
+
|
36
|
+
def initialize(configuration: Sentry.configuration, **options)
|
37
|
+
super(configuration: configuration)
|
38
|
+
|
39
|
+
@type = TYPE
|
40
|
+
@level = options.fetch(:level)
|
41
|
+
@body = options[:body]
|
42
|
+
@template = @body if is_template?
|
43
|
+
@attributes = options[:attributes] || DEFAULT_ATTRIBUTES
|
44
|
+
@contexts = DEFAULT_CONTEXT
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_hash
|
48
|
+
SERIALIZEABLE_ATTRIBUTES.each_with_object({}) do |name, memo|
|
49
|
+
memo[name] = serialize(name)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def serialize(name)
|
56
|
+
serializer = :"serialize_#{name}"
|
57
|
+
|
58
|
+
if respond_to?(serializer, true)
|
59
|
+
__send__(serializer)
|
60
|
+
else
|
61
|
+
public_send(name)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def serialize_level
|
66
|
+
level.to_s
|
67
|
+
end
|
68
|
+
|
69
|
+
def serialize_sdk_name
|
70
|
+
Sentry.sdk_meta["name"]
|
71
|
+
end
|
72
|
+
|
73
|
+
def serialize_sdk_version
|
74
|
+
Sentry.sdk_meta["version"]
|
75
|
+
end
|
76
|
+
|
77
|
+
def serialize_timestamp
|
78
|
+
Time.parse(timestamp).to_f
|
79
|
+
end
|
80
|
+
|
81
|
+
def serialize_trace_id
|
82
|
+
contexts.dig(:trace, :trace_id)
|
83
|
+
end
|
84
|
+
|
85
|
+
def serialize_parent_span_id
|
86
|
+
contexts.dig(:trace, :parent_span_id)
|
87
|
+
end
|
88
|
+
|
89
|
+
def serialize_body
|
90
|
+
if parameters.empty?
|
91
|
+
body
|
92
|
+
elsif parameters.is_a?(Hash)
|
93
|
+
body % parameters
|
94
|
+
else
|
95
|
+
sprintf(body, *parameters)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def serialize_attributes
|
100
|
+
hash = attributes.each_with_object({}) do |(key, value), memo|
|
101
|
+
memo[key] = attribute_hash(value)
|
102
|
+
end
|
103
|
+
|
104
|
+
SENTRY_ATTRIBUTES.each do |key, name|
|
105
|
+
if (value = serialize(name))
|
106
|
+
hash[key] = attribute_hash(value)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
hash
|
111
|
+
end
|
112
|
+
|
113
|
+
def attribute_hash(value)
|
114
|
+
{ value: value, type: value_type(value) }
|
115
|
+
end
|
116
|
+
|
117
|
+
def value_type(value)
|
118
|
+
case value
|
119
|
+
when Integer
|
120
|
+
"integer"
|
121
|
+
when TrueClass, FalseClass
|
122
|
+
"boolean"
|
123
|
+
when Float
|
124
|
+
"double"
|
125
|
+
else
|
126
|
+
"string"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def parameters
|
131
|
+
@parameters ||= begin
|
132
|
+
return DEFAULT_PARAMETERS unless template
|
133
|
+
|
134
|
+
parameters = template_tokens.empty? ?
|
135
|
+
attributes.fetch(:parameters, DEFAULT_PARAMETERS) : attributes.slice(*template_tokens)
|
136
|
+
|
137
|
+
if parameters.is_a?(Hash)
|
138
|
+
parameters.each do |key, value|
|
139
|
+
attributes["sentry.message.parameter.#{key}"] = value
|
140
|
+
end
|
141
|
+
else
|
142
|
+
parameters.each_with_index do |param, index|
|
143
|
+
attributes["sentry.message.parameter.#{index}"] = param
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
TOKEN_REGEXP = /%\{(\w+)\}/
|
150
|
+
|
151
|
+
def template_tokens
|
152
|
+
@template_tokens ||= body.scan(TOKEN_REGEXP).flatten.map(&:to_sym)
|
153
|
+
end
|
154
|
+
|
155
|
+
def is_template?
|
156
|
+
body.include?("%s") || TOKEN_REGEXP.match?(body)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "sentry/threaded_periodic_worker"
|
4
|
+
|
5
|
+
module Sentry
|
6
|
+
# LogEventBuffer buffers log events and sends them to Sentry in a single envelope.
|
7
|
+
#
|
8
|
+
# This is used internally by the `Sentry::Client`.
|
9
|
+
#
|
10
|
+
# @!visibility private
|
11
|
+
class LogEventBuffer < ThreadedPeriodicWorker
|
12
|
+
FLUSH_INTERVAL = 5 # seconds
|
13
|
+
DEFAULT_MAX_EVENTS = 100
|
14
|
+
|
15
|
+
# @!visibility private
|
16
|
+
attr_reader :pending_events
|
17
|
+
|
18
|
+
def initialize(configuration, client)
|
19
|
+
super(configuration.sdk_logger, FLUSH_INTERVAL)
|
20
|
+
|
21
|
+
@client = client
|
22
|
+
@pending_events = []
|
23
|
+
@max_events = configuration.max_log_events || DEFAULT_MAX_EVENTS
|
24
|
+
@dsn = configuration.dsn
|
25
|
+
@sdk = Sentry.sdk_meta
|
26
|
+
@mutex = Mutex.new
|
27
|
+
|
28
|
+
log_debug("[Logging] Initialized buffer with max_events=#{@max_events}, flush_interval=#{FLUSH_INTERVAL}s")
|
29
|
+
end
|
30
|
+
|
31
|
+
def start
|
32
|
+
ensure_thread
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def flush
|
37
|
+
@mutex.synchronize do
|
38
|
+
return if empty?
|
39
|
+
|
40
|
+
log_debug("[LogEventBuffer] flushing #{size} log events")
|
41
|
+
|
42
|
+
send_events
|
43
|
+
end
|
44
|
+
|
45
|
+
log_debug("[LogEventBuffer] flushed #{size} log events")
|
46
|
+
|
47
|
+
self
|
48
|
+
end
|
49
|
+
alias_method :run, :flush
|
50
|
+
|
51
|
+
def add_event(event)
|
52
|
+
raise ArgumentError, "expected a LogEvent, got #{event.class}" unless event.is_a?(LogEvent)
|
53
|
+
|
54
|
+
@mutex.synchronize do
|
55
|
+
@pending_events << event
|
56
|
+
send_events if size >= @max_events
|
57
|
+
end
|
58
|
+
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
def empty?
|
63
|
+
@pending_events.empty?
|
64
|
+
end
|
65
|
+
|
66
|
+
def size
|
67
|
+
@pending_events.size
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def send_events
|
73
|
+
@client.send_envelope(to_envelope)
|
74
|
+
@pending_events.clear
|
75
|
+
end
|
76
|
+
|
77
|
+
def to_envelope
|
78
|
+
envelope = Envelope.new(
|
79
|
+
event_id: SecureRandom.uuid.delete("-"),
|
80
|
+
sent_at: Sentry.utc_now.iso8601,
|
81
|
+
dsn: @dsn,
|
82
|
+
sdk: @sdk
|
83
|
+
)
|
84
|
+
|
85
|
+
envelope.add_item(
|
86
|
+
{
|
87
|
+
type: "log",
|
88
|
+
item_count: size,
|
89
|
+
content_type: "application/vnd.sentry.items.log+json"
|
90
|
+
},
|
91
|
+
{ items: @pending_events.map(&:to_hash) }
|
92
|
+
)
|
93
|
+
|
94
|
+
envelope
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -34,7 +34,7 @@ module Sentry
|
|
34
34
|
attr_reader :client, :thread, :buckets, :flush_shift, :code_locations
|
35
35
|
|
36
36
|
def initialize(configuration, client)
|
37
|
-
super(configuration.
|
37
|
+
super(configuration.sdk_logger, FLUSH_INTERVAL)
|
38
38
|
@client = client
|
39
39
|
@before_emit = configuration.metrics.before_emit
|
40
40
|
@enable_code_locations = configuration.metrics.enable_code_locations
|