raygun4ruby 3.2.0 → 3.2.5.pre

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 (117) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +21 -18
  3. data/.rspec +1 -1
  4. data/.travis.yml +20 -12
  5. data/CHANGELOG.md +130 -117
  6. data/Gemfile +4 -4
  7. data/LICENSE.txt +22 -22
  8. data/README.md +420 -420
  9. data/Rakefile +27 -27
  10. data/examples/sinatras_raygun.rb +17 -17
  11. data/lib/generators/raygun/install_generator.rb +26 -26
  12. data/lib/raygun.rb +179 -179
  13. data/lib/raygun/affected_user.rb +59 -59
  14. data/lib/raygun/breadcrumbs.rb +34 -34
  15. data/lib/raygun/breadcrumbs/breadcrumb.rb +34 -34
  16. data/lib/raygun/breadcrumbs/store.rb +86 -86
  17. data/lib/raygun/client.rb +308 -303
  18. data/lib/raygun/configuration.rb +194 -194
  19. data/lib/raygun/error.rb +10 -10
  20. data/lib/raygun/javascript_tracker.rb +42 -42
  21. data/lib/raygun/middleware/breadcrumbs_store_initializer.rb +19 -19
  22. data/lib/raygun/middleware/javascript_exception_tracking.rb +32 -32
  23. data/lib/raygun/middleware/rack_exception_interceptor.rb +18 -18
  24. data/lib/raygun/middleware/rails_insert_affected_user.rb +26 -26
  25. data/lib/raygun/railtie.rb +39 -39
  26. data/lib/raygun/services/apply_whitelist_filter_to_payload.rb +27 -27
  27. data/lib/raygun/sidekiq.rb +71 -67
  28. data/lib/raygun/version.rb +3 -3
  29. data/lib/raygun4ruby.rb +1 -1
  30. data/lib/resque/failure/raygun.rb +25 -25
  31. data/lib/tasks/raygun.tasks +7 -7
  32. data/raygun4ruby.gemspec +45 -45
  33. data/spec/dummy/.gitignore +17 -0
  34. data/spec/dummy/Gemfile +47 -0
  35. data/spec/dummy/README.rdoc +28 -0
  36. data/spec/dummy/Rakefile +6 -6
  37. data/spec/dummy/app/assets/config/manifest.js +3 -3
  38. data/spec/dummy/app/assets/javascripts/application.js +13 -15
  39. data/spec/dummy/app/assets/stylesheets/application.css +15 -15
  40. data/spec/dummy/app/controllers/application_controller.rb +5 -2
  41. data/spec/dummy/app/controllers/home_controller.rb +4 -4
  42. data/spec/dummy/app/helpers/application_helper.rb +2 -2
  43. data/spec/dummy/app/{assets/javascripts/channels → mailers}/.keep +0 -0
  44. data/spec/dummy/{storage → app/models}/.keep +0 -0
  45. data/spec/dummy/app/views/home/index.html.erb +3 -3
  46. data/spec/dummy/app/views/home/index.json.erb +1 -1
  47. data/spec/dummy/app/views/layouts/application.html.erb +14 -15
  48. data/spec/dummy/bin/bundle +3 -3
  49. data/spec/dummy/bin/rails +9 -4
  50. data/spec/dummy/bin/rake +9 -4
  51. data/spec/dummy/bin/setup +29 -36
  52. data/spec/dummy/bin/spring +17 -0
  53. data/spec/dummy/config.ru +4 -5
  54. data/spec/dummy/config/application.rb +26 -18
  55. data/spec/dummy/config/boot.rb +3 -5
  56. data/spec/dummy/config/database.yml +25 -25
  57. data/spec/dummy/config/environment.rb +5 -5
  58. data/spec/dummy/config/environments/development.rb +41 -61
  59. data/spec/dummy/config/environments/production.rb +79 -94
  60. data/spec/dummy/config/initializers/assets.rb +11 -14
  61. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -7
  62. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -5
  63. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -4
  64. data/spec/dummy/config/initializers/inflections.rb +16 -16
  65. data/spec/dummy/config/initializers/mime_types.rb +4 -4
  66. data/spec/dummy/config/initializers/session_store.rb +3 -0
  67. data/spec/dummy/config/initializers/to_time_preserves_timezone.rb +10 -0
  68. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -14
  69. data/spec/dummy/config/locales/en.yml +23 -33
  70. data/spec/dummy/config/routes.rb +58 -3
  71. data/spec/dummy/config/secrets.yml +22 -0
  72. data/spec/dummy/db/seeds.rb +7 -0
  73. data/spec/dummy/{db/test.sqlite3 → lib/tasks/.keep} +0 -0
  74. data/spec/dummy/public/404.html +67 -67
  75. data/spec/dummy/public/422.html +67 -67
  76. data/spec/dummy/public/500.html +66 -66
  77. data/spec/dummy/public/robots.txt +5 -0
  78. data/spec/dummy/{public/apple-touch-icon-precomposed.png → vendor/assets/javascripts/.keep} +0 -0
  79. data/spec/dummy/{public/apple-touch-icon.png → vendor/assets/stylesheets/.keep} +0 -0
  80. data/spec/features/javascript_spec.rb +48 -48
  81. data/spec/rails_helper.rb +4 -4
  82. data/spec/raygun/breadcrumbs/breadcrumb_spec.rb +171 -171
  83. data/spec/raygun/breadcrumbs/store_spec.rb +170 -170
  84. data/spec/raygun/raygun_spec.rb +47 -47
  85. data/spec/services/apply_whitelist_filter_to_payload_spec.rb +251 -251
  86. data/spec/spec_helper.rb +24 -24
  87. data/spec/support/fake_logger.rb +17 -17
  88. data/test/integration/client_test.rb +19 -19
  89. data/test/test_helper.rb +72 -72
  90. data/test/unit/affected_user_test.rb +136 -136
  91. data/test/unit/client_test.rb +812 -790
  92. data/test/unit/configuration_test.rb +206 -206
  93. data/test/unit/raygun_test.rb +25 -25
  94. data/test/unit/resque_failure_test.rb +24 -24
  95. data/test/unit/sidekiq_failure_test.rb +32 -32
  96. metadata +36 -49
  97. data/lib/raygun/testable.rb +0 -23
  98. data/spec/dummy/.ruby-version +0 -1
  99. data/spec/dummy/app/assets/javascripts/cable.js +0 -13
  100. data/spec/dummy/app/channels/application_cable/channel.rb +0 -4
  101. data/spec/dummy/app/channels/application_cable/connection.rb +0 -4
  102. data/spec/dummy/app/jobs/application_job.rb +0 -2
  103. data/spec/dummy/app/mailers/application_mailer.rb +0 -4
  104. data/spec/dummy/app/models/application_record.rb +0 -3
  105. data/spec/dummy/app/views/layouts/mailer.html.erb +0 -13
  106. data/spec/dummy/app/views/layouts/mailer.text.erb +0 -1
  107. data/spec/dummy/bin/update +0 -31
  108. data/spec/dummy/bin/yarn +0 -11
  109. data/spec/dummy/config/cable.yml +0 -10
  110. data/spec/dummy/config/environments/test.rb +0 -46
  111. data/spec/dummy/config/initializers/application_controller_renderer.rb +0 -8
  112. data/spec/dummy/config/initializers/content_security_policy.rb +0 -25
  113. data/spec/dummy/config/puma.rb +0 -34
  114. data/spec/dummy/config/spring.rb +0 -6
  115. data/spec/dummy/config/storage.yml +0 -34
  116. data/spec/dummy/db/schema.rb +0 -15
  117. data/spec/dummy/package.json +0 -5
