sentry-ruby 5.18.2 → 5.28.1

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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +3 -1
  3. data/Gemfile +9 -3
  4. data/README.md +12 -7
  5. data/Rakefile +9 -11
  6. data/bin/console +1 -0
  7. data/lib/sentry/attachment.rb +40 -0
  8. data/lib/sentry/background_worker.rb +2 -3
  9. data/lib/sentry/backpressure_monitor.rb +1 -1
  10. data/lib/sentry/backtrace.rb +7 -8
  11. data/lib/sentry/baggage.rb +7 -7
  12. data/lib/sentry/breadcrumb/sentry_logger.rb +6 -6
  13. data/lib/sentry/breadcrumb.rb +5 -4
  14. data/lib/sentry/check_in_event.rb +5 -4
  15. data/lib/sentry/client.rb +121 -19
  16. data/lib/sentry/configuration.rb +207 -38
  17. data/lib/sentry/core_ext/object/deep_dup.rb +1 -1
  18. data/lib/sentry/cron/monitor_check_ins.rb +6 -4
  19. data/lib/sentry/cron/monitor_config.rb +1 -1
  20. data/lib/sentry/debug_structured_logger.rb +94 -0
  21. data/lib/sentry/dsn.rb +35 -3
  22. data/lib/sentry/envelope/item.rb +88 -0
  23. data/lib/sentry/envelope.rb +2 -85
  24. data/lib/sentry/event.rb +13 -8
  25. data/lib/sentry/excon/middleware.rb +77 -0
  26. data/lib/sentry/excon.rb +10 -0
  27. data/lib/sentry/faraday.rb +77 -0
  28. data/lib/sentry/graphql.rb +1 -1
  29. data/lib/sentry/hub.rb +57 -4
  30. data/lib/sentry/interfaces/mechanism.rb +1 -1
  31. data/lib/sentry/interfaces/request.rb +6 -6
  32. data/lib/sentry/interfaces/single_exception.rb +3 -3
  33. data/lib/sentry/interfaces/stacktrace.rb +3 -1
  34. data/lib/sentry/interfaces/stacktrace_builder.rb +15 -2
  35. data/lib/sentry/linecache.rb +3 -3
  36. data/lib/sentry/log_event.rb +219 -0
  37. data/lib/sentry/log_event_buffer.rb +75 -0
  38. data/lib/sentry/logger.rb +1 -1
  39. data/lib/sentry/metrics/aggregator.rb +13 -13
  40. data/lib/sentry/metrics/configuration.rb +12 -2
  41. data/lib/sentry/metrics/set_metric.rb +2 -2
  42. data/lib/sentry/metrics/timing.rb +8 -0
  43. data/lib/sentry/metrics.rb +27 -15
  44. data/lib/sentry/net/http.rb +17 -38
  45. data/lib/sentry/profiler/helpers.rb +46 -0
  46. data/lib/sentry/profiler.rb +27 -57
  47. data/lib/sentry/propagation_context.rb +60 -23
  48. data/lib/sentry/rack/capture_exceptions.rb +2 -2
  49. data/lib/sentry/rack.rb +2 -2
  50. data/lib/sentry/rake.rb +2 -2
  51. data/lib/sentry/release_detector.rb +4 -4
  52. data/lib/sentry/rspec.rb +91 -0
  53. data/lib/sentry/scope.rb +27 -2
  54. data/lib/sentry/session_flusher.rb +10 -6
  55. data/lib/sentry/span.rb +17 -4
  56. data/lib/sentry/std_lib_logger.rb +55 -0
  57. data/lib/sentry/structured_logger.rb +138 -0
  58. data/lib/sentry/test_helper.rb +45 -1
  59. data/lib/sentry/threaded_periodic_worker.rb +3 -3
  60. data/lib/sentry/transaction.rb +41 -13
  61. data/lib/sentry/transaction_event.rb +5 -3
  62. data/lib/sentry/transport/debug_transport.rb +70 -0
  63. data/lib/sentry/transport/dummy_transport.rb +1 -0
  64. data/lib/sentry/transport/http_transport.rb +21 -18
  65. data/lib/sentry/transport.rb +26 -11
  66. data/lib/sentry/utils/env_helper.rb +21 -0
  67. data/lib/sentry/utils/http_tracing.rb +74 -0
  68. data/lib/sentry/utils/logging_helper.rb +10 -3
  69. data/lib/sentry/utils/real_ip.rb +1 -1
  70. data/lib/sentry/utils/sample_rand.rb +97 -0
  71. data/lib/sentry/utils/uuid.rb +13 -0
  72. data/lib/sentry/vernier/output.rb +89 -0
  73. data/lib/sentry/vernier/profiler.rb +132 -0
  74. data/lib/sentry/version.rb +1 -1
  75. data/lib/sentry-ruby.rb +106 -12
  76. data/sentry-ruby-core.gemspec +3 -1
  77. data/sentry-ruby.gemspec +4 -2
  78. metadata +28 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 866b4388eea19f9cc866f10e34f25abfb311a5877015df29f5dded5bfa182e7a
