rollbar 2.19.3 → 2.21.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/.rubocop.yml +5 -0
- data/.travis.yml +42 -16
- data/Gemfile +31 -14
- data/data/rollbar.snippet.js +1 -1
- data/docs/configuration.md +9 -0
- data/gemfiles/rails30.gemfile +8 -10
- data/gemfiles/rails31.gemfile +8 -9
- data/gemfiles/rails32.gemfile +8 -9
- data/gemfiles/rails40.gemfile +8 -9
- data/gemfiles/rails41.gemfile +8 -9
- data/gemfiles/rails42.gemfile +8 -11
- data/gemfiles/rails50.gemfile +13 -12
- data/gemfiles/rails51.gemfile +13 -12
- data/gemfiles/rails52.gemfile +13 -12
- data/gemfiles/rails60.gemfile +67 -0
- data/lib/rails/rollbar_runner.rb +1 -1
- data/lib/rollbar/configuration.rb +11 -1
- data/lib/rollbar/item.rb +15 -6
- data/lib/rollbar/json.rb +2 -51
- data/lib/rollbar/language_support.rb +3 -19
- data/lib/rollbar/logger_proxy.rb +5 -1
- data/lib/rollbar/notifier.rb +23 -10
- data/lib/rollbar/plugins/basic_socket.rb +1 -1
- data/lib/rollbar/rake_tasks.rb +3 -147
- data/lib/rollbar/request_data_extractor.rb +3 -2
- data/lib/rollbar/rollbar_test.rb +147 -0
- data/lib/rollbar/scrubbers/params.rb +2 -2
- data/lib/rollbar/scrubbers/url.rb +0 -1
- data/lib/rollbar/truncation.rb +9 -2
- data/lib/rollbar/truncation/min_body_strategy.rb +2 -3
- data/lib/rollbar/truncation/remove_any_key_strategy.rb +123 -0
- data/lib/rollbar/truncation/remove_extra_strategy.rb +35 -0
- data/lib/rollbar/truncation/remove_request_strategy.rb +21 -0
- data/lib/rollbar/truncation/strings_strategy.rb +2 -3
- data/lib/rollbar/util.rb +2 -2
- data/lib/rollbar/util/hash.rb +15 -0
- data/lib/rollbar/version.rb +1 -1
- data/rollbar.gemspec +0 -2
- metadata +10 -21
- data/gemfiles/ruby_1_8_and_1_9_2.gemfile +0 -51
- data/lib/rollbar/json/default.rb +0 -11
- data/lib/rollbar/json/oj.rb +0 -16
@@ -6,7 +6,7 @@ Rollbar.plugins.define('basic_socket') do
|
|
6
6
|
# Needed to avoid active_support (< 4.1.0) bug serializing JSONs
|
7
7
|
dependency do
|
8
8
|
defined?(ActiveSupport::VERSION::STRING) &&
|
9
|
-
Gem::Version.new(ActiveSupport::VERSION::STRING) < Gem::Version.new('
|
9
|
+
Gem::Version.new(ActiveSupport::VERSION::STRING) < Gem::Version.new('4.1.0')
|
10
10
|
end
|
11
11
|
|
12
12
|
execute do
|
data/lib/rollbar/rake_tasks.rb
CHANGED
@@ -1,154 +1,10 @@
|
|
1
|
-
require 'rollbar'
|
2
|
-
begin
|
3
|
-
require 'rack/mock'
|
4
|
-
rescue LoadError
|
5
|
-
puts 'Cannot load rack/mock'
|
6
|
-
end
|
7
|
-
require 'logger'
|
8
1
|
|
9
2
|
namespace :rollbar do
|
10
3
|
desc 'Verify your gem installation by sending a test exception to Rollbar'
|
11
4
|
task :test => [:environment] do
|
12
|
-
|
13
|
-
|
14
|
-
end
|
15
|
-
|
16
|
-
# Module to inject into the Rails controllers or rack apps
|
17
|
-
module RollbarTest # :nodoc:
|
18
|
-
def test_rollbar
|
19
|
-
puts 'Raising RollbarTestingException to simulate app failure.'
|
20
|
-
|
21
|
-
raise RollbarTestingException.new, ::RollbarTest.success_message
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.run
|
25
|
-
return unless confirmed_token?
|
26
|
-
|
27
|
-
configure_rails if defined?(Rails)
|
28
|
-
|
29
|
-
puts 'Testing manual report...'
|
30
|
-
Rollbar.error('Test error from rollbar:test')
|
31
|
-
|
32
|
-
return unless defined?(Rack::MockRequest)
|
33
|
-
|
34
|
-
protocol, app = setup_app
|
35
|
-
|
36
|
-
puts 'Processing...'
|
37
|
-
env = Rack::MockRequest.env_for("#{protocol}://www.example.com/verify", 'REMOTE_ADDR' => '127.0.0.1')
|
38
|
-
status, = app.call(env)
|
39
|
-
|
40
|
-
puts error_message unless status.to_i == 500
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.configure_rails
|
44
|
-
Rails.logger = if defined?(ActiveSupport::TaggedLogging)
|
45
|
-
ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
|
46
|
-
else
|
47
|
-
Logger.new(STDOUT)
|
48
|
-
end
|
49
|
-
|
50
|
-
Rails.logger.level = Logger::DEBUG
|
51
|
-
Rollbar.preconfigure do |config|
|
52
|
-
config.logger = Rails.logger
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def self.confirmed_token?
|
57
|
-
return true if Rollbar.configuration.access_token
|
58
|
-
|
59
|
-
puts token_error_message
|
60
|
-
|
61
|
-
false
|
62
|
-
end
|
63
|
-
|
64
|
-
def self.authlogic_config
|
65
|
-
# from http://stackoverflow.com/questions/5270835/authlogic-activation-problems
|
66
|
-
return unless defined?(Authlogic)
|
67
|
-
|
68
|
-
Authlogic::Session::Base.controller = Authlogic::ControllerAdapters::RailsAdapter.new(self)
|
69
|
-
end
|
70
|
-
|
71
|
-
def self.setup_app
|
72
|
-
puts 'Setting up the test app.'
|
73
|
-
|
74
|
-
if defined?(Rails)
|
75
|
-
app = rails_app
|
5
|
+
rollbar_dir = Gem.loaded_specs['rollbar'].full_gem_path
|
6
|
+
require "#{rollbar_dir}/lib/rollbar/rollbar_test"
|
76
7
|
|
77
|
-
|
78
|
-
|
79
|
-
authlogic_config
|
80
|
-
|
81
|
-
[rails_protocol(app), app]
|
82
|
-
else
|
83
|
-
['http', rack_app]
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def self.rails_app
|
88
|
-
# The setup below is needed for Rails 5.x, but not for Rails 4.x and below.
|
89
|
-
# (And fails on Rails 4.x in various ways depending on the exact version.)
|
90
|
-
return Rails.application if Rails.version < '5.0.0'
|
91
|
-
|
92
|
-
# Spring now runs by default in development on all new Rails installs. This causes
|
93
|
-
# the new `/verify` route to not get picked up if `config.cache_classes == false`
|
94
|
-
# which is also a default in development env.
|
95
|
-
#
|
96
|
-
# `config.cache_classes` needs to be set, but the only possible time is at app load,
|
97
|
-
# so here we clone the default app with an updated config.
|
98
|
-
#
|
99
|
-
config = Rails.application.config
|
100
|
-
config.cache_classes = true
|
101
|
-
|
102
|
-
# Make a copy of the app, so the config can be updated.
|
103
|
-
Rails.application.class.name.constantize.new(:config => config)
|
104
|
-
end
|
105
|
-
|
106
|
-
def self.draw_rails_route(app)
|
107
|
-
app.routes_reloader.execute_if_updated
|
108
|
-
app.routes.draw do
|
109
|
-
get 'verify' => 'rollbar_test#verify', :as => 'verify'
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def self.rails_protocol(app)
|
114
|
-
defined?(app.config.force_ssl && app.config.force_ssl) ? 'https' : 'http'
|
115
|
-
end
|
116
|
-
|
117
|
-
def self.rack_app
|
118
|
-
Class.new do
|
119
|
-
include RollbarTest
|
120
|
-
|
121
|
-
def self.call(_env)
|
122
|
-
new.test_rollbar
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
def self.token_error_message
|
128
|
-
'Rollbar needs an access token configured. Check the README for instructions.'
|
129
|
-
end
|
130
|
-
|
131
|
-
def self.error_message
|
132
|
-
'Test failed! You may have a configuration issue, or you could be using a gem that\'s blocking the test. Contact support@rollbar.com if you need help troubleshooting.'
|
133
|
-
end
|
134
|
-
|
135
|
-
def self.success_message
|
136
|
-
'Testing rollbar with "rake rollbar:test". If you can see this, it works.'
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
class RollbarTestingException < RuntimeError; end
|
141
|
-
|
142
|
-
if defined?(Rails)
|
143
|
-
class RollbarTestController < ActionController::Base # :nodoc:
|
144
|
-
include RollbarTest
|
145
|
-
|
146
|
-
def verify
|
147
|
-
test_rollbar
|
148
|
-
end
|
149
|
-
|
150
|
-
def logger
|
151
|
-
nil
|
152
|
-
end
|
8
|
+
RollbarTest.run
|
153
9
|
end
|
154
10
|
end
|
@@ -208,8 +208,9 @@ module Rollbar
|
|
208
208
|
end
|
209
209
|
|
210
210
|
def json_request?(rack_req)
|
211
|
-
|
212
|
-
|
211
|
+
json_regex = /\bjson\b/
|
212
|
+
|
213
|
+
!!(rack_req.env['CONTENT_TYPE'] =~ json_regex)
|
213
214
|
end
|
214
215
|
|
215
216
|
def rollbar_route_params(env)
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'rollbar'
|
2
|
+
begin
|
3
|
+
require 'rack/mock'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'Cannot load rack/mock'
|
6
|
+
end
|
7
|
+
require 'logger'
|
8
|
+
|
9
|
+
# Module to inject into the Rails controllers or rack apps
|
10
|
+
module RollbarTest # :nodoc:
|
11
|
+
def test_rollbar
|
12
|
+
puts 'Raising RollbarTestingException to simulate app failure.'
|
13
|
+
|
14
|
+
raise RollbarTestingException.new, ::RollbarTest.success_message
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.run
|
18
|
+
return unless confirmed_token?
|
19
|
+
|
20
|
+
configure_rails if defined?(Rails)
|
21
|
+
|
22
|
+
puts 'Testing manual report...'
|
23
|
+
Rollbar.error('Test error from rollbar:test')
|
24
|
+
|
25
|
+
return unless defined?(Rack::MockRequest)
|
26
|
+
|
27
|
+
protocol, app = setup_app
|
28
|
+
|
29
|
+
puts 'Processing...'
|
30
|
+
env = Rack::MockRequest.env_for("#{protocol}://www.example.com/verify", 'REMOTE_ADDR' => '127.0.0.1')
|
31
|
+
status, = app.call(env)
|
32
|
+
|
33
|
+
puts error_message unless status.to_i == 500
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.configure_rails
|
37
|
+
Rails.logger = if defined?(ActiveSupport::TaggedLogging)
|
38
|
+
ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
|
39
|
+
else
|
40
|
+
Logger.new(STDOUT)
|
41
|
+
end
|
42
|
+
|
43
|
+
Rails.logger.level = Logger::DEBUG
|
44
|
+
Rollbar.preconfigure do |config|
|
45
|
+
config.logger = Rails.logger
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.confirmed_token?
|
50
|
+
return true if Rollbar.configuration.access_token
|
51
|
+
|
52
|
+
puts token_error_message
|
53
|
+
|
54
|
+
false
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.authlogic_config
|
58
|
+
# from http://stackoverflow.com/questions/5270835/authlogic-activation-problems
|
59
|
+
return unless defined?(Authlogic)
|
60
|
+
|
61
|
+
Authlogic::Session::Base.controller = Authlogic::ControllerAdapters::RailsAdapter.new(self)
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.setup_app
|
65
|
+
puts 'Setting up the test app.'
|
66
|
+
|
67
|
+
if defined?(Rails)
|
68
|
+
app = rails_app
|
69
|
+
|
70
|
+
draw_rails_route(app)
|
71
|
+
|
72
|
+
authlogic_config
|
73
|
+
|
74
|
+
[rails_protocol(app), app]
|
75
|
+
else
|
76
|
+
['http', rack_app]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.rails_app
|
81
|
+
# The setup below is needed for Rails 5.x, but not for Rails 4.x and below.
|
82
|
+
# (And fails on Rails 4.x in various ways depending on the exact version.)
|
83
|
+
return Rails.application if Rails.version < '5.0.0'
|
84
|
+
|
85
|
+
# Spring now runs by default in development on all new Rails installs. This causes
|
86
|
+
# the new `/verify` route to not get picked up if `config.cache_classes == false`
|
87
|
+
# which is also a default in development env.
|
88
|
+
#
|
89
|
+
# `config.cache_classes` needs to be set, but the only possible time is at app load,
|
90
|
+
# so here we clone the default app with an updated config.
|
91
|
+
#
|
92
|
+
config = Rails.application.config
|
93
|
+
config.cache_classes = true
|
94
|
+
|
95
|
+
# Make a copy of the app, so the config can be updated.
|
96
|
+
Rails.application.class.name.constantize.new(:config => config)
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.draw_rails_route(app)
|
100
|
+
app.routes_reloader.execute_if_updated
|
101
|
+
app.routes.draw do
|
102
|
+
get 'verify' => 'rollbar_test#verify', :as => 'verify'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.rails_protocol(app)
|
107
|
+
defined?(app.config.force_ssl && app.config.force_ssl) ? 'https' : 'http'
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.rack_app
|
111
|
+
Class.new do
|
112
|
+
include RollbarTest
|
113
|
+
|
114
|
+
def self.call(_env)
|
115
|
+
new.test_rollbar
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.token_error_message
|
121
|
+
'Rollbar needs an access token configured. Check the README for instructions.'
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.error_message
|
125
|
+
'Test failed! You may have a configuration issue, or you could be using a gem that\'s blocking the test. Contact support@rollbar.com if you need help troubleshooting.'
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.success_message
|
129
|
+
'Testing rollbar with "rake rollbar:test". If you can see this, it works.'
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class RollbarTestingException < RuntimeError; end
|
134
|
+
|
135
|
+
if defined?(Rails)
|
136
|
+
class RollbarTestController < ActionController::Base # :nodoc:
|
137
|
+
include RollbarTest
|
138
|
+
|
139
|
+
def verify
|
140
|
+
test_rollbar
|
141
|
+
end
|
142
|
+
|
143
|
+
def logger
|
144
|
+
nil
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -52,10 +52,10 @@ module Rollbar
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def build_whitelist_regex(whitelist)
|
55
|
-
fields = whitelist.find_all { |f| f.is_a?(String) || f.is_a?(Symbol) }
|
55
|
+
fields = whitelist.find_all { |f| f.is_a?(String) || f.is_a?(Symbol) || f.is_a?(Regexp) }
|
56
56
|
return unless fields.any?
|
57
57
|
|
58
|
-
Regexp.new(fields.map { |val| /\A#{Regexp.escape(val.to_s)}\z/ }.join('|'))
|
58
|
+
Regexp.new(fields.map { |val| val.is_a?(Regexp) ? val : /\A#{Regexp.escape(val.to_s)}\z/ }.join('|'))
|
59
59
|
end
|
60
60
|
|
61
61
|
def scrub(params, options)
|
data/lib/rollbar/truncation.rb
CHANGED
@@ -4,6 +4,9 @@ require 'rollbar/truncation/raw_strategy'
|
|
4
4
|
require 'rollbar/truncation/frames_strategy'
|
5
5
|
require 'rollbar/truncation/strings_strategy'
|
6
6
|
require 'rollbar/truncation/min_body_strategy'
|
7
|
+
require 'rollbar/truncation/remove_request_strategy'
|
8
|
+
require 'rollbar/truncation/remove_extra_strategy'
|
9
|
+
require 'rollbar/truncation/remove_any_key_strategy'
|
7
10
|
|
8
11
|
module Rollbar
|
9
12
|
module Truncation
|
@@ -13,13 +16,17 @@ module Rollbar
|
|
13
16
|
STRATEGIES = [RawStrategy,
|
14
17
|
FramesStrategy,
|
15
18
|
StringsStrategy,
|
16
|
-
MinBodyStrategy
|
19
|
+
MinBodyStrategy,
|
20
|
+
RemoveRequestStrategy,
|
21
|
+
RemoveExtraStrategy,
|
22
|
+
RemoveAnyKeyStrategy].freeze
|
17
23
|
|
18
|
-
def self.truncate(payload)
|
24
|
+
def self.truncate(payload, attempts = [])
|
19
25
|
result = nil
|
20
26
|
|
21
27
|
STRATEGIES.each do |strategy|
|
22
28
|
result = strategy.call(payload)
|
29
|
+
attempts << result.bytesize
|
23
30
|
break unless truncate?(result)
|
24
31
|
end
|
25
32
|
|
@@ -11,8 +11,7 @@ module Rollbar
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def call(payload)
|
14
|
-
|
15
|
-
body = new_payload['data']['body']
|
14
|
+
body = payload['data']['body']
|
16
15
|
|
17
16
|
if body['trace_chain']
|
18
17
|
body['trace_chain'] = body['trace_chain'].map do |trace_data|
|
@@ -22,7 +21,7 @@ module Rollbar
|
|
22
21
|
body['trace'] = truncate_trace_data(body['trace'])
|
23
22
|
end
|
24
23
|
|
25
|
-
dump(
|
24
|
+
dump(payload)
|
26
25
|
end
|
27
26
|
|
28
27
|
def truncate_trace_data(trace_data)
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'rollbar/util'
|
2
|
+
|
3
|
+
module Rollbar
|
4
|
+
module Truncation
|
5
|
+
class RemoveAnyKeyStrategy
|
6
|
+
include ::Rollbar::Truncation::Mixin
|
7
|
+
|
8
|
+
attr_accessor :payload, :data, :sizes, :extracted_title
|
9
|
+
|
10
|
+
def self.call(payload)
|
11
|
+
new(payload).call
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(payload)
|
15
|
+
@payload = payload
|
16
|
+
@data = payload['data']
|
17
|
+
@extracted_title = extract_title(data['body']) if data['body']
|
18
|
+
end
|
19
|
+
|
20
|
+
def call
|
21
|
+
remove_unknown_root_keys
|
22
|
+
|
23
|
+
json_payload = remove_oversized_data_keys
|
24
|
+
|
25
|
+
return json_payload if json_payload
|
26
|
+
|
27
|
+
dump(payload)
|
28
|
+
end
|
29
|
+
|
30
|
+
def remove_unknown_root_keys
|
31
|
+
payload.keys.reject { |key| root_keys.include?(key) }.each do |key|
|
32
|
+
truncation_key['root'] ||= {}
|
33
|
+
size = dump(payload.delete(key)).bytesize
|
34
|
+
truncation_key['root'][key] = "unknown root key removed, size: #{size} bytes"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def remove_oversized_data_keys
|
39
|
+
data_keys.keys.sort { |a, b| data_keys[b] <=> data_keys[a] }.each do |key|
|
40
|
+
json_payload = remove_key_and_return_payload(key)
|
41
|
+
|
42
|
+
return json_payload unless truncate?(json_payload)
|
43
|
+
end
|
44
|
+
|
45
|
+
false
|
46
|
+
end
|
47
|
+
|
48
|
+
def remove_key_and_return_payload(key)
|
49
|
+
size = data_keys[key]
|
50
|
+
|
51
|
+
data.delete(key)
|
52
|
+
|
53
|
+
replace_message_body if key == 'body'
|
54
|
+
|
55
|
+
truncation_key[key] = "key removed, size: #{size} bytes"
|
56
|
+
|
57
|
+
dump(payload)
|
58
|
+
end
|
59
|
+
|
60
|
+
def replace_message_body
|
61
|
+
data['body'] = message_key
|
62
|
+
data['title'] ||= extracted_title if extracted_title
|
63
|
+
end
|
64
|
+
|
65
|
+
def truncation_key
|
66
|
+
@truncation_key ||=
|
67
|
+
# initialize the diagnostic key for truncation
|
68
|
+
(data['notifier']['diagnostic'] ||= {}) &&
|
69
|
+
(data['notifier']['diagnostic']['truncation'] ||= {})
|
70
|
+
end
|
71
|
+
|
72
|
+
def root_keys
|
73
|
+
# Valid keys in root of payload
|
74
|
+
%w[access_token data]
|
75
|
+
end
|
76
|
+
|
77
|
+
def skip_keys
|
78
|
+
# Don't try to truncate these data keys
|
79
|
+
%w[notifier uuid title platform language framework level]
|
80
|
+
end
|
81
|
+
|
82
|
+
def message_key
|
83
|
+
# use this message if data.body gets removed
|
84
|
+
{
|
85
|
+
'message' => {
|
86
|
+
'body' => 'Payload keys removed due to oversized payload. See diagnostic key'
|
87
|
+
}
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
def extract_title(body)
|
92
|
+
return body['message']['body'] if body['message'] && body['message']['body']
|
93
|
+
return extract_title_from_trace(body['trace']) if body['trace']
|
94
|
+
return extract_title_from_trace(body['trace_chain'][0]) if body['trace_chain'] && body['trace_chain'][0]
|
95
|
+
end
|
96
|
+
|
97
|
+
def extract_title_from_trace(trace)
|
98
|
+
exception = trace['exception']
|
99
|
+
|
100
|
+
"#{exception['class']}: #{exception['message']}"
|
101
|
+
end
|
102
|
+
|
103
|
+
def data_keys
|
104
|
+
@data_keys ||= {}.tap do |hash|
|
105
|
+
data.keys.reject { |key| skip_keys.include?(key) }.each do |key|
|
106
|
+
set_key_size(key, hash)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def set_key_size(key, hash)
|
112
|
+
size = dump(data[key]).bytesize
|
113
|
+
hash[key] = size
|
114
|
+
rescue ::JSON::GeneratorError
|
115
|
+
hash[key] = 0 # don't try to truncate non JSON object
|
116
|
+
|
117
|
+
# Log it
|
118
|
+
truncation_key['non_json_keys'] ||= {}
|
119
|
+
truncation_key['non_json_keys'][key] = data[key].class
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|