appmap 0.94.1 → 0.95.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ca46315eb8aa31221cee8a1d656187a4e4355e63f6b2be1d591829dddb76f7e0
4
- data.tar.gz: d038130a2827f01f573bf69dbe32ab837d7ff68b28dce7a6076a80eee2f4550e
3
+ metadata.gz: a3f036b61b1750e2820f676694a15af9461404dc9b8f048add0b9b7291dd1d4a
4
+ data.tar.gz: f40f6587ecbf21c250952b1ecfc51349d6ad3cf53276d6cf30d9a97f859295b2
5
5
  SHA512:
6
- metadata.gz: 58a84ec34e17b22acf37fd0b9f964fb93f82531cb7f800721d33214b1bc434b81e47cbf7202f87ddeade990644d4cc512898125bc59851e82f035f9093c31eb2
7
- data.tar.gz: a8a0bfa9cf83dc59d6e3848228f602bf8030c01601755a853a55ee4b452a970e2de1c823649da1fc0b557b8b9b6ba24c142f324975a20a222aadc239d37958b1
6
+ metadata.gz: bd45853f98e82f90b6885b615931f73b125a5194041221c0bf9f21b41ab1f2285489ef3b7e1915905b41e434e65d607eebdf232d53a6bc1e1b75f516f73d2222
7
+ data.tar.gz: 3465e8521239b8eadc1145e06847bb454a1f6b43ef0adf6ed50f8d2fb6eccb63ac79a86b913628db7344b10c8b68980dd6c3aa5ac9741eac4bfef6b39d109a76
data/.gitignore CHANGED
@@ -2,12 +2,14 @@
2
2
  /html/
3
3
  .bundle
4
4
  /.yardoc
5
+ /.yarn
5
6
  /_yardoc/
6
7
  /coverage/
7
8
  /doc/
8
9
  /pkg/
9
10
  /spec/reports/
10
11
  tmp/
12
+ appmap_hook.log
11
13
  vendor
12
14
  node_modules
13
15
  Gemfile.lock
data/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ 14
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.0.2
data/.rufo ADDED
@@ -0,0 +1,2 @@
1
+ # .rufo
2
+ quote_style :single
data/CHANGELOG.md CHANGED
@@ -1,3 +1,20 @@
1
+ # [0.95.0](https://github.com/getappmap/appmap-ruby/compare/v0.94.1...v0.95.0) (2022-12-15)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Disable active_record hooks ([239986d](https://github.com/getappmap/appmap-ruby/commit/239986deded12e65384b441033180c30a3ffe698))
7
+ * Fix Array#pack hook ([2a8b2ed](https://github.com/getappmap/appmap-ruby/commit/2a8b2ed4d9fb047213e6a3fd427d846c5d312e2f))
8
+ * Fix name of rails7 test database ([6581b65](https://github.com/getappmap/appmap-ruby/commit/6581b65c088ee8415fd150d11c4ab6793173f0e2))
9
+ * Typo in label configuration ([999b25b](https://github.com/getappmap/appmap-ruby/commit/999b25b75057a7f6061cfbc9cdc04616f5aeb998))
10
+
11
+
12
+ ### Features
13
+
14
+ * Enable deserialize.safe labels ([62be78a](https://github.com/getappmap/appmap-ruby/commit/62be78a0502d83c9b5a2b7d97abd17d144bd41c5))
15
+ * Enhance the hook log a lot ([c93d91c](https://github.com/getappmap/appmap-ruby/commit/c93d91c5be86b9195e14ef40741600c633638b40))
16
+ * Enhancing hook logging and profiling ([01dbe4b](https://github.com/getappmap/appmap-ruby/commit/01dbe4bb973b71fea0885f55d17b80d6b6b8fb6e))
17
+
1
18
  ## [0.94.1](https://github.com/getappmap/appmap-ruby/compare/v0.94.0...v0.94.1) (2022-11-23)
2
19
 
3
20
 
@@ -19,6 +19,6 @@
19
19
  - method: ActiveSupport::MessageEncryptor#encrypt_and_sign
20
20
  require_name: active_support/message_encryptor
21
21
  force: true
22
- - method: ActiveSupport::MessageEncryptor#decrypt_and_verifdy
22
+ - method: ActiveSupport::MessageEncryptor#decrypt_and_verify
23
23
  require_name: active_support/message_encryptor
24
24
  force: true
@@ -7,9 +7,9 @@
7
7
  - method: Marshal#dump
8
8
  require_name: ruby
9
9
  label: serialize
10
- - method: String#pack
10
+ - method: Array#pack
11
11
  require_name: ruby
12
- label: string.pack
12
+ label: array.pack
13
13
  - methods:
14
14
  - String#unpack
15
15
  - String#unpack1
data/lib/appmap/config.rb CHANGED
@@ -8,6 +8,7 @@ require 'appmap/handler'
8
8
  require 'appmap/service/guesser'
9
9
  require 'appmap/swagger/configuration'
10
10
  require 'appmap/depends/configuration'
11
+ require_relative './hook_log'
11
12
 
12
13
  module AppMap
13
14
  class Config
@@ -499,7 +500,11 @@ module AppMap
499
500
 
500
501
  def never_hook?(cls, method)
501
502
  _, separator, = ::AppMap::Hook.qualify_method_name(method)
502
- return true if exclude.member?(cls.name) || exclude.member?([ cls.name, separator, method.name ].join)
503
+ if exclude.member?(cls.name) || exclude.member?([ cls.name, separator, method.name ].join)
504
+ HookLog.log "Hooking of #{method} disabled by configuration" if HookLog.enabled?
505
+ return true
506
+ end
507
+ false
503
508
  end
504
509
  end
505
510
  end
@@ -1,2 +1,2 @@
1
1
  - method: Rails::Application#config_for
2
- # label: deserialize.safe
2
+ label: deserialize.safe
@@ -1,2 +1,2 @@
1
1
  - method: Rails::Application::Configuration#database_configuration
2
- # label: deserialize.safe
2
+ label: deserialize.safe
@@ -38,13 +38,13 @@ module AppMap
38
38
  end
39
39
 
40
40
  def activate
41
- if Hook::LOG
41
+ if HookLog.enabled?
42
42
  msg = if method_display_name
43
- "#{method_display_name}"
44
- else
45
- "#{hook_method.name} (class resolution deferred)"
46
- end
47
- warn "AppMap: Hooking #{msg} at line #{(hook_method.source_location || []).join(':')}"
43
+ "#{method_display_name}"
44
+ else
45
+ "#{hook_method.name} (class resolution deferred)"
46
+ end
47
+ HookLog.log "Hooking #{msg} at line #{(hook_method.source_location || []).join(':')}"
48
48
  end
49
49
 
50
50
  hook_method_parameters = hook_method.parameters.dup.freeze
@@ -65,8 +65,7 @@ module AppMap
65
65
  protected
66
66
 
67
67
  def defining_class(hook_class)
68
- cls = \
69
- if RUBY_MAJOR_VERSION == 2 && RUBY_MINOR_VERSION <= 5
68
+ cls = if RUBY_MAJOR_VERSION == 2 && RUBY_MINOR_VERSION <= 5
70
69
  hook_class
71
70
  .ancestors
72
71
  .select { |cls| cls.method_defined?(hook_method.name) }
data/lib/appmap/hook.rb CHANGED
@@ -1,17 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'English'
4
+ require_relative './hook_log'
4
5
 
5
6
  module AppMap
6
7
  class Hook
7
- LOG = (ENV['APPMAP_DEBUG'] == 'true' || ENV['DEBUG'] == 'true')
8
- LOG_HOOK = (ENV['DEBUG_HOOK'] == 'true')
9
-
10
8
  OBJECT_INSTANCE_METHODS = %i[! != !~ <=> == === =~ __id__ __send__ class clone define_singleton_method display dup
11
9
  enum_for eql? equal? extend freeze frozen? hash inspect instance_eval instance_exec instance_of? instance_variable_defined? instance_variable_get instance_variable_set instance_variables is_a? itself kind_of? method methods nil? object_id private_methods protected_methods public_method public_methods public_send remove_instance_variable respond_to? send singleton_class singleton_method singleton_methods taint tainted? tap then to_enum to_s to_h to_a trust untaint untrust untrusted? yield_self].freeze
12
10
  OBJECT_STATIC_METHODS = %i[! != !~ < <= <=> == === =~ > >= __id__ __send__ alias_method allocate ancestors attr
13
11
  attr_accessor attr_reader attr_writer autoload autoload? class class_eval class_exec class_variable_defined? class_variable_get class_variable_set class_variables clone const_defined? const_get const_missing const_set constants define_method define_singleton_method deprecate_constant display dup enum_for eql? equal? extend freeze frozen? hash include include? included_modules inspect instance_eval instance_exec instance_method instance_methods instance_of? instance_variable_defined? instance_variable_get instance_variable_set instance_variables is_a? itself kind_of? method method_defined? methods module_eval module_exec name new nil? object_id prepend private_class_method private_constant private_instance_methods private_method_defined? private_methods protected_instance_methods protected_method_defined? protected_methods public_class_method public_constant public_instance_method public_instance_methods public_method public_method_defined? public_methods public_send remove_class_variable remove_instance_variable remove_method respond_to? send singleton_class singleton_class? singleton_method singleton_methods superclass taint tainted? tap then to_enum to_s trust undef_method untaint untrust untrusted? yield_self].freeze
14
- SLOW_PACKAGE_THRESHOLD = 0.05
12
+ SLOW_PACKAGE_THRESHOLD = 0.001
15
13
 
16
14
  @unbound_method_arity = ::UnboundMethod.instance_method(:arity)
17
15
  @method_arity = ::Method.instance_method(:arity)
@@ -21,7 +19,7 @@ module AppMap
21
19
  Mutex.new.synchronize do
22
20
  @hook_builtins = true if @hook_builtins.nil?
23
21
 
24
- return false unless @hook_builtins
22
+ next false unless @hook_builtins
25
23
 
26
24
  @hook_builtins = false
27
25
  true
@@ -41,9 +39,9 @@ module AppMap
41
39
  def qualify_method_name(method)
42
40
  if method.owner.singleton_class?
43
41
  class_name = singleton_method_owner_name(method)
44
- [ class_name, '.', method.name ]
42
+ [class_name, '.', method.name]
45
43
  else
46
- [ method.owner.name, '#', method.name ]
44
+ [method.owner.name, '#', method.name]
47
45
  end
48
46
  end
49
47
  end
@@ -69,19 +67,23 @@ module AppMap
69
67
  @slow_packages = Set.new
70
68
 
71
69
  if ENV['APPMAP_PROFILE_HOOK'] == 'true'
70
+ dump_times = lambda do
71
+ @module_load_times
72
+ .keys
73
+ .select { |key| !@slow_packages.member?(key) }
74
+ .each do |key|
75
+ elapsed = @module_load_times[key]
76
+ if elapsed >= SLOW_PACKAGE_THRESHOLD
77
+ @slow_packages.add(key)
78
+ warn "AppMap: Package #{key} took #{@module_load_times[key]} seconds to hook"
79
+ end
80
+ end
81
+ end
82
+
83
+ at_exit &dump_times
72
84
  Thread.new do
73
- sleep 1
74
85
  while true
75
- @module_load_times
76
- .keys
77
- .select { |key| !@slow_packages.member?(key) }
78
- .each do |key|
79
- elapsed = @module_load_times[key]
80
- if elapsed >= SLOW_PACKAGE_THRESHOLD
81
- @slow_packages.add(key)
82
- warn "AppMap: Package #{key} took #{@module_load_times[key]} seconds to hook"
83
- end
84
- end
86
+ dump_times.call
85
87
  sleep 5
86
88
  end
87
89
  end
@@ -99,45 +101,51 @@ module AppMap
99
101
  hook_loaded_code = lambda do |hooks_by_class, builtin|
100
102
  hooks_by_class.each do |class_name, hooks|
101
103
  Array(hooks).each do |hook|
102
- if builtin && hook.package.require_name && hook.package.require_name != 'ruby'
103
- require hook.package.require_name
104
- end
105
-
106
- Array(hook.method_names).each do |method_name|
107
- method_name = method_name.to_sym
108
-
109
- warn "AppMap: Initiating hook for builtin #{class_name} #{method_name}" if LOG
110
-
104
+ HookLog.builtin class_name do
105
+ if builtin && hook.package.require_name && hook.package.require_name != 'ruby'
106
+ begin
107
+ require hook.package.require_name
108
+ rescue
109
+ HookLog.load_error hook.package.require_name, "Unable to require #{hook.package.require_name}: #{$!}" if HookLog.enabled?
110
+ next
111
+ end
112
+ end
113
+
111
114
  begin
112
115
  base_cls = Object.const_get class_name
113
116
  rescue NameError
117
+ HookLog.load_error class_name, "Class #{class_name} not found in global scope" if HookLog.enabled?
114
118
  next
115
119
  end
116
-
117
- hook_method = lambda do |entry|
118
- cls, method = entry
119
- next if config.never_hook?(cls, method)
120
-
121
- hook.package.handler_class.new(hook.package, cls, method).activate
122
- end
123
-
124
- methods = []
125
- # irb(main):001:0> Kernel.public_instance_method(:system)
126
- # (irb):1:in `public_instance_method': method `system' for module `Kernel' is private (NameError)
127
- if base_cls == Kernel
128
- methods << [ base_cls, base_cls.instance_method(method_name) ] rescue nil
129
- end
130
- methods << [ base_cls, base_cls.public_instance_method(method_name) ] rescue nil
131
- methods << [ base_cls, base_cls.protected_instance_method(method_name) ] rescue nil
132
- if base_cls.respond_to?(:singleton_class)
133
- methods << [ base_cls.singleton_class, base_cls.singleton_class.public_instance_method(method_name) ] rescue nil
134
- methods << [ base_cls.singleton_class, base_cls.singleton_class.protected_instance_method(method_name) ] rescue nil
135
- end
136
- methods.compact!
137
- if methods.empty?
138
- warn "Method #{method_name} not found on #{base_cls.name}" if LOG
139
- else
140
- methods.each(&hook_method)
120
+
121
+ Array(hook.method_names).each do |method_name|
122
+ method_name = method_name.to_sym
123
+
124
+ hook_method = lambda do |entry|
125
+ cls, method = entry
126
+ next if config.never_hook?(cls, method)
127
+
128
+ hook.package.handler_class.new(hook.package, cls, method).activate
129
+ end
130
+
131
+ methods = []
132
+ # irb(main):001:0> Kernel.public_instance_method(:system)
133
+ # (irb):1:in `public_instance_method': method `system' for module `Kernel' is private (NameError)
134
+ if base_cls == Kernel
135
+ methods << [base_cls, base_cls.instance_method(method_name)] rescue nil
136
+ end
137
+ methods << [base_cls, base_cls.public_instance_method(method_name)] rescue nil
138
+ methods << [base_cls, base_cls.protected_instance_method(method_name)] rescue nil
139
+ if base_cls.respond_to?(:singleton_class)
140
+ methods << [base_cls.singleton_class, base_cls.singleton_class.public_instance_method(method_name)] rescue nil
141
+ methods << [base_cls.singleton_class, base_cls.singleton_class.protected_instance_method(method_name)] rescue nil
142
+ end
143
+ methods.compact!
144
+ if methods.empty?
145
+ HookLog.load_error [ base_cls.name, method_name ].join('[#.]'), "Method #{method_name} not found on #{base_cls.name}" if HookLog.enabled?
146
+ else
147
+ methods.each(&hook_method)
148
+ end
141
149
  end
142
150
  end
143
151
  end
@@ -145,89 +153,93 @@ module AppMap
145
153
  end
146
154
 
147
155
  hook_loaded_code.(config.builtin_hooks, true)
148
- hook_loaded_code.(config.gem_hooks, false)
149
156
  end
150
157
 
151
158
  protected
152
159
 
153
160
  def trace_location(trace_point)
154
- [ trace_point.path, trace_point.lineno ].join(':')
161
+ [trace_point.path, trace_point.lineno].join(':')
155
162
  end
156
163
 
157
164
  def trace_end(trace_point)
158
165
  location = trace_location(trace_point)
159
- warn "Class or module ends at location #{location}" if Hook::LOG || Hook::LOG_HOOK
160
166
  return unless @trace_locations.add?(location)
161
-
162
- path = trace_point.path
163
- enabled = !@notrace_paths.member?(path) && config.path_enabled?(path)
164
- unless enabled
165
- warn 'Not hooking - path is not enabled' if Hook::LOG || Hook::LOG_HOOK
166
- @notrace_paths << path
167
- return
168
- end
169
-
170
- cls = trace_point.self
171
-
172
- instance_methods = cls.public_instance_methods(false) + cls.protected_instance_methods(false) - OBJECT_INSTANCE_METHODS
173
- # NoMethodError: private method `singleton_class' called for Rack::MiniProfiler:Class
174
- class_methods = begin
175
- if cls.respond_to?(:singleton_class)
176
- cls.singleton_class.public_instance_methods(false) + cls.singleton_class.protected_instance_methods(false) - instance_methods - OBJECT_STATIC_METHODS
177
- else
178
- []
167
+ HookLog.on_load location do
168
+ path = trace_point.path
169
+ enabled = !@notrace_paths.member?(path) && config.path_enabled?(path)
170
+ unless enabled
171
+ HookLog.log 'Not hooking - path is not enabled' if HookLog.enabled?
172
+ @notrace_paths << path
173
+ next
179
174
  end
180
- rescue NameError
181
- []
182
- end
183
175
 
184
- hook = lambda do |hook_cls|
185
- lambda do |method_id|
186
- method = \
187
- begin
188
- hook_cls.instance_method(method_id)
189
- rescue NameError
190
- warn "AppMap: Method #{hook_cls} #{fn} is not accessible: #{$!}" if LOG
191
- next
176
+ cls = trace_point.self
177
+
178
+ instance_methods = cls.public_instance_methods(false) + cls.protected_instance_methods(false) - OBJECT_INSTANCE_METHODS
179
+ # NoMethodError: private method `singleton_class' called for Rack::MiniProfiler:Class
180
+ class_methods = begin
181
+ if cls.respond_to?(:singleton_class)
182
+ cls.singleton_class.public_instance_methods(false) + cls.singleton_class.protected_instance_methods(false) - instance_methods - OBJECT_STATIC_METHODS
183
+ else
184
+ []
192
185
  end
186
+ rescue NameError
187
+ []
188
+ end
193
189
 
194
- package = config.lookup_package(hook_cls, method)
195
- # doing this check first returned early in 98.7% of cases in sample_app_6th_ed
196
- next unless package
190
+ hook = lambda do |hook_cls|
191
+ lambda do |method_id|
192
+ method = begin
193
+ hook_cls.instance_method(method_id)
194
+ rescue NameError
195
+ HookLog.load_error [ hook_cls, method_id ].join('[#.]'), "Method #{hook_cls} #{method_id} is not accessible: #{$!}" if HookLog.enabled?
196
+ next
197
+ end
197
198
 
198
- # Don't try and trace the AppMap methods or there will be
199
- # a stack overflow in the defined hook method.
200
- next if %w[Marshal AppMap ActiveSupport].member?((hook_cls&.name || '').split('::')[0])
199
+ package = config.lookup_package(hook_cls, method)
200
+ # doing this check first returned early in 98.7% of cases in sample_app_6th_ed
201
+ next unless package
201
202
 
202
- next if method_id == :call
203
+ # Don't try and trace the AppMap methods or there will be
204
+ # a stack overflow in the defined hook method.
205
+ next if %w[Marshal AppMap ActiveSupport].member?((hook_cls&.name || '').split('::')[0])
203
206
 
204
- next if self.class.already_hooked?(method)
207
+ next if method_id == :call
205
208
 
206
- warn "AppMap: Examining #{hook_cls} #{method.name}" if LOG
209
+ next if self.class.already_hooked?(method)
207
210
 
208
- disasm = RubyVM::InstructionSequence.disasm(method)
209
- # Skip methods that have no instruction sequence, as they are either have no body or they are or native.
210
- # TODO: Figure out how to tell the difference?
211
- next unless disasm
211
+ HookLog.log "Examining #{hook_cls} #{method.name}" if HookLog.enabled?
212
212
 
213
- package.handler_class.new(package, hook_cls, method).activate
213
+ disasm = RubyVM::InstructionSequence.disasm(method)
214
+ # Skip methods that have no instruction sequence, as they are either have no body or they are or native.
215
+ # TODO: Figure out how to tell the difference?
216
+ next unless disasm
217
+
218
+ package.handler_class.new(package, hook_cls, method).activate
219
+ end
214
220
  end
215
- end
216
221
 
217
- start = Time.now
218
- instance_methods.each(&hook.(cls))
219
- begin
220
- # NoMethodError: private method `singleton_class' called for Rack::MiniProfiler:Class
221
- class_methods.each(&hook.(cls.singleton_class)) if cls.respond_to?(:singleton_class)
222
- rescue NameError
223
- # NameError:
224
- # uninitialized constant Faraday::Connection
225
- warn "NameError in #{__FILE__}: #{$!.message}"
226
- end
227
- elapsed = Time.now - start
228
- if location.index(Bundler.bundle_path.to_s) == 0
229
- package_tokens = location[Bundler.bundle_path.to_s.length + 1..-1].split('/')
230
- @module_load_times[package_tokens[1]] += elapsed
222
+ start = Time.now
223
+ instance_methods.each(&hook.(cls))
224
+ begin
225
+ # NoMethodError: private method `singleton_class' called for Rack::MiniProfiler:Class
226
+ class_methods.each(&hook.(cls.singleton_class)) if cls.respond_to?(:singleton_class)
227
+ rescue NameError
228
+ # NameError:
229
+ # uninitialized constant Faraday::Connection
230
+ warn "NameError in #{__FILE__}: #{$!.message}"
231
+ end
232
+ elapsed = Time.now - start
233
+ if location.index(Bundler.bundle_path.to_s) == 0
234
+ package_tokens = location[Bundler.bundle_path.to_s.length + 1..-1].split('/')
235
+ @module_load_times[package_tokens[1]] += elapsed
236
+ else
237
+ file_path = location[Dir.pwd.length + 1..-1]
238
+ if file_path
239
+ location = file_path.split('/', 3)[0..1].join('/')
240
+ @module_load_times[location] += elapsed
241
+ end
242
+ end
231
243
  end
232
244
  end
233
245
  end
@@ -0,0 +1,99 @@
1
+ module AppMap
2
+ class HookLog
3
+ LOG = (ENV['APPMAP_DEBUG'] == 'true' || ENV['DEBUG'] == 'true')
4
+ LOG_HOOK = (ENV['DEBUG_HOOK'] == 'true' || ENV['APPMAP_LOG_HOOK'] == 'true')
5
+ LOG_HOOK_FILE = (ENV['APPMAP_LOG_HOOK_FILE'] || 'appmap_hook.log')
6
+
7
+ def initialize
8
+ @file_handle = self.class.send :open_log_file
9
+ @elapsed = Hash.new { |h, k| h[k] = [] }
10
+
11
+ at_exit do
12
+ @file_handle.puts 'Elapsed time:'
13
+ @elapsed.keys.each do |k|
14
+ @file_handle.puts "#{k}:\t#{@elapsed[k].sum}"
15
+ end
16
+ @file_handle.flush
17
+ @file_handle.close
18
+ end
19
+ end
20
+
21
+ def start_time(timer)
22
+ @elapsed[timer] << [Util.gettime]
23
+ end
24
+
25
+ def end_time(timer)
26
+ unless @elapsed[timer].last.is_a?(Array)
27
+ warn "AppMap: Unbalanced timing data in hook log"
28
+ @elapsed[timer].pop
29
+ return
30
+ end
31
+
32
+ @elapsed[timer][-1] = Util.gettime - @elapsed[timer].last[0]
33
+ end
34
+
35
+ class << self
36
+ def enabled?
37
+ LOG || LOG_HOOK
38
+ end
39
+
40
+ def builtin(class_name, &block)
41
+ return yield unless enabled?
42
+
43
+ begin
44
+ log "eager\tbegin\tInitiating eager hook for #{class_name}"
45
+ @hook_log.start_time :eager
46
+
47
+ yield
48
+ ensure
49
+ @hook_log.end_time :eager
50
+ log "eager\tend\tCompleted eager hook for #{class_name}"
51
+ end
52
+ end
53
+
54
+ def on_load(location, &block)
55
+ return yield unless enabled?
56
+
57
+ begin
58
+ log "on-load\tbegin\tInitiating on-load hook for class or module defined at location #{location}"
59
+ @hook_log.start_time :on_load
60
+
61
+ yield
62
+ ensure
63
+ @hook_log.end_time :on_load
64
+ log "on-load\tend\tCompleted on-load hook for location #{location}"
65
+ end
66
+ end
67
+
68
+ def load_error(name, msg)
69
+ log "load_error\t#{name}\t#{msg}"
70
+ end
71
+
72
+ def log(msg)
73
+ unless HookLog.enabled?
74
+ warn "AppMap: HookLog is not enabled. Disregarding message #{msg}"
75
+ return
76
+ end
77
+
78
+ @hook_log ||= HookLog.new
79
+ @hook_log.log msg
80
+ end
81
+
82
+ protected def open_log_file
83
+ if LOG_HOOK_FILE == 'stderr'
84
+ $stderr
85
+ else
86
+ File.open(LOG_HOOK_FILE, 'w')
87
+ end
88
+ end
89
+ end
90
+
91
+ def log(msg)
92
+ if LOG_HOOK_FILE == 'stderr'
93
+ msg = "AppMap: #{msg}"
94
+ end
95
+ msg = "#{Util.gettime}\t#{msg}"
96
+ @file_handle.puts(msg)
97
+ end
98
+ end
99
+ end
@@ -3,7 +3,7 @@
3
3
  module AppMap
4
4
  URL = 'https://github.com/applandinc/appmap-ruby'
5
5
 
6
- VERSION = '0.94.1'
6
+ VERSION = '0.95.0'
7
7
 
8
8
  APPMAP_FORMAT_VERSION = '1.10.0'
9
9