4
- data.tar.gz: 2ba353d4fdbe545c8e76fbd2c3b0169f481965e8e55cc74ad3e4cd613b6c4f76
3
+ metadata.gz: 496e873f83c42877eabed1e6aac9db6e6f043ce9d3d2f5725d6905ddfce917c0
4
+ data.tar.gz: 9e66db9076448282ced30a490dbf07dddd45ce30fa19b514586804f719d3fae6
5
5
  SHA512:
6
- metadata.gz: c70d18ba79eb9c7a359d2d25cbd97bc8fa6472d89a422e57ebce7daee6f4b5cab0dd14bb35281bf22cbdfb1d4198800ebdf5a96baf044f202afae3c1a9ee0160
7
- data.tar.gz: aebeeb641244f112b0957f409d0055379f168413354e25f19118744e79352b12159b693945c1bfc506e8d14ad076b115c9e7037be6d7bbe08114b3f6b7370df5
6
+ metadata.gz: 40d1b342d7d6fd63c03f427e9ea3b6391c52252d40d9d22bea5d17d9b175ae9a6e559810eeecc806634355561c51f2f6237b3dc17a0eb43754bb940ce111a36a
7
+ data.tar.gz: 6c4c5dffc5d389dfb365a2678b2545c6681aebe7bdb6aac702cb2e6fc7b540030575696cdb55794501e0effbdeb5b15f7ac88516e49f34bbfba91601187e995b
data/.rspec CHANGED
@@ -1,2 +1,4 @@
1
- --format documentation
1
+ --require spec_helper
2
+ --format progress
2
3
  --color
4
+ --order rand
data/Gemfile CHANGED
@@ -1,6 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
  git_source(:github) { |name| "https://github.com/#{name}.git" }
3
5
 
6
+ eval_gemfile "../Gemfile.dev"
7
+
4
8
  gem "sentry-ruby", path: "./"
5
9
 
6
10
  rack_version = ENV["RACK_VERSION"]
@@ -14,6 +18,7 @@ gem "puma"
14
18
 
15
19
  gem "timecop"
16
20
  gem "stackprof" unless RUBY_PLATFORM == "java"
21
+ gem "vernier", platforms: :ruby if RUBY_VERSION >= "3.2.1"
17
22
 
18
23
  gem "graphql", ">= 2.2.6" if RUBY_VERSION.to_f >= 2.7
19
24
 
@@ -22,7 +27,8 @@ gem "benchmark_driver"
22
27
  gem "benchmark-ipsa"
23
28
  gem "benchmark-memory"
24
29
 
25
- gem "yard", github: "lsegal/yard"
30
+ gem "yard"
26
31
  gem "webrick"
27
-
28
- eval_gemfile File.expand_path("../Gemfile", __dir__)
32
+ gem "faraday"
33
+ gem "excon"
34
+ gem "webmock"
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
 
@@ -33,7 +33,7 @@ If you're using `sentry-raven`, we recommend you to migrate to this new SDK. You
33
33
 
34
34
  ## Requirements
35
35
 
36
- We test from Ruby 2.4 to Ruby 3.2 at the latest patchlevel/teeny version. We also support JRuby 9.0.
36
+ We test from Ruby 2.4 to Ruby 3.4 at the latest patchlevel/teeny version. We also support JRuby 9.0.
37
37
 
38
38
  If you use self-hosted Sentry, please also make sure its version is above `20.6.0`.
39
39
 