@@ -1,59 +1,59 @@
1
- module Raygun
2
- class AffectedUser
3
-
4
- DEFAULT_MAPPING = {
5
- identifier: [ :id, :username ],
6
- email: :email,
7
- full_name: [ :full_name, :name ],
8
- first_name: :first_name,
9
- uuid: :uuid
10
- }.freeze
11
- SUPPORTED_ATTRIBUTES = DEFAULT_MAPPING.keys.freeze
12
- NAME_TO_RAYGUN_NAME_MAPPING = {
13
- identifier: :identifier,
14
- email: :email,
15
- full_name: :fullName,
16
- first_name: :firstName,
17
- uuid: :uuid
18
- }.freeze
19
-
20
- class << self
21
- def information_hash(user_object)
22
- if user_object.nil? || user_object.is_a?(String)
23
- handle_anonymous_user(user_object)
24
- else
25
- handle_known_user(user_object)
26
- end
27
- end
28
-
29
- private
30
-
31
- def handle_anonymous_user(user_object)
32
- result = { isAnonymous: true }
33
- result[:identifier] = user_object unless user_object.nil?
34
- result
35
- end
36
-
37
- def handle_known_user(user_object)
38
- SUPPORTED_ATTRIBUTES.reduce({ isAnonymous: false }) do |result, attribute|
39
- mapping = Raygun.configuration.affected_user_mapping
40
- method = mapping[attribute]
41
-
42
- value = if method.is_a? Proc
43
- method.call(user_object)
44
- else
45
- attributes = Array(method)
46
- attribute_to_use = attributes.select do |attr|
47
- user_object.respond_to?(attr, true)
48
- end.first
49
-
50
- user_object.send(attribute_to_use) unless attribute_to_use == nil
51
- end
52
-
53
- result[NAME_TO_RAYGUN_NAME_MAPPING[attribute]] = value unless value == nil
54
- result
55
- end
56
- end
57
- end
58
- end
59
- end
1
+ module Raygun
2
+ class AffectedUser
3
+
4
+ DEFAULT_MAPPING = {
5
+ identifier: [ :id, :username ],
6
+ email: :email,
7
+ full_name: [ :full_name, :name ],
8
+ first_name: :first_name,
9
+ uuid: :uuid
10
+ }.freeze
11
+ SUPPORTED_ATTRIBUTES = DEFAULT_MAPPING.keys.freeze
12
+ NAME_TO_RAYGUN_NAME_MAPPING = {
13
+ identifier: :identifier,
14
+ email: :email,
15
+ full_name: :fullName,
16
+ first_name: :firstName,
17
+ uuid: :uuid
18
+ }.freeze
19
+
20
+ class << self
21
+ def information_hash(user_object)
22
+ if user_object.nil? || user_object.is_a?(String)
23
+ handle_anonymous_user(user_object)
24
+ else
25
+ handle_known_user(user_object)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def handle_anonymous_user(user_object)
32
+ result = { isAnonymous: true }
33
+ result[:identifier] = user_object unless user_object.nil?
34
+ result
35
+ end
36
+
37
+ def handle_known_user(user_object)
38
+ SUPPORTED_ATTRIBUTES.reduce({ isAnonymous: false }) do |result, attribute|
39
+ mapping = Raygun.configuration.affected_user_mapping
40
+ method = mapping[attribute]
41
+
42
+ value = if method.is_a? Proc
43
+ method.call(user_object)
44
+ else
45
+ attributes = Array(method)
46
+ attribute_to_use = attributes.select do |attr|
47
+ user_object.respond_to?(attr, true)
48
+ end.first
49
+
50
+ user_object.send(attribute_to_use) unless attribute_to_use == nil
51
+ end
52
+
53
+ result[NAME_TO_RAYGUN_NAME_MAPPING[attribute]] = value unless value == nil
54
+ result
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,34 +1,34 @@
1
- module Raygun
2
- module Breadcrumbs
3
- BREADCRUMB_LEVELS = [
4
- :debug,
5
- :info,
6
- :warning,
7
- :error,
8
- :fatal
9
- ]
10
-
11
- def record_breadcrumb(
12
- message: nil,
13
- category: '',
14
- level: :info,
15
- timestamp: Time.now.utc.to_i,
16
- metadata: {},
17
- class_name: nil,
18
- method_name: nil,
19
- line_number: nil
20
- )
21
- class_name = class_name || self.class.name
22
- Raygun::Breadcrumbs::Store.record(
23
- message: message,
24
- category: category,
25
- level: level,
26
- timestamp: timestamp,
27
- metadata: metadata,
28
- class_name: class_name,
29
- method_name: method_name,
30
- line_number: line_number,
31
- )
32
- end
33
- end
34
- end
1
+ module Raygun
2
+ module Breadcrumbs
3
+ BREADCRUMB_LEVELS = [
4
+ :debug,
5
+ :info,
6
+ :warning,
7
+ :error,
8
+ :fatal
9
+ ]
10
+
11
+ def record_breadcrumb(
12
+ message: nil,
13
+ category: '',
14
+ level: :info,
15
+ timestamp: Time.now.utc.to_i,
16
+ metadata: {},
17
+ class_name: nil,
18
+ method_name: nil,
19
+ line_number: nil
20
+ )
21
+ class_name = class_name || self.class.name
22
+ Raygun::Breadcrumbs::Store.record(
23
+ message: message,
24
+ category: category,
25
+ level: level,
26
+ timestamp: timestamp,
27
+ metadata: metadata,
28
+ class_name: class_name,
29
+ method_name: method_name,
30
+ line_number: line_number,
31
+ )
32
+ end
33
+ end
34
+ end
@@ -1,34 +1,34 @@
1
- module Raygun
2
- module Breadcrumbs
3
- class Breadcrumb
4
- ATTRIBUTES = [
5
- :message, :category, :metadata, :class_name,
6
- :method_name, :line_number, :timestamp, :level,
7
- :type
8
- ]
9
- attr_accessor(*ATTRIBUTES)
10
-
11
- def build_payload
12
- payload = {
13
- message: message,
14
- category: category,
15
- level: Breadcrumbs::BREADCRUMB_LEVELS.index(level),
16
- CustomData: metadata,
17
- timestamp: timestamp,
18
- type: type
19
- }
20
-
21
- payload[:location] = "#{class_name}:#{method_name}" unless class_name == nil
22
- payload[:location] += ":#{line_number}" if payload.has_key?(:location) && line_number != nil
23
-
24
- Hash[payload.select do |k, v|
25
- v != nil
26
- end]
27
- end
28
-
29
- def size
30
- return message.length + 100
31
- end
32
- end
33
- end
34
- end
1
+ module Raygun
2
+ module Breadcrumbs
3
+ class Breadcrumb
4
+ ATTRIBUTES = [
5
+ :message, :category, :metadata, :class_name,
6
+ :method_name, :line_number, :timestamp, :level,
7
+ :type
8
+ ]
9
+ attr_accessor(*ATTRIBUTES)
10
+
11
+ def build_payload
12
+ payload = {
13
+ message: message,
14
+ category: category,
15
+ level: Breadcrumbs::BREADCRUMB_LEVELS.index(level),
16
+ CustomData: metadata,
17
+ timestamp: timestamp,
18
+ type: type
19
+ }
20
+
21
+ payload[:location] = "#{class_name}:#{method_name}" unless class_name == nil
22
+ payload[:location] += ":#{line_number}" if payload.has_key?(:location) && line_number != nil
23
+
24
+ Hash[payload.select do |k, v|
25
+ v != nil
26
+ end]
27
+ end
28
+
29
+ def size
30
+ return message.length + 100
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,86 +1,86 @@
1
- require_relative 'breadcrumb'
2
-
3
- module Raygun
4
- module Breadcrumbs
5
- class Store
6
- def self.initialize
7
- Thread.current[:breadcrumbs] ||= []
8
- end
9
-
10
- def self.clear
11
- Thread.current[:breadcrumbs] = nil
12
- end
13
-
14
- def self.stored
15
- Thread.current[:breadcrumbs]
16
- end
17
-
18
- def self.record(
19
- message: nil,
20
- category: '',
21
- level: :info,
22
- timestamp: Time.now.utc.to_i,
23
- metadata: {},
24
- class_name: nil,
25
- method_name: nil,
26
- line_number: nil
27
- )
28
- raise ArgumentError.new('missing keyword: message') if message == nil
29
- crumb = Breadcrumb.new
30
-
31
- crumb.message = message
32
- crumb.category = category
33
- crumb.level = level
34
- crumb.metadata = metadata
35
- crumb.timestamp = timestamp
36
- crumb.type = 'manual'
37
-
38
- caller = caller_locations[1]
39
- crumb.class_name = class_name
40
- crumb.method_name = method_name || caller.label
41
- crumb.line_number = line_number || caller.lineno
42
-
43
- Thread.current[:breadcrumbs] << crumb if should_record?(crumb)
44
- end
45
-
46
- def self.any?
47
- stored != nil && stored.length > 0
48
- end
49
-
50
- def self.take_until_size(size)
51
- breadcrumb_size = 0
52
-
53
- stored.reverse.take_while do |crumb|
54
- breadcrumb_size += crumb.size
55
-
56
- breadcrumb_size < size
57
- end.reverse
58
- end
59
-
60
- private
61
-
62
- def self.should_record?(crumb)
63
- if stored.nil?
64
- if Raygun.configuration.debug
65
- Raygun.log('[Raygun.breadcrumbs] store is uninitialized while breadcrumb is being recorded, discarding breadcrumb')
66
- end
67
-
68
- return false
69
- end
70
-
71
- levels = Raygun::Breadcrumbs::BREADCRUMB_LEVELS
72
-
73
- active_level = levels.index(Raygun.configuration.breadcrumb_level)
74
- crumb_level = levels.index(crumb.level) || -1
75
-
76
- discard = crumb_level < active_level
77
-
78
- if discard && Raygun.configuration.debug
79
- Raygun.log("[Raygun.breadcrumbs] discarding breadcrumb because #{crumb.level} is below active breadcrumb level (#{Raygun.configuration.breadcrumb_level})")
80
- end
81
-
82
- !discard
83
- end
84
- end
85
- end
86
- end
1
+ require_relative 'breadcrumb'
2
+
3
+ module Raygun
4
+ module Breadcrumbs
5
+ class Store
6
+ def self.initialize
7
+ Thread.current[:breadcrumbs] ||= []
8
+ end
9
+
10
+ def self.clear
11
+ Thread.current[:breadcrumbs] = nil
12
+ end
13
+
14
+ def self.stored
15
+ Thread.current[:breadcrumbs]
16
+ end
17
+
18
+ def self.record(
19
+ message: nil,
20
+ category: '',
21
+ level: :info,
22
+ timestamp: Time.now.utc.to_i,
23
+ metadata: {},
24
+ class_name: nil,
25
+ method_name: nil,
26
+ line_number: nil
27
+ )
28
+ raise ArgumentError.new('missing keyword: message') if message == nil
29
+ crumb = Breadcrumb.new
30
+
31
+ crumb.message = message
32
+ crumb.category = category
33
+ crumb.level = level
34
+ crumb.metadata = metadata
35
+ crumb.timestamp = timestamp
36
+ crumb.type = 'manual'
37
+
38
+ caller = caller_locations[1]
39
+ crumb.class_name = class_name
40
+ crumb.method_name = method_name || caller.label
41
+ crumb.line_number = line_number || caller.lineno
42
+
43
+ Thread.current[:breadcrumbs] << crumb if should_record?(crumb)
44
+ end
45
+
46
+ def self.any?
47
+ stored != nil && stored.length > 0
48
+ end
49
+
50
+ def self.take_until_size(size)
51
+ breadcrumb_size = 0
52
+
53
+ stored.reverse.take_while do |crumb|
54
+ breadcrumb_size += crumb.size
55
+
56
+ breadcrumb_size < size
57
+ end.reverse
58
+ end
59
+
60
+ private
61
+
62
+ def self.should_record?(crumb)
63
+ if stored.nil?
64
+ if Raygun.configuration.debug
65
+ Raygun.log('[Raygun.breadcrumbs] store is uninitialized while breadcrumb is being recorded, discarding breadcrumb')
66
+ end
67
+
68
+ return false
69
+ end
70
+
71
+ levels = Raygun::Breadcrumbs::BREADCRUMB_LEVELS
72
+
73
+ active_level = levels.index(Raygun.configuration.breadcrumb_level)
74
+ crumb_level = levels.index(crumb.level) || -1
75
+
76
+ discard = crumb_level < active_level
77
+
78
+ if discard && Raygun.configuration.debug
79
+ Raygun.log("[Raygun.breadcrumbs] discarding breadcrumb because #{crumb.level} is below active breadcrumb level (#{Raygun.configuration.breadcrumb_level})")
80
+ end
81
+
82
+ !discard
83
+ end
84
+ end
85
+ end
86
+ end
data/lib/raygun/client.rb CHANGED
@@ -1,303 +1,308 @@
1
- module Raygun
2
- # client for the Raygun REST APIv1
3
- # as per http://raygun.io/raygun-providers/rest-json-api?v=1
4
- class Client
5
-
6
- ENV_IP_ADDRESS_KEYS = %w(action_dispatch.remote_ip raygun.remote_ip REMOTE_ADDR)
7
- NO_API_KEY_MESSAGE = "[RAYGUN] Just a note, you've got no API Key configured, which means we can't report exceptions. Specify your Raygun API key using Raygun#setup (find yours at https://app.raygun.io)"
8
- MAX_BREADCRUMBS_SIZE = 100_000
9
-
10
- include HTTParty
11
-
12
- def initialize
13
- @api_key = require_api_key
14
- @headers = {
15
- "X-ApiKey" => @api_key
16
- }
17
-
18
- enable_http_proxy if Raygun.configuration.proxy_settings[:address]
19
- self.class.base_uri Raygun.configuration.api_url
20
- self.class.default_timeout(Raygun.configuration.error_report_send_timeout)
21
- end
22
-
23
- def require_api_key
24
- Raygun.configuration.api_key || print_api_key_warning
25
- end
26
-
27
- def track_exception(exception_instance, env = {}, user = nil)
28
- create_entry(build_payload_hash(exception_instance, env, user))
29
- end
30
-
31
- private
32
-
33
- def enable_http_proxy
34
- self.class.http_proxy(Raygun.configuration.proxy_settings[:address],
35
- Raygun.configuration.proxy_settings[:port] || "80",
36
- Raygun.configuration.proxy_settings[:username],
37
- Raygun.configuration.proxy_settings[:password])
38
- end
39
-
40
- def client_details
41
- {
42
- name: Raygun::CLIENT_NAME,
43
- version: Raygun::VERSION,
44
- clientUrl: Raygun::CLIENT_URL
45
- }
46
- end
47
-
48
- def error_details(exception)
49
- details = {
50
- className: exception.class.to_s,
51
- message: exception.message.to_s.encode('UTF-16', :undef => :replace, :invalid => :replace).encode('UTF-8'),
52
- stackTrace: (exception.backtrace || []).map { |line| stack_trace_for(line) },
53
- }
54
-
55
- details.update(innerError: error_details(exception.cause)) if exception.respond_to?(:cause) && exception.cause
56
-
57
- details
58
- end
59
-
60
- def stack_trace_for(line)
61
- # see http://www.ruby-doc.org/core-2.0/Exception.html#method-i-backtrace
62
- file_name, line_number, method = line.split(":")
63
- {
64
- lineNumber: line_number,
65
- fileName: file_name,
66
- methodName: method ? method.gsub(/^in `(.*?)'$/, "\\1") : "(none)"
67
- }
68
- end
69
-
70
- def hostname
71
- Socket.gethostname
72
- end
73
-
74
- def version
75
- Raygun.configuration.version
76
- end
77
-
78
- def user_information(env)
79
- env["raygun.affected_user"]
80
- end
81
-
82
- def affected_user_present?(env)
83
- !!env["raygun.affected_user"]
84
- end
85
-
86
- def rack_env
87
- ENV["RACK_ENV"]
88
- end
89
-
90
- def rails_env
91
- ENV["RAILS_ENV"]
92
- end
93
-
94
- def request_information(env)
95
- Raygun.log('retrieving request information')
96
-
97
- return {} if env.nil? || env.empty?
98
- {
99
- hostName: env["SERVER_NAME"],
100
- url: env["PATH_INFO"],
101
- httpMethod: env["REQUEST_METHOD"],
102
- iPAddress: "#{ip_address_from(env)}",
103
- queryString: Rack::Utils.parse_nested_query(env["QUERY_STRING"]),
104
- headers: headers(env),
105
- form: form_params(env),
106
- rawData: raw_data(env)
107
- }
108
- end
109
-
110
- def headers(rack_env)
111
- rack_env.select { |k, v| k.to_s.start_with?("HTTP_") }.inject({}) do |hsh, (k, v)|
112
- hsh[normalize_raygun_header_key(k)] = v
113
- hsh
114
- end
115
- end
116
-
117
- def normalize_raygun_header_key(key)
118
- key.sub(/^HTTP_/, '')
119
- .sub(/_/, ' ')
120
- .split.map(&:capitalize).join(' ')
121
- .sub(/ /, '-')
122
- end
123
-
124
- def form_params(env)
125
- Raygun.log('retrieving form params')
126
-
127
- params = action_dispatch_params(env) || rack_params(env) || {}
128
- filter_params_with_blacklist(params, env["action_dispatch.parameter_filter"])
129
- end
130
-
131
- def action_dispatch_params(env)
132
- env["action_dispatch.request.parameters"]
133
- end
134
-
135
- def rack_params(env)
136
- request = Rack::Request.new(env)
137
- request.params if env["rack.input"]
138
- end
139
-
140
- def raw_data(rack_env)
141
- Raygun.log('retrieving raw data')
142
- request = Rack::Request.new(rack_env)
143
-
144
- return unless Raygun.configuration.record_raw_data
145
- return if request.get?
146
- Raygun.log('passed raw_data checks')
147
-
148
- input = rack_env['rack.input']
149
-
150
- if input && !request.form_data?
151
- input.rewind
152
-
153
- body = input.read(4096) || ''
154
- input.rewind
155
-
156
- body
157
- else
158
- {}
159
- end
160
- end
161
-
162
- def filter_custom_data(env)
163
- params = env.delete(:custom_data) || {}
164
- filter_params_with_blacklist(params, env["action_dispatch.parameter_filter"])
165
- end
166
-
167
- # see http://raygun.io/raygun-providers/rest-json-api?v=1
168
- def build_payload_hash(exception_instance, env = {}, user = nil)
169
- Raygun.log('building payload hash')
170
- custom_data = filter_custom_data(env) || {}
171
- exception_custom_data = if exception_instance.respond_to?(:raygun_custom_data)
172
- exception_instance.raygun_custom_data
173
- else
174
- {}
175
- end
176
-
177
- tags = env.delete(:tags) || []
178
-
179
- if rails_env
180
- tags << rails_env
181
- else
182
- tags << rack_env
183
- end
184
-
185
- configuration_tags = []
186
- if Raygun.configuration.tags.is_a?(Proc)
187
- configuration_tags = Raygun.configuration.tags.call(exception_instance, env)
188
- else
189
- configuration_tags = Raygun.configuration.tags
190
- end
191
-
192
- Raygun.log('set tags')
193
-
194
- grouping_key = env.delete(:grouping_key)
195
-
196
- configuration_custom_data = Raygun.configuration.custom_data
197
- configured_custom_data = if configuration_custom_data.is_a?(Proc)
198
- configuration_custom_data.call(exception_instance, env)
199
- else
200
- configuration_custom_data
201
- end
202
-
203
- Raygun.log('set custom data')
204
-
205
- error_details = {
206
- machineName: hostname,
207
- version: version,
208
- client: client_details,
209
- error: error_details(exception_instance),
210
- userCustomData: exception_custom_data.merge(custom_data).merge(configured_custom_data),
211
- tags: configuration_tags.concat(tags).compact.uniq,
212
- request: request_information(env),
213
- environment: {
214
- utcOffset: Time.now.utc_offset / 3600
215
- }
216
- }
217
- store = ::Raygun::Breadcrumbs::Store
218
- error_details[:breadcrumbs] = store.take_until_size(MAX_BREADCRUMBS_SIZE).map(&:build_payload) if store.any?
219
-
220
- Raygun.log('set details and breadcrumbs')
221
-
222
- error_details.merge!(groupingKey: grouping_key) if grouping_key
223
-
224
- user_details = if affected_user_present?(env)
225
- user_information(env)
226
- elsif user != nil
227
- AffectedUser.information_hash(user)
228
- end
229
- error_details.merge!(user: user_details) unless user_details == nil
230
-
231
- Raygun.log('set user details')
232
-
233
- if Raygun.configuration.filter_payload_with_whitelist
234
- Raygun.log('filtering payload with whitelist')
235
- error_details = filter_payload_with_whitelist(error_details)
236
- end
237
-
238
- {
239
- occurredOn: Time.now.utc.iso8601,
240
- details: error_details
241
- }
242
- end
243
-
244
- def create_entry(payload_hash)
245
- Raygun.log('sending payload to api')
246
-
247
- self.class.post(
248
- "/entries",
249
- verify_peer: true,
250
- verify: true,
251
- headers: @headers,
252
- body: JSON.generate(payload_hash),
253
- )
254
- end
255
-
256
- def filter_params_with_blacklist(params_hash = {}, extra_filter_keys = nil)
257
- filter_parameters = Raygun.configuration.filter_parameters
258
-
259
- if filter_parameters.is_a? Proc
260
- filter_parameters.call(params_hash)
261
- else
262
- filter_keys = (Array(extra_filter_keys) + filter_parameters).map(&:to_s)
263
-
264
- filter_params_with_array(params_hash, filter_keys)
265
- end
266
- end
267
-
268
- def filter_payload_with_whitelist(payload_hash)
269
- shape = Raygun.configuration.whitelist_payload_shape
270
-
271
- if shape.is_a? Proc
272
- shape.call(payload_hash)
273
- else
274
- # Always keep the client hash, so force it to true here
275
- Services::ApplyWhitelistFilterToPayload.new.call(shape.merge(client: true), payload_hash)
276
- end
277
- end
278
-
279
- def filter_params_with_array(params_hash, filter_keys)
280
- # Recursive filtering of (nested) hashes
281
- (params_hash || {}).inject({}) do |result, (k, v)|
282
- result[k] = case v
283
- when Hash
284
- filter_params_with_array(v, filter_keys)
285
- else
286
- filter_keys.any? { |fk| /#{fk}/i === k.to_s } ? "[FILTERED]" : v
287
- end
288
- result
289
- end
290
- end
291
-
292
- def ip_address_from(env_hash)
293
- ENV_IP_ADDRESS_KEYS.each do |key_to_try|
294
- return env_hash[key_to_try] unless env_hash[key_to_try].nil? || env_hash[key_to_try] == ""
295
- end
296
- "(Not Available)"
297
- end
298
-
299
- def print_api_key_warning
300
- $stderr.puts(NO_API_KEY_MESSAGE)
301
- end
302
- end
303
- end
1
+ module Raygun
2
+ # client for the Raygun REST APIv1
3
+ # as per https://raygun.com/documentation/product-guides/crash-reporting/api/
4
+ class Client
5
+
6
+ ENV_IP_ADDRESS_KEYS = %w(action_dispatch.remote_ip raygun.remote_ip REMOTE_ADDR)
7
+ NO_API_KEY_MESSAGE = "[RAYGUN] Just a note, you've got no API Key configured, which means we can't report exceptions. Specify your Raygun API key using Raygun#setup (find yours at https://app.raygun.com)"
8
+ MAX_BREADCRUMBS_SIZE = 100_000
9
+
10
+ include HTTParty
11
+
12
+ def initialize
13
+ @api_key = require_api_key
14
+ @headers = {
15
+ "X-ApiKey" => @api_key
16
+ }
17
+
18
+ enable_http_proxy if Raygun.configuration.proxy_settings[:address]
19
+ self.class.base_uri Raygun.configuration.api_url
20
+ self.class.default_timeout(Raygun.configuration.error_report_send_timeout)
21
+ end
22
+
23
+ def require_api_key
24
+ Raygun.configuration.api_key || print_api_key_warning
25
+ end
26
+
27
+ def track_exception(exception_instance, env = {}, user = nil)
28
+ create_entry(build_payload_hash(exception_instance, env, user))
29
+ end
30
+
31
+ private
32
+
33
+ def enable_http_proxy
34
+ self.class.http_proxy(Raygun.configuration.proxy_settings[:address],
35
+ Raygun.configuration.proxy_settings[:port] || "80",
36
+ Raygun.configuration.proxy_settings[:username],
37
+ Raygun.configuration.proxy_settings[:password])
38
+ end
39
+
40
+ def client_details
41
+ {
42
+ name: Raygun::CLIENT_NAME,
43
+ version: Raygun::VERSION,
44
+ clientUrl: Raygun::CLIENT_URL
45
+ }
46
+ end
47
+
48
+ def error_details(exception)
49
+ details = {
50
+ className: exception.class.to_s,
51
+ message: exception.message.to_s.encode('UTF-16', :undef => :replace, :invalid => :replace).encode('UTF-8'),
52
+ stackTrace: (exception.backtrace || []).map { |line| stack_trace_for(line) },
53
+ }
54
+
55
+ details.update(innerError: error_details(exception.cause)) if exception.respond_to?(:cause) && exception.cause
56
+
57
+ details
58
+ end
59
+
60
+ def stack_trace_for(line)
61
+ # see http://www.ruby-doc.org/core-2.0/Exception.html#method-i-backtrace
62
+ file_name, line_number, method = line.split(":")
63
+ {
64
+ lineNumber: line_number,
65
+ fileName: file_name,
66
+ methodName: method ? method.gsub(/^in `(.*?)'$/, "\\1") : "(none)"
67
+ }
68
+ end
69
+
70
+ def hostname
71
+ Socket.gethostname
72
+ end
73
+
74
+ def version
75
+ Raygun.configuration.version
76
+ end
77
+
78
+ def user_information(env)
79
+ env["raygun.affected_user"]
80
+ end
81
+
82
+ def affected_user_present?(env)
83
+ !!env["raygun.affected_user"]
84
+ end
85
+
86
+ def rack_env
87
+ ENV["RACK_ENV"]
88
+ end
89
+
90
+ def rails_env
91
+ ENV["RAILS_ENV"]
92
+ end
93
+
94
+ def request_information(env)
95
+ Raygun.log('retrieving request information')
96
+
97
+ return {} if env.nil? || env.empty?
98
+ {
99
+ hostName: env["SERVER_NAME"],
100
+ url: env["PATH_INFO"],
101
+ httpMethod: env["REQUEST_METHOD"],
102
+ iPAddress: "#{ip_address_from(env)}",
103
+ queryString: Rack::Utils.parse_nested_query(env["QUERY_STRING"]),
104
+ headers: headers(env),
105
+ form: form_params(env),
106
+ rawData: raw_data(env)
107
+ }
108
+ end
109
+
110
+ def headers(rack_env)
111
+ rack_env.select { |k, v| k.to_s.start_with?("HTTP_") }.inject({}) do |hsh, (k, v)|
112
+ hsh[normalize_raygun_header_key(k)] = v
113
+ hsh
114
+ end
115
+ end
116
+
117
+ def normalize_raygun_header_key(key)
118
+ key.sub(/^HTTP_/, '')
119
+ .sub(/_/, ' ')
120
+ .split.map(&:capitalize).join(' ')
121
+ .sub(/ /, '-')
122
+ end
123
+
124
+ def form_params(env)
125
+ Raygun.log('retrieving form params')
126
+
127
+ params = action_dispatch_params(env) || rack_params(env) || {}
128
+ filter_params_with_blacklist(params, env["action_dispatch.parameter_filter"])
129
+ end
130
+
131
+ def action_dispatch_params(env)
132
+ env["action_dispatch.request.parameters"]
133
+ end
134
+
135
+ def rack_params(env)
136
+ request = Rack::Request.new(env)
137
+ request.params if env["rack.input"]
138
+ end
139
+
140
+ def raw_data(rack_env)
141
+ Raygun.log('retrieving raw data')
142
+ request = Rack::Request.new(rack_env)
143
+
144
+ return unless Raygun.configuration.record_raw_data
145
+ return if request.get?
146
+ Raygun.log('passed raw_data checks')
147
+
148
+ input = rack_env['rack.input']
149
+
150
+ if input && !request.form_data?
151
+ input.rewind
152
+
153
+ body = input.read(4096) || ''
154
+ input.rewind
155
+
156
+ body
157
+ else
158
+ {}
159
+ end
160
+ end
161
+
162
+ def filter_custom_data(env)
163
+ params = env.delete(:custom_data) || {}
164
+ filter_params_with_blacklist(params, env["action_dispatch.parameter_filter"])
165
+ end
166
+
167
+ # see https://raygun.com/documentation/product-guides/crash-reporting/api/
168
+ def build_payload_hash(exception_instance, env = {}, user = nil)
169
+ Raygun.log('building payload hash')
170
+ custom_data = filter_custom_data(env) || {}
171
+ exception_custom_data = if exception_instance.respond_to?(:raygun_custom_data)
172
+ exception_instance.raygun_custom_data
173
+ else
174
+ {}
175
+ end
176
+
177
+ tags = env.delete(:tags) || []
178
+
179
+ if rails_env
180
+ tags << rails_env
181
+ else
182
+ tags << rack_env
183
+ end
184
+
185
+ combined_tags = []
186
+
187
+ if Raygun.configuration.tags.is_a?(Proc)
188
+ configuration_tags = Raygun.configuration.tags.call(exception_instance, env)
189
+ else
190
+ configuration_tags = Raygun.configuration.tags
191
+ end
192
+
193
+ combined_tags.concat(configuration_tags)
194
+
195
+ Raygun.log('set tags')
196
+
197
+ grouping_key = env.delete(:grouping_key)
198
+ correlation_id = env.delete(:correlation_id)
199
+
200
+ configuration_custom_data = Raygun.configuration.custom_data
201
+ configured_custom_data = if configuration_custom_data.is_a?(Proc)
202
+ configuration_custom_data.call(exception_instance, env)
203
+ else
204
+ configuration_custom_data
205
+ end
206
+
207
+ Raygun.log('set custom data')
208
+
209
+ error_details = {
210
+ machineName: hostname,
211
+ version: version,
212
+ client: client_details,
213
+ error: error_details(exception_instance),
214
+ userCustomData: exception_custom_data.merge(custom_data).merge(configured_custom_data),
215
+ tags: combined_tags.concat(tags).compact.uniq,
216
+ request: request_information(env),
217
+ environment: {
218
+ utcOffset: Time.now.utc_offset / 3600
219
+ }
220
+ }
221
+ store = ::Raygun::Breadcrumbs::Store
222
+ error_details[:breadcrumbs] = store.take_until_size(MAX_BREADCRUMBS_SIZE).map(&:build_payload) if store.any?
223
+
224
+ Raygun.log('set details and breadcrumbs')
225
+
226
+ error_details.merge!(groupingKey: grouping_key) if grouping_key
227
+ error_details.merge!(correlationId: correlation_id) if correlation_id
228
+
229
+ user_details = if affected_user_present?(env)
230
+ user_information(env)
231
+ elsif user != nil
232
+ AffectedUser.information_hash(user)
233
+ end
234
+ error_details.merge!(user: user_details) unless user_details == nil
235
+
236
+ Raygun.log('set user details')
237
+
238
+ if Raygun.configuration.filter_payload_with_whitelist
239
+ Raygun.log('filtering payload with whitelist')
240
+ error_details = filter_payload_with_whitelist(error_details)
241
+ end
242
+
243
+ {
244
+ occurredOn: Time.now.utc.iso8601,
245
+ details: error_details
246
+ }
247
+ end
248
+
249
+ def create_entry(payload_hash)
250
+ Raygun.log('sending payload to api')
251
+
252
+ self.class.post(
253
+ "/entries",
254
+ verify_peer: true,
255
+ verify: true,
256
+ headers: @headers,
257
+ body: JSON.generate(payload_hash),
258
+ )
259
+ end
260
+
261
+ def filter_params_with_blacklist(params_hash = {}, extra_filter_keys = nil)
262
+ filter_parameters = Raygun.configuration.filter_parameters
263
+
264
+ if filter_parameters.is_a? Proc
265
+ filter_parameters.call(params_hash)
266
+ else
267
+ filter_keys = (Array(extra_filter_keys) + filter_parameters).map(&:to_s)
268
+
269
+ filter_params_with_array(params_hash, filter_keys)
270
+ end
271
+ end
272
+
273
+ def filter_payload_with_whitelist(payload_hash)
274
+ shape = Raygun.configuration.whitelist_payload_shape
275
+
276
+ if shape.is_a? Proc
277
+ shape.call(payload_hash)
278
+ else
279
+ # Always keep the client hash, so force it to true here
280
+ Services::ApplyWhitelistFilterToPayload.new.call(shape.merge(client: true), payload_hash)
281
+ end
282
+ end
283
+
284
+ def filter_params_with_array(params_hash, filter_keys)
285
+ # Recursive filtering of (nested) hashes
286
+ (params_hash || {}).inject({}) do |result, (k, v)|
287
+ result[k] = case v
288
+ when Hash
289
+ filter_params_with_array(v, filter_keys)
290
+ else
291
+ filter_keys.any? { |fk| /#{fk}/i === k.to_s } ? "[FILTERED]" : v
292
+ end
293
+ result
294
+ end
295
+ end
296
+
297
+ def ip_address_from(env_hash)
298
+ ENV_IP_ADDRESS_KEYS.each do |key_to_try|
299
+ return env_hash[key_to_try] unless env_hash[key_to_try].nil? || env_hash[key_to_try] == ""
300
+ end
301
+ "(Not Available)"
302
+ end
303
+
304
+ def print_api_key_warning
305
+ $stderr.puts(NO_API_KEY_MESSAGE)
306
+ end
307
+ end
308
+ end