Class: DeprecationCollector

Inherits:
Object
  • Object
show all
Defined in:
lib/deprecation_collector.rb,
lib/deprecation_collector/web.rb,
lib/deprecation_collector/storage.rb,
lib/deprecation_collector/version.rb,
lib/deprecation_collector/collectors.rb,
lib/deprecation_collector/web/router.rb,
lib/deprecation_collector/deprecation.rb,
lib/deprecation_collector/web/helpers.rb,
lib/deprecation_collector/web/application.rb,
lib/deprecation_collector/storage/active_record.rb

Overview

:nodoc:

Defined Under Namespace

Modules: ActiveSupportDeprecationCollectionPatch, KernelWarningCollector, MultipartWarningJoiner, Storage, WarningCollector Classes: Deprecation, Web

Constant Summary collapse

VERSION =
"0.8.0"
ACTIVE_SUPPORT_BEHAVIORS =
{
  rails71: ->(message, callstack, deprecator) do
    # TODO: use deprecator.gem_name, deprecator.deprecation_horizon
    DeprecationCollector.collect(message, callstack, :rails)
    ActiveSupport::Deprecation::DEFAULT_BEHAVIORS[stock_activesupport_behavior].call(message, callstack, deprecator)
  end,
  legacy: ->(message, callstack, deprecation_horizon, gem_name) do
    DeprecationCollector.collect(message, callstack, :rails)
    ActiveSupport::Deprecation::DEFAULT_BEHAVIORS[stock_activesupport_behavior].call(
      message, callstack, deprecation_horizon, gem_name
    )
  end
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(mutex: nil) ⇒ DeprecationCollector

Returns a new instance of DeprecationCollector.



62
63
64
65
66
67
# File 'lib/deprecation_collector.rb', line 62

def initialize(mutex: nil)
  @enabled = true
  @instance_mutex = mutex

  load_default_config
end

Instance Attribute Details

#app_nameObject

NB: count is expensive in production env (but possible if needed) - produces a lot of redis writes



55
56
57
# File 'lib/deprecation_collector.rb', line 55

def app_name
  @app_name
end

#app_revisionObject

NB: count is expensive in production env (but possible if needed) - produces a lot of redis writes



55
56
57
# File 'lib/deprecation_collector.rb', line 55

def app_revision
  @app_revision
end

#app_rootObject

NB: count is expensive in production env (but possible if needed) - produces a lot of redis writes



55
56
57
# File 'lib/deprecation_collector.rb', line 55

def app_root
  @app_root
end

#context_saver(&block) ⇒ Object



137
138
139
140
141
# File 'lib/deprecation_collector.rb', line 137

def context_saver(&block)
  return @context_saver unless block

  @context_saver = block
end

#countObject

Returns the value of attribute count.



59
60
61
# File 'lib/deprecation_collector.rb', line 59

def count
  @count
end

#exclude_realmsObject

NB: count is expensive in production env (but possible if needed) - produces a lot of redis writes



55
56
57
# File 'lib/deprecation_collector.rb', line 55

def exclude_realms
  @exclude_realms
end

#fingerprinter(&block) ⇒ Object



143
144
145
146
147
# File 'lib/deprecation_collector.rb', line 143

def fingerprinter(&block)
  return @fingerprinter unless block

  @fingerprinter = block
end

#key_prefixObject

Returns the value of attribute key_prefix.



59
60
61
# File 'lib/deprecation_collector.rb', line 59

def key_prefix
  @key_prefix
end

NB: count is expensive in production env (but possible if needed) - produces a lot of redis writes



55
56
57
# File 'lib/deprecation_collector.rb', line 55

def print_recurring
  @print_recurring
end

NB: count is expensive in production env (but possible if needed) - produces a lot of redis writes



55
56
57
# File 'lib/deprecation_collector.rb', line 55

def print_to_stderr
  @print_to_stderr
end

#raise_on_deprecationObject

NB: count is expensive in production env (but possible if needed) - produces a lot of redis writes



55
56
57
# File 'lib/deprecation_collector.rb', line 55

def raise_on_deprecation
  @raise_on_deprecation
end

#save_full_backtraceObject

NB: count is expensive in production env (but possible if needed) - produces a lot of redis writes



55
56
57
# File 'lib/deprecation_collector.rb', line 55

def save_full_backtrace
  @save_full_backtrace
end

#write_intervalObject

Returns the value of attribute write_interval.



59
60
61
# File 'lib/deprecation_collector.rb', line 59

def write_interval
  @write_interval
end

#write_interval_jitterObject

Returns the value of attribute write_interval_jitter.



59
60
61
# File 'lib/deprecation_collector.rb', line 59

def write_interval_jitter
  @write_interval_jitter
end

Class Method Details

.collect(message, backtrace, realm) ⇒ Object



31
32
33
# File 'lib/deprecation_collector.rb', line 31

def self.collect(message, backtrace, realm)
  instance.collect(message, backtrace, realm)
end

.create_instanceObject



23
24
25
26
27
28
29
# File 'lib/deprecation_collector.rb', line 23

def self.create_instance
  @instance_mutex.synchronize do
    # no real need to reuse the mutex, but it is used only once here anyway
    @instance ||= new(mutex: @instance_mutex)
  end
  @instance
end

.installObject

inside dev env may be called multiple times



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/deprecation_collector.rb', line 36

def self.install
  create_instance # to make it created, configuration comes later

  @instance_mutex.synchronize do
    unless @installed
      at_exit { instance.write_to_redis(force: true) }
      @installed = true
    end

    yield instance if block_given?
    instance.fetch_known_digests
  end

  install_collectors

  @instance
end

.instanceObject



17
18
19
20
21
# File 'lib/deprecation_collector.rb', line 17

def self.instance
  return @instance if defined?(@instance) && @instance

  create_instance
end

Instance Method Details

#app_root_prefixObject



149
150
151
# File 'lib/deprecation_collector.rb', line 149

def app_root_prefix
  "#{app_root}/"
end

#cleanup(&block) ⇒ Object

Raises:

  • (ArgumentError)


243
244
245
246
247
# File 'lib/deprecation_collector.rb', line 243

def cleanup(&block)
  raise ArgumentError, "provide a block to filter deprecations" unless block

  storage.cleanup(&block)
end

#cleanup_prefixesObject



153
154
155
# File 'lib/deprecation_collector.rb', line 153

def cleanup_prefixes
  @cleanup_prefixes ||= Gem.path + [app_root_prefix]
end

#collect(message, backtrace = caller_locations, realm = :unknown) ⇒ Object



157
158
159
160
161
162
163
164
165
166
167
# File 'lib/deprecation_collector.rb', line 157

def collect(message, backtrace = caller_locations, realm = :unknown)
  return if !@enabled || exclude_realms.include?(realm) || @ignore_message_regexp&.match?(message)
  raise "Deprecation: #{message}" if @raise_on_deprecation

  recursion_iterations_detected = backtrace.count { |l| l.path == __FILE__ && l.base_label == __method__.to_s }
  return if recursion_iterations_detected > 1 # we have a loop, ignore deep nested deprecations

  deprecation = Deprecation.new(message, realm, backtrace, cleanup_prefixes)
  fresh = store_deprecation(deprecation, allow_context: recursion_iterations_detected.zero?)
  log_deprecation_if_needed(deprecation, fresh)
end

#count?Boolean

Returns:

  • (Boolean)


173
174
175
# File 'lib/deprecation_collector.rb', line 173

def count?
  @count
end

#delete_deprecations(remove_digests) ⇒ Object



239
240
241
# File 'lib/deprecation_collector.rb', line 239

def delete_deprecations(remove_digests)
  storage.delete(remove_digests)
end

#disableObject



206
207
208
209
# File 'lib/deprecation_collector.rb', line 206

def disable
  @enabled = false
  storage.disable
end

#dumpObject



211
212
213
# File 'lib/deprecation_collector.rb', line 211

def dump
  read_each.to_a.compact.to_json
end

#enableObject



201
202
203
204
# File 'lib/deprecation_collector.rb', line 201

def enable
  storage.enable
  @enabled = true
end

#enabled?Boolean

Returns:

  • (Boolean)


197
198
199
# File 'lib/deprecation_collector.rb', line 197

def enabled?
  @enabled
end

#enabled_in_redis?Boolean

deprecated, use storage.enabled?

Returns:

  • (Boolean)


193
194
195
# File 'lib/deprecation_collector.rb', line 193

def enabled_in_redis?
  storage.enabled?
end

#fetch_known_digestsObject

prevent fresh process from wiring frequent already known messages



184
185
186
# File 'lib/deprecation_collector.rb', line 184

def fetch_known_digests
  storage.fetch_known_digests
end

#flush_redis(enable: false) ⇒ Object



188
189
190
# File 'lib/deprecation_collector.rb', line 188

def flush_redis(enable: false)
  storage.clear(enable: enable)
end

#ignored_messages=(val) ⇒ Object



133
134
135
# File 'lib/deprecation_collector.rb', line 133

def ignored_messages=(val)
  @ignore_message_regexp = (val && Regexp.union(val)) || nil
end

#import_dump(json) ⇒ Object



215
216
217
218
219
220
221
222
223
224
225
# File 'lib/deprecation_collector.rb', line 215

def import_dump(json)
  dump = JSON.parse(json)
  # TODO: some checks

  digests = dump.map { |dep| dep["digest"] }
  raise "need digests" unless digests.none?(&:nil?)

  dump_hash = dump.map { |dep| [dep.delete("digest"), dep] }.to_h

  storage.import(dump_hash)
end

#load_default_configObject



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/deprecation_collector.rb', line 69

def load_default_config
  if (redis = defined?($redis) && $redis) # rubocop:disable Style/GlobalVars
    self.redis = redis
  end
  @raise_on_deprecation = false
  @exclude_realms = []
  @ignore_message_regexp = nil
  @app_root = (defined?(Rails) && Rails.root.present? && Rails.root) || Dir.pwd
  # NB: in production with hugreds of workers may easily overload redis with writes, so more delay needed:
  self.count = false
  self.write_interval = 900 # 15.minutes
  self.write_interval_jitter = 60
  self.key_prefix = "deprecations"
  @context_saver = nil
end

#model=(val) ⇒ Object



92
93
94
95
96
# File 'lib/deprecation_collector.rb', line 92

def model=(val)
  require "deprecation_collector/storage/active_record" unless defined?(DeprecationCollector::Storage::ActiveRecord)
  self.storage = DeprecationCollector::Storage::ActiveRecord
  storage.model = val
end

#read_eachObject



227
228
229
230
231
232
233
# File 'lib/deprecation_collector.rb', line 227

def read_each
  return to_enum(:read_each) unless block_given?

  storage.read_each do |digest, data, count, notes|
    yield decode_deprecation(digest, data, count, notes)
  end
end

#read_one(digest) ⇒ Object



235
236
237
# File 'lib/deprecation_collector.rb', line 235

def read_one(digest)
  decode_deprecation(*storage.read_one(digest))
end

#redis=(val) ⇒ Object

Raises:

  • (ArgumentError)


85
86
87
88
89
90
# File 'lib/deprecation_collector.rb', line 85

def redis=(val)
  raise ArgumentError, "redis should not be nil" unless val

  self.storage = DeprecationCollector::Storage::Redis unless storage.respond_to?(:redis=)
  storage.redis = val
end

#storageObject



118
119
120
# File 'lib/deprecation_collector.rb', line 118

def storage
  @storage ||= DeprecationCollector::Storage::StdErr.new
end

#storage=(val) ⇒ Object



122
123
124
125
126
127
128
129
130
131
# File 'lib/deprecation_collector.rb', line 122

def storage=(val)
  unless val.is_a?(Class)
    @storage = val
    return
  end

  @storage = val.new(mutex: @instance_mutex, count: count,
                     write_interval: write_interval, write_interval_jitter: write_interval_jitter,
                     key_prefix: key_prefix)
end

#unsent_data?Boolean

Returns:

  • (Boolean)


169
170
171
# File 'lib/deprecation_collector.rb', line 169

def unsent_data?
  unsent_deprecations.any?
end

#write_to_redis(force: false) ⇒ Object



177
178
179
180
181
# File 'lib/deprecation_collector.rb', line 177

def write_to_redis(force: false)
  return unless force || @enabled

  storage.flush(force: force)
end