appmap 0.44.0 → 0.45.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 02dfa68caaa5e5413c68ae06e89664a676624c9dbb301e2ebbc2a7ae26359afd
4
- data.tar.gz: af97e3fb4bb428a238b854ef30969f232c48d4f3a59956865a4cc95788ac35e8
3
+ metadata.gz: 0db499c7da4baea3f29fb56214a95e18047a07fc0c132ba559f7f6eb7ef9033a
4
+ data.tar.gz: 3aabf3bf1a31c39d6844ccf17807a4b7e206783bdf8e373008d3db0d04a854d7
5
5
  SHA512:
6
- metadata.gz: 9a0f2454acf5d10c48aaf1abb6e4b23d674acbbfe495770a25ff83093c38dd81cf0bb1c85dfbae4691773586ace8500c3f9b879b31749c4142851f460ed1a87c
7
- data.tar.gz: a2860fb0258b96d95e5ca98f7d13b382c86b635ce1c2d97084c1f651a55973d2ffe31e360695fdec963292081e527b96876c6b42976a90573b9201e14032d1f5
6
+ metadata.gz: a38e51f5c932d9a04b566c2664104e08a56af80fd3277c039603eb719336599bc9764781e1d0835c6c7b702e4e0465587fd4537e292df325c0d9efe4a90c1493
7
+ data.tar.gz: 168e2b8c1605cf7b4f8c4d6a5f373b5add4ce1804c4c494f3cc4e3b29da5a08f4e121889bca36e06e1e29aa4c9943c16b76f7868e9331c3a7e5b33af0817f1a0
data/.releaserc.yml ADDED
@@ -0,0 +1,11 @@
1
+ plugins:
2
+ - '@semantic-release/commit-analyzer'
3
+ - '@semantic-release/release-notes-generator'
4
+ - '@semantic-release/changelog'
5
+ - 'semantic-release-rubygem'
6
+ - - '@semantic-release/git'
7
+ - assets:
8
+ - CHANGELOG.md
9
+ - appmap.gemspec
10
+ - lib/appmap/version.rb
11
+ - '@semantic-release/github'
data/.travis.yml CHANGED
@@ -16,23 +16,29 @@ before_script:
16
16
 
17
17
  cache:
18
18
  bundler: true
19
- directories:
20
- - $HOME/docker
21
-
22
- # https://stackoverflow.com/a/41975912
23
- before_cache:
24
- # Save tagged docker images
25
- - >
26
- mkdir -p $HOME/docker && docker images -a --filter='dangling=false' --format '{{.Repository}}:{{.Tag}} {{.ID}}'
27
- | xargs -n 2 -t sh -c 'test -e $HOME/docker/$1.tar.gz || docker save $0 | gzip -2 > $HOME/docker/$1.tar.gz'
28
-
29
- before_install:
30
- # Load cached docker images
31
- - if [[ -d $HOME/docker ]]; then ls $HOME/docker/*.tar.gz | xargs -I {file} sh -c "zcat {file} | docker load"; fi
32
19
 
20
+
21
+ # GEM_ALTERNATIVE_NAME only needed for deployment
33
22
  jobs:
34
23
  include:
35
24
  - stage: test
36
25
  script:
37
26
  - mkdir tmp
38
- - bundle exec rake test
27
+ - GEM_ALTERNATIVE_NAME='' bundle exec rake test
28
+
29
+
30
+ before_deploy:
31
+ - |
32
+ nvm install --lts \
33
+ && nvm use --lts \
34
+ && npm i -g \
35
+ semantic-release \
36
+ @semantic-release/git \
37
+ @semantic-release/changelog \
38
+ semantic-release-rubygem
39
+
40
+ deploy:
41
+ - provider: script
42
+ script: ./release.sh
43
+ on:
44
+ branch: master
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [0.45.0](https://github.com/applandinc/appmap-ruby/compare/v0.44.0...v0.45.0) (2021-05-03)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Properly name status_code in HTTP server response ([556e87c](https://github.com/applandinc/appmap-ruby/commit/556e87c9a7bf214f6b8714add4f77448fd223d33))
7
+
8
+
9
+ ### Features
10
+
11
+ * Record http_client_request and http_client_response ([1db32ae](https://github.com/applandinc/appmap-ruby/commit/1db32ae0d26a7f1400b6b814d25b13368f06c158))
12
+ * Update AppMap format version to 1.5.0 ([061705e](https://github.com/applandinc/appmap-ruby/commit/061705e4619cb881e8edd022ef835183e399e127))
13
+ * **build:** add deployment via `semantic-release` with automatic publication to rubygems ([9f183de](https://github.com/applandinc/appmap-ruby/commit/9f183de13f405900000c3da979c3a8a5b6e34a24))
14
+
1
15
  # v0.44.0
2
16
 
3
17
  * Support recording and labeling of indivudal functions via `functions:` section in *appmap.yml*.
data/README_CI.md ADDED
@@ -0,0 +1,29 @@
1
+ # Configuration variables:
2
+
3
+ * `GH_TOKEN`: used by `semantic-release` to push changes to Github and manage releases
4
+ * `GEM_HOST_API_KEY`: rubygems API key
5
+ * `GEM_ALTERNATIVE_NAME` (optional): used for testing of CI flows,
6
+ to avoid publication of test releases under official package name
7
+ * `DOCKERHUB\_USERNAME`, `DOCKERHUB_PASSWORD`: optional dockerhub credentials,
8
+ to avoid throttling of dockerhub anonymous pulls
9
+
10
+ Note: for security reasons, it's better to use dedicated (not personal)
11
+ Dockerhub account,
12
+ and also use [access tokens](https://docs.docker.com/docker-hub/access-tokens/)
13
+ instead of primary password
14
+
15
+ # Release command
16
+
17
+ `./release.sh`
18
+
19
+ Bash wrapper script is used merely as a launcher of `semantic-release`
20
+ with extra logic to explicitly determine git url from `TRAVIS_REPO_SLUG` \
21
+ variable if its defined (otherwise git url is taken from `package.json`,
22
+ which breaks CI on forked repos).
23
+
24
+ # CI flow
25
+
26
+ 1. Test happens using current version number specified in `lib/appmap/version.rb`, then `release.sh` launches `semantic-release` to do the rest
27
+ 2. The version number is increased (including modicication of `version.rb`)
28
+ 3. Gem is published under new version number
29
+ 4. Github release is created with the new version number
data/Rakefile CHANGED
@@ -37,7 +37,8 @@ end
37
37
 
38
38
  def build_base_image(ruby_version)
39
39
  run_cmd "docker build" \
40
- " --build-arg RUBY_VERSION=#{ruby_version} --build-arg GEM_VERSION=#{GEM_VERSION}" \
40
+ " --build-arg RUBY_VERSION=#{ruby_version}" \
41
+ " --build-arg GEM_VERSION=#{GEM_VERSION}" \
41
42
  " -t appmap:#{GEM_VERSION} -f Dockerfile.appmap ."
42
43
  end
43
44
 
@@ -46,7 +47,7 @@ def build_app_image(app, ruby_version)
46
47
  run_cmd( {"RUBY_VERSION" => ruby_version, "GEM_VERSION" => GEM_VERSION},
47
48
  " docker-compose build" \
48
49
  " --build-arg RUBY_VERSION=#{ruby_version}" \
49
- " --build-arg GEM_VERSION=#{GEM_VERSION}")
50
+ " --build-arg GEM_VERSION=#{GEM_VERSION}" )
50
51
  end
51
52
  end
52
53
 
@@ -138,3 +139,4 @@ task spec: %i[spec:all]
138
139
  task test: %i[spec:all minitest]
139
140
 
140
141
  task default: :test
142
+
data/appmap.gemspec CHANGED
@@ -4,8 +4,12 @@ lib = File.expand_path('lib', __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
  require 'appmap/version'
6
6
 
7
+
8
+
7
9
  Gem::Specification.new do |spec|
8
- spec.name = 'appmap'
10
+ # ability to parameterize gem name is added intentionally,
11
+ # to support the possibility of unofficial releases, e.g. during CI tests
12
+ spec.name = (ENV['GEM_ALTERNATIVE_NAME'].to_s.empty? ? 'appmap' : ENV["GEM_ALTERNATIVE_NAME"] )
9
13
  spec.version = AppMap::VERSION
10
14
  spec.authors = ['Kevin Gilpin']
11
15
  spec.email = ['kgilpin@gmail.com']
data/lib/appmap.rb CHANGED
@@ -9,6 +9,7 @@ end
9
9
 
10
10
  require 'appmap/version'
11
11
  require 'appmap/hook'
12
+ require 'appmap/handler/net_http'
12
13
  require 'appmap/config'
13
14
  require 'appmap/trace'
14
15
  require 'appmap/class_map'
data/lib/appmap/config.rb CHANGED
@@ -3,6 +3,13 @@
3
3
  module AppMap
4
4
  class Config
5
5
  Package = Struct.new(:path, :gem, :package_name, :exclude, :labels, :shallow) do
6
+ attr_writer :handler_class
7
+
8
+ def handler_class
9
+ require 'appmap/handler/function'
10
+ @handler_class || AppMap::Handler::Function
11
+ end
12
+
6
13
  # Indicates that only the entry points to a package will be recorded.
7
14
  # Once the code has entered a package, subsequent calls within the package will not be
8
15
  # recorded unless the code leaves the package and re-enters it.
@@ -42,6 +49,7 @@ module AppMap
42
49
  path: path,
43
50
  package_name: package_name,
44
51
  gem: gem,
52
+ handler_class: handler_class.name,
45
53
  exclude: exclude.blank? ? nil : exclude,
46
54
  labels: labels.blank? ? nil : labels,
47
55
  shallow: shallow
@@ -107,7 +115,9 @@ module AppMap
107
115
  Hook.new(:invoke_after, Package.build_from_path('active_support', package_name: 'active_support', labels: %w[mvc.after_action])),
108
116
  ],
109
117
  'OpenSSL::X509::Certificate' => Hook.new(:sign, OPENSSL_PACKAGES.(%w[crypto.x509])),
110
- 'Net::HTTP' => Hook.new(:request, Package.build_from_path('net/http', package_name: 'net/http', labels: %w[protocol.http])),
118
+ 'Net::HTTP' => Hook.new(:request, Package.build_from_path('net/http', package_name: 'net/http', labels: %w[protocol.http]).tap do |package|
119
+ package.handler_class = AppMap::Handler::NetHTTP
120
+ end),
111
121
  'Net::SMTP' => Hook.new(:send, Package.build_from_path('net/smtp', package_name: 'net/smtp', labels: %w[protocol.email.smtp])),
112
122
  'Net::POP3' => Hook.new(:mails, Package.build_from_path('net/pop3', package_name: 'net/pop', labels: %w[protocol.email.pop])),
113
123
  'Net::IMAP' => Hook.new(:send_command, Package.build_from_path('net/imap', package_name: 'net/imap', labels: %w[protocol.email.imap])),
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'appmap/event'
4
+
5
+ module AppMap
6
+ module Handler
7
+ module Function
8
+ class << self
9
+ def handle_call(defined_class, hook_method, receiver, args)
10
+ AppMap::Event::MethodCall.build_from_invocation(defined_class, hook_method, receiver, args)
11
+ end
12
+
13
+ def handle_return(call_event_id, elapsed, return_value, exception)
14
+ AppMap::Event::MethodReturn.build_from_invocation(call_event_id, elapsed, return_value, exception)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'appmap/event'
4
+
5
+ module AppMap
6
+ module Handler
7
+ class HTTPClientRequest < AppMap::Event::MethodEvent
8
+ attr_accessor :request_method, :url, :params, :headers
9
+
10
+ def initialize(http, request)
11
+ super AppMap::Event.next_id_counter, :call, Thread.current.object_id
12
+
13
+ path, query = request.path.split('?')
14
+ query ||= ''
15
+
16
+ protocol = http.use_ssl? ? 'https' : 'http'
17
+ port = if http.use_ssl? && http.port == 443
18
+ nil
19
+ elsif !http.use_ssl? && http.port == 80
20
+ nil
21
+ else
22
+ ":#{http.port}"
23
+ end
24
+
25
+ url = [ protocol, '://', http.address, port, path ].compact.join
26
+
27
+ self.request_method = request.method
28
+ self.url = url
29
+ self.headers = AppMap::Util.select_headers(NetHTTP.request_headers(request))
30
+ self.params = Rack::Utils.parse_nested_query(query)
31
+ end
32
+
33
+ def to_h
34
+ super.tap do |h|
35
+ h[:http_client_request] = {
36
+ request_method: request_method,
37
+ url: url,
38
+ headers: headers
39
+ }.compact
40
+
41
+ unless params.blank?
42
+ h[:message] = params.keys.map do |key|
43
+ val = params[key]
44
+ {
45
+ name: key,
46
+ class: val.class.name,
47
+ value: self.class.display_string(val),
48
+ object_id: val.__id__,
49
+ }
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ class HTTPClientResponse < AppMap::Event::MethodReturnIgnoreValue
57
+ attr_accessor :status, :mime_type, :headers
58
+
59
+ def initialize(response, parent_id, elapsed)
60
+ super AppMap::Event.next_id_counter, :return, Thread.current.object_id
61
+
62
+ self.status = response.code.to_i
63
+ self.parent_id = parent_id
64
+ self.elapsed = elapsed
65
+ self.headers = AppMap::Util.select_headers(NetHTTP.response_headers(response))
66
+ end
67
+
68
+ def to_h
69
+ super.tap do |h|
70
+ h[:http_client_response] = {
71
+ status_code: status,
72
+ mime_type: mime_type,
73
+ headers: headers
74
+ }.compact
75
+ end
76
+ end
77
+ end
78
+
79
+ class NetHTTP
80
+ class << self
81
+ def request_headers(request)
82
+ {}.tap do |headers|
83
+ request.each_header do |k,v|
84
+ key = [ 'HTTP', k.underscore.upcase ].join('_')
85
+ headers[key] = v
86
+ end
87
+ end
88
+ end
89
+
90
+ alias response_headers request_headers
91
+
92
+ def handle_call(defined_class, hook_method, receiver, args)
93
+ # request will call itself again in a start block if it's not already started.
94
+ return unless receiver.started?
95
+
96
+ http = receiver
97
+ request = args.first
98
+ HTTPClientRequest.new(http, request)
99
+ end
100
+
101
+ def handle_return(call_event_id, elapsed, return_value, exception)
102
+ HTTPClientResponse.new(return_value, call_event_id, elapsed)
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -76,7 +76,7 @@ module AppMap
76
76
  raise
77
77
  ensure
78
78
  with_disabled_hook.call do
79
- after_hook.call(self, call_event, start_time, return_value, exception)
79
+ after_hook.call(self, call_event, start_time, return_value, exception) if call_event
80
80
  end
81
81
  end
82
82
  end
@@ -87,18 +87,16 @@ module AppMap
87
87
  protected
88
88
 
89
89
  def before_hook(receiver, defined_class, args)
90
- require 'appmap/event'
91
- call_event = AppMap::Event::MethodCall.build_from_invocation(defined_class, hook_method, receiver, args)
92
- AppMap.tracing.record_event call_event, package: hook_package, defined_class: defined_class, method: hook_method
90
+ call_event = hook_package.handler_class.handle_call(defined_class, hook_method, receiver, args)
91
+ AppMap.tracing.record_event(call_event, package: hook_package, defined_class: defined_class, method: hook_method) if call_event
93
92
  [ call_event, TIME_NOW.call ]
94
93
  end
95
94
 
96
95
  def after_hook(_receiver, call_event, start_time, return_value, exception)
97
- require 'appmap/event'
98
96
  elapsed = TIME_NOW.call - start_time
99
- return_event = \
100
- AppMap::Event::MethodReturn.build_from_invocation call_event.id, elapsed, return_value, exception
97
+ return_event = hook_package.handler_class.handle_return(call_event.id, elapsed, return_value, exception)
101
98
  AppMap.tracing.record_event return_event
99
+ nil
102
100
  end
103
101
 
104
102
  def with_disabled_hook(&function)
@@ -6,26 +6,6 @@ require 'appmap/hook'
6
6
  module AppMap
7
7
  module Rails
8
8
  module RequestHandler
9
- # Host and User-Agent will just introduce needless variation.
10
- # Content-Type and Authorization get their own fields in the request.
11
- IGNORE_HEADERS = %w[host user_agent content_type authorization].map(&:upcase).map {|h| "HTTP_#{h}"}.freeze
12
-
13
- class << self
14
- def selected_headers(env)
15
- # Rack prepends HTTP_ to all client-sent headers.
16
- matching_headers = env
17
- .select { |k,v| k.start_with? 'HTTP_'}
18
- .reject { |k,v| IGNORE_HEADERS.member?(k) }
19
- .reject { |k,v| v.blank? }
20
- .each_with_object({}) do |kv, memo|
21
- key = kv[0].sub(/^HTTP_/, '').split('_').map(&:capitalize).join('-')
22
- value = kv[1]
23
- memo[key] = value
24
- end
25
- matching_headers.blank? ? nil : matching_headers
26
- end
27
- end
28
-
29
9
  class HTTPServerRequest < AppMap::Event::MethodEvent
30
10
  attr_accessor :normalized_path_info, :request_method, :path_info, :params, :mime_type, :headers, :authorization
31
11
 
@@ -35,7 +15,7 @@ module AppMap
35
15
  self.request_method = request.request_method
36
16
  self.normalized_path_info = normalized_path(request)
37
17
  self.mime_type = request.headers['Content-Type']
38
- self.headers = RequestHandler.selected_headers(request.env)
18
+ self.headers = AppMap::Util.select_headers(request.env)
39
19
  self.authorization = request.headers['Authorization']
40
20
  self.path_info = request.path_info.split('?')[0]
41
21
  # ActionDispatch::Http::ParameterFilter is deprecated
@@ -59,16 +39,18 @@ module AppMap
59
39
  headers: headers,
60
40
  }.compact
61
41
 
62
- h[:message] = params.keys.map do |key|
63
- val = params[key]
64
- {
65
- name: key,
66
- class: val.class.name,
67
- value: self.class.display_string(val),
68
- object_id: val.__id__,
69
- }.tap do |message|
70
- properties = object_properties(val)
71
- message[:properties] = properties if properties
42
+ unless params.blank?
43
+ h[:message] = params.keys.map do |key|
44
+ val = params[key]
45
+ {
46
+ name: key,
47
+ class: val.class.name,
48
+ value: self.class.display_string(val),
49
+ object_id: val.__id__,
50
+ }.tap do |message|
51
+ properties = object_properties(val)
52
+ message[:properties] = properties if properties
53
+ end
72
54
  end
73
55
  end
74
56
  end
@@ -97,13 +79,13 @@ module AppMap
97
79
  self.mime_type = response.headers['Content-Type']
98
80
  self.parent_id = parent_id
99
81
  self.elapsed = elapsed
100
- self.headers = RequestHandler.selected_headers(response.headers)
82
+ self.headers = AppMap::Util.select_headers(response.headers)
101
83
  end
102
84
 
103
85
  def to_h
104
86
  super.tap do |h|
105
87
  h[:http_server_response] = {
106
- status: status,
88
+ status_code: status,
107
89
  mime_type: mime_type,
108
90
  headers: headers
109
91
  }.compact
data/lib/appmap/trace.rb CHANGED
@@ -82,7 +82,8 @@ module AppMap
82
82
 
83
83
  @last_package_for_thread[Thread.current.object_id] = package if package
84
84
  @events << event
85
- @methods << Trace::ScopedMethod.new(package, defined_class, method, event.static) \
85
+ static = event.static if event.respond_to?(:static)
86
+ @methods << Trace::ScopedMethod.new(package, defined_class, method, static) \
86
87
  if package && defined_class && method && (event.event == :call)
87
88
  end
88
89