@@ -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
@@ -1,20 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rake/clean"
2
4
  CLOBBER.include "pkg"
3
5
 
4
6
  require "bundler/gem_helper"
5
7
  Bundler::GemHelper.install_tasks(name: "sentry-ruby")
6
8
 
7
- require "rspec/core/rake_task"
9
+ require_relative "../lib/sentry/test/rake_tasks"
8
10
 
9
- RSpec::Core::RakeTask.new(:spec).tap do |task|
10
- task.rspec_opts = "--order rand"
11
- task.exclude_pattern = "spec/isolated/**/*_spec.rb"
12
- end
11
+ ISOLATED_SPECS = "spec/isolated/**/*_spec.rb"
13
12
 
14
- task :isolated_specs do
15
- Dir["spec/isolated/*"].each do |file|
16
- sh "bundle exec rspec #{file}"
17
- end
18
- end
13
+ Sentry::Test::RakeTasks.define_spec_tasks(
14
+ isolated_specs_pattern: ISOLATED_SPECS,
15
+ spec_exclude_pattern: ISOLATED_SPECS
16
+ )
19
17
 
20
- task default: [:spec, :isolated_specs]
18
+ task default: [:spec, :"spec:isolated"]
data/bin/console CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require "bundler/setup"
4
5
  require "debug"
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ class Attachment
5
+ PathNotFoundError = Class.new(StandardError)
6
+
7
+ attr_reader :bytes, :filename, :path, :content_type
8
+
9
+ def initialize(bytes: nil, filename: nil, content_type: nil, path: nil)
10
+ @bytes = bytes
11
+ @filename = filename || infer_filename(path)
12
+ @path = path
13
+ @content_type = content_type
14
+ end
15
+
16
+ def to_envelope_headers
17
+ { type: "attachment", filename: filename, content_type: content_type, length: payload.bytesize }
18
+ end
19
+
20
+ def payload
21
+ @payload ||= if bytes
22
+ bytes
23
+ else
24
+ File.binread(path)
25
+ end
26
+ rescue Errno::ENOENT
27
+ raise PathNotFoundError, "Failed to read attachment file, file not found: #{path}"
28
+ end
29
+
30
+ private
31
+
32
+ def infer_filename(path)
33
+ if path
34
+ File.basename(path)
35
+ else
36
+ raise ArgumentError, "filename or path is required"
37
+ end
38
+ end
39
+ end
40
+ end
@@ -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
@@ -12,11 +12,11 @@ module Sentry
12
12
  RUBY_INPUT_FORMAT = /
13
13
  ^ \s* (?: [a-zA-Z]: | uri:classloader: )? ([^:]+ | <.*>):
14
14
  (\d+)
15
- (?: :in\s('|`)([^']+)')?$
16
- /x.freeze
15
+ (?: :in\s('|`)(?:([\w:]+)\#)?([^']+)')?$
16
+ /x
17
17
 
18
18
  # org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:170)
19
- JAVA_INPUT_FORMAT = /^(.+)\.([^\.]+)\(([^\:]+)\:(\d+)\)$/.freeze
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
@@ -37,10 +37,11 @@ module Sentry
37
37
  # @return [Line] The parsed backtrace line
38
38
  def self.parse(unparsed_line, in_app_pattern = nil)
39
39
  ruby_match = unparsed_line.match(RUBY_INPUT_FORMAT)
40
+
40
41
  if ruby_match
41
- _, file, number, _, method = ruby_match.to_a
42
+ _, file, number, _, module_name, method = ruby_match.to_a
42
43
  file.sub!(/\.class$/, RB_EXTENSION)
43
- module_name = nil
44
+ module_name = module_name
44
45
  else
45
46
  java_match = unparsed_line.match(JAVA_INPUT_FORMAT)
46
47
  _, module_name, method, file, number = java_match.to_a
@@ -80,8 +81,6 @@ module Sentry
80
81
  end
81
82
  end
82
83
 
83
- APP_DIRS_PATTERN = /(bin|exe|app|config|lib|test|spec)/.freeze
84
-
85
84
  # holder for an Array of Backtrace::Line instances
86
85
  attr_reader :lines
87
86
 
@@ -91,7 +90,7 @@ module Sentry
91
90
  ruby_lines = backtrace_cleanup_callback.call(ruby_lines) if backtrace_cleanup_callback
92
91
 
93
92
  in_app_pattern ||= begin
94
- Regexp.new("^(#{project_root}/)?#{app_dirs_pattern || APP_DIRS_PATTERN}")
93
+ Regexp.new("^(#{project_root}/)?#{app_dirs_pattern}")
95
94
  end
96
95
 
97
96
  lines = ruby_lines.to_a.map do |unparsed_line|
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'cgi'
3
+ require "cgi"
4
4
 
5
5
  module Sentry
6
6
  # A {https://www.w3.org/TR/baggage W3C Baggage Header} implementation.
7
7
  class Baggage
8
- SENTRY_PREFIX = 'sentry-'
9
- SENTRY_PREFIX_REGEX = /^sentry-/.freeze
8
+ SENTRY_PREFIX = "sentry-"
9
+ SENTRY_PREFIX_REGEX = /^sentry-/
10
10
 
11
11
  # @return [Hash]
12
12
  attr_reader :items
@@ -30,14 +30,14 @@ module Sentry
30
30
  items = {}
31
31
  mutable = true
32
32
 
33
- header.split(',').each do |item|
33
+ header.split(",").each do |item|
34
34
  item = item.strip
35
- key, val = item.split('=')
35
+ key, val = item.split("=")
36
36
 
37
37
  next unless key && val
38
38
  next unless key =~ SENTRY_PREFIX_REGEX
39
39
 
40
- baggage_key = key.split('-')[1]
40
+ baggage_key = key.split("-")[1]
41
41
  next unless baggage_key
42
42
 
43
43
  items[CGI.unescape(baggage_key)] = CGI.unescape(val)
@@ -64,7 +64,7 @@ module Sentry
64
64
  # @return [String]
65
65
  def serialize
66
66
  items = @items.map { |k, v| "#{SENTRY_PREFIX}#{CGI.escape(k)}=#{CGI.escape(v)}" }
67
- items.join(',')
67
+ items.join(",")
68
68
  end
69
69
  end
70
70
  end
@@ -1,16 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'logger'
3
+ require "logger"
4
4
 
5
5
  module Sentry
6
6
  class Breadcrumb
7
7
  module SentryLogger
8
8
  LEVELS = {
9
- ::Logger::DEBUG => 'debug',
10
- ::Logger::INFO => 'info',
11
- ::Logger::WARN => 'warn',
12
- ::Logger::ERROR => 'error',
13
- ::Logger::FATAL => 'fatal'
9
+ ::Logger::DEBUG => "debug",
10
+ ::Logger::INFO => "info",
11
+ ::Logger::WARN => "warn",
12
+ ::Logger::ERROR => "error",
13
+ ::Logger::FATAL => "fatal"
14
14
  }.freeze
15
15
 
16
16
  def add(*args, &block)
@@ -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 || "").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
@@ -1,11 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'securerandom'
4
- require 'sentry/cron/monitor_config'
3
+ require "securerandom"
4
+ require "sentry/cron/monitor_config"
5
+ require "sentry/utils/uuid"
5
6
 
6
7
  module Sentry
7
8
  class CheckInEvent < Event
8
- TYPE = 'check_in'
9
+ TYPE = "check_in"
9
10
 
10
11
  # uuid to identify this check-in.
11
12
  # @return [String]
@@ -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,23 +17,23 @@ 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)
30
33
  else
31
34
  @transport =
32
35
  case configuration.dsn&.scheme
33
- when 'http', 'https'
36
+ when "http", "https"
34
37
  HTTPTransport.new(configuration)
35
38
  else
36
39
  DummyTransport.new(configuration)
@@ -38,6 +41,10 @@ module Sentry
38
41
  end
39
42
 
40
43
  @spotlight_transport = SpotlightTransport.new(configuration) if configuration.spotlight
44
+
45
+ if configuration.enable_logs
46
+ @log_event_buffer = LogEventBuffer.new(configuration, self).start
47
+ end
41
48
  end
42
49
 
43
50
  # Applies the given scope's data to the event and sends it to Sentry.
@@ -49,7 +56,7 @@ module Sentry
49
56
  return unless configuration.sending_allowed?
50
57
 
51
58
  if event.is_a?(ErrorEvent) && !configuration.sample_allowed?
52
- transport.record_lost_event(:sample_rate, 'error')
59
+ transport.record_lost_event(:sample_rate, "error")
53
60
  return
54
61
  end
55
62
 
@@ -64,11 +71,11 @@ module Sentry
64
71
  if event.nil?
65
72
  log_debug("Discarded event because one of the event processors returned nil")
66
73
  transport.record_lost_event(:event_processor, data_category)
67
- transport.record_lost_event(:event_processor, 'span', num: spans_before + 1) if is_transaction
74
+ transport.record_lost_event(:event_processor, "span", num: spans_before + 1) if is_transaction
68
75
  return
69
76
  elsif is_transaction
70
77
  spans_delta = spans_before - event.spans.size
71
- transport.record_lost_event(:event_processor, 'span', num: spans_delta) if spans_delta > 0
78
+ transport.record_lost_event(:event_processor, "span", num: spans_delta) if spans_delta > 0
72
79
  end
73
80
 
74
81
  if async_block = configuration.async
@@ -76,7 +83,7 @@ module Sentry
76
83
  elsif configuration.background_worker_threads != 0 && hint.fetch(:background, true)
77
84
  unless dispatch_background_event(event, hint)
78
85
  transport.record_lost_event(:queue_overflow, data_category)
79
- transport.record_lost_event(:queue_overflow, 'span', num: spans_before + 1) if is_transaction
86
+ transport.record_lost_event(:queue_overflow, "span", num: spans_before + 1) if is_transaction
80
87
  end
81
88
  else
82
89
  send_event(event, hint)
@@ -88,6 +95,15 @@ module Sentry
88
95
  nil
89
96
  end
90
97
 
98
+ # Buffer a log event to be sent later with other logs in a single envelope
99
+ # @param event [LogEvent] the log event to be buffered
100
+ # @return [LogEvent]
101
+ def buffer_log_event(event, scope)
102
+ return unless event.is_a?(LogEvent)
103
+ @log_event_buffer.add_event(scope.apply_to_event(event))
104
+ event
105
+ end
106
+
91
107
  # Capture an envelope directly.
92
108
  # @param envelope [Envelope] the envelope to be captured.
93
109
  # @return [void]
@@ -100,6 +116,7 @@ module Sentry
100
116
  def flush
101
117
  transport.flush if configuration.sending_to_dsn_allowed?
102
118
  spotlight_transport.flush if spotlight_transport
119
+ @log_event_buffer&.flush
103
120
  end
104
121
 
105
122
  # Initializes an Event object with the given exception. Returns `nil` if the exception's class is excluded from reporting.
@@ -167,6 +184,23 @@ module Sentry
167
184
  )
168
185
  end
169
186
 
187
+ # Initializes a LogEvent object with the given message and options
188
+ #
189
+ # @param message [String] the log message
190
+ # @param level [Symbol] the log level (:trace, :debug, :info, :warn, :error, :fatal)
191
+ # @param options [Hash] additional options
192
+ # @option options [Array] :parameters Array of values to replace template tokens in the message
193
+ #
194
+ # @return [LogEvent] the created log event
195
+ def event_from_log(message, level:, **options)
196
+ return unless configuration.sending_allowed?
197
+
198
+ attributes = options.reject { |k, _| k == :level || k == :severity || k == :origin }
199
+ origin = options[:origin]
200
+
201
+ LogEvent.new(level: level, body: message, attributes: attributes, origin: origin)
202
+ end
203
+
170
204
  # Initializes an Event object with the given Transaction object.
171
205
  # @param transaction [Transaction] the transaction to be recorded.
172
206
  # @return [TransactionEvent]
@@ -183,8 +217,19 @@ module Sentry
183
217
  if event_type != TransactionEvent::TYPE && configuration.before_send
184
218
  event = configuration.before_send.call(event, hint)
185
219
 
186
- if event.nil?
187
- log_debug("Discarded event because before_send returned nil")
220
+ case event
221
+ when ErrorEvent, CheckInEvent
222
+ # do nothing
223
+ when Hash
224
+ log_debug(<<~MSG)
225
+ Returning a Hash from before_send is deprecated and will be removed in the next major version.
226
+ Please return a Sentry::ErrorEvent object instead.
227
+ MSG
228
+ else
229
+ # Avoid serializing the event object in this case because we aren't sure what it is and what it contains
230
+ log_debug(<<~MSG)
231
+ Discarded event because before_send didn't return a Sentry::ErrorEvent object but an instance of #{event.class}
232
+ MSG
188
233
  transport.record_lost_event(:before_send, data_category)
189
234
  return
190
235
  end
@@ -193,15 +238,25 @@ module Sentry
193
238
  if event_type == TransactionEvent::TYPE && configuration.before_send_transaction
194
239
  event = configuration.before_send_transaction.call(event, hint)
195
240
 
196
- if event.nil?
197
- log_debug("Discarded event because before_send_transaction returned nil")
198
- transport.record_lost_event(:before_send, 'transaction')
199
- transport.record_lost_event(:before_send, 'span', num: spans_before + 1)
200
- return
201
- else
241
+ if event.is_a?(TransactionEvent) || event.is_a?(Hash)
202
242
  spans_after = event.is_a?(TransactionEvent) ? event.spans.size : 0
203
243
  spans_delta = spans_before - spans_after
204
- transport.record_lost_event(:before_send, 'span', num: spans_delta) if spans_delta > 0
244
+ transport.record_lost_event(:before_send, "span", num: spans_delta) if spans_delta > 0
245
+
246
+ if event.is_a?(Hash)
247
+ log_debug(<<~MSG)
248
+ Returning a Hash from before_send_transaction is deprecated and will be removed in the next major version.
249
+ Please return a Sentry::TransactionEvent object instead.
250
+ MSG
251
+ end
252
+ else
253
+ # Avoid serializing the event object in this case because we aren't sure what it is and what it contains
254
+ log_debug(<<~MSG)
255
+ Discarded event because before_send_transaction didn't return a Sentry::TransactionEvent object but an instance of #{event.class}
256
+ MSG
257
+ transport.record_lost_event(:before_send, "transaction")
258
+ transport.record_lost_event(:before_send, "span", num: spans_before + 1)
259
+ return
205
260
  end
206
261
  end
207
262
 
@@ -212,10 +267,57 @@ module Sentry
212
267
  rescue => e
213
268
  log_error("Event sending failed", e, debug: configuration.debug)
214
269
  transport.record_lost_event(:network_error, data_category)
215
- transport.record_lost_event(:network_error, 'span', num: spans_before + 1) if event.is_a?(TransactionEvent)
270
+ transport.record_lost_event(:network_error, "span", num: spans_before + 1) if event.is_a?(TransactionEvent)
216
271
  raise
217
272
  end
218
273
 
274
+ # Send an envelope with batched logs
275
+ # @param log_events [Array<LogEvent>] the log events to be sent
276
+ # @api private
277
+ # @return [void]
278
+ def send_logs(log_events)
279
+ envelope = Envelope.new(
280
+ event_id: Sentry::Utils.uuid,
281
+ sent_at: Sentry.utc_now.iso8601,
282
+ dsn: configuration.dsn,
283
+ sdk: Sentry.sdk_meta
284
+ )
285
+
286
+ discarded_count = 0
287
+ envelope_items = []
288
+
289
+ if configuration.before_send_log
290
+ log_events.each do |log_event|
291
+ processed_log_event = configuration.before_send_log.call(log_event)
292
+
293
+ if processed_log_event
294
+ envelope_items << processed_log_event.to_hash
295
+ else
296
+ discarded_count += 1
297
+ end
298
+ end
299
+
300
+ envelope_items
301
+ else
302
+ envelope_items = log_events.map(&:to_hash)
303
+ end
304
+
305
+ envelope.add_item(
306
+ {
307
+ type: "log",
308
+ item_count: envelope_items.size,
309
+ content_type: "application/vnd.sentry.items.log+json"
310
+ },
311
+ { items: envelope_items }
312
+ )
313
+
314
+ send_envelope(envelope)
315
+
316
+ unless discarded_count.zero?
317
+ transport.record_lost_event(:before_send, "log_item", num: discarded_count)
318
+ end
319
+ end
320
+
219
321
  # Send an envelope directly to Sentry.
220
322
  # @param envelope [Envelope] the envelope to be sent.
221
323
  # @return [void]