hoss-agent 1.0.11

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 (77) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/Bug_report.md +40 -0
  3. data/.github/ISSUE_TEMPLATE/Feature_request.md +17 -0
  4. data/.github/PULL_REQUEST_TEMPLATE.md +60 -0
  5. data/.gitignore +27 -0
  6. data/.rspec +2 -0
  7. data/Dockerfile +43 -0
  8. data/Gemfile +105 -0
  9. data/LICENSE +201 -0
  10. data/hoss-agent.gemspec +42 -0
  11. data/lib/hoss-agent.rb +210 -0
  12. data/lib/hoss.rb +21 -0
  13. data/lib/hoss/agent.rb +235 -0
  14. data/lib/hoss/central_config.rb +184 -0
  15. data/lib/hoss/central_config/cache_control.rb +51 -0
  16. data/lib/hoss/child_durations.rb +64 -0
  17. data/lib/hoss/config.rb +315 -0
  18. data/lib/hoss/config/bytes.rb +42 -0
  19. data/lib/hoss/config/duration.rb +40 -0
  20. data/lib/hoss/config/options.rb +154 -0
  21. data/lib/hoss/config/regexp_list.rb +30 -0
  22. data/lib/hoss/config/wildcard_pattern_list.rb +54 -0
  23. data/lib/hoss/context.rb +64 -0
  24. data/lib/hoss/context/request.rb +28 -0
  25. data/lib/hoss/context/request/socket.rb +36 -0
  26. data/lib/hoss/context/request/url.rb +59 -0
  27. data/lib/hoss/context/response.rb +47 -0
  28. data/lib/hoss/context/user.rb +59 -0
  29. data/lib/hoss/context_builder.rb +112 -0
  30. data/lib/hoss/deprecations.rb +39 -0
  31. data/lib/hoss/error.rb +49 -0
  32. data/lib/hoss/error/exception.rb +70 -0
  33. data/lib/hoss/error/log.rb +41 -0
  34. data/lib/hoss/error_builder.rb +90 -0
  35. data/lib/hoss/event.rb +131 -0
  36. data/lib/hoss/instrumenter.rb +107 -0
  37. data/lib/hoss/internal_error.rb +23 -0
  38. data/lib/hoss/logging.rb +70 -0
  39. data/lib/hoss/metadata.rb +36 -0
  40. data/lib/hoss/metadata/process_info.rb +35 -0
  41. data/lib/hoss/metadata/service_info.rb +76 -0
  42. data/lib/hoss/metadata/system_info.rb +47 -0
  43. data/lib/hoss/metadata/system_info/container_info.rb +136 -0
  44. data/lib/hoss/naively_hashable.rb +38 -0
  45. data/lib/hoss/rails.rb +68 -0
  46. data/lib/hoss/railtie.rb +42 -0
  47. data/lib/hoss/report.rb +9 -0
  48. data/lib/hoss/sinatra.rb +53 -0
  49. data/lib/hoss/spies.rb +104 -0
  50. data/lib/hoss/spies/faraday.rb +117 -0
  51. data/lib/hoss/spies/http.rb +93 -0
  52. data/lib/hoss/spies/net_http.rb +113 -0
  53. data/lib/hoss/stacktrace.rb +33 -0
  54. data/lib/hoss/stacktrace/frame.rb +66 -0
  55. data/lib/hoss/stacktrace_builder.rb +124 -0
  56. data/lib/hoss/transport/base.rb +191 -0
  57. data/lib/hoss/transport/connection.rb +55 -0
  58. data/lib/hoss/transport/connection/http.rb +139 -0
  59. data/lib/hoss/transport/connection/proxy_pipe.rb +94 -0
  60. data/lib/hoss/transport/filters.rb +60 -0
  61. data/lib/hoss/transport/filters/hash_sanitizer.rb +77 -0
  62. data/lib/hoss/transport/filters/secrets_filter.rb +48 -0
  63. data/lib/hoss/transport/headers.rb +74 -0
  64. data/lib/hoss/transport/serializers.rb +113 -0
  65. data/lib/hoss/transport/serializers/context_serializer.rb +112 -0
  66. data/lib/hoss/transport/serializers/error_serializer.rb +92 -0
  67. data/lib/hoss/transport/serializers/event_serializer.rb +73 -0
  68. data/lib/hoss/transport/serializers/metadata_serializer.rb +92 -0
  69. data/lib/hoss/transport/serializers/report_serializer.rb +33 -0
  70. data/lib/hoss/transport/user_agent.rb +48 -0
  71. data/lib/hoss/transport/worker.rb +330 -0
  72. data/lib/hoss/util.rb +54 -0
  73. data/lib/hoss/util/inflector.rb +110 -0
  74. data/lib/hoss/util/lru_cache.rb +65 -0
  75. data/lib/hoss/util/throttle.rb +52 -0
  76. data/lib/hoss/version.rb +22 -0
  77. metadata +147 -0
@@ -0,0 +1,9 @@
1
+ module Hoss
2
+ # @api private
3
+ class Report
4
+ extend Forwardable
5
+ attr_accessor(
6
+ :events
7
+ )
8
+ end
9
+ end
@@ -0,0 +1,53 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ # frozen_string_literal: true
19
+
20
+ module Hoss
21
+ # Module for starting the Hoss agent and hooking into Sinatra.
22
+ module Sinatra
23
+ extend self
24
+ # Start the Hoss agent and hook into Sinatra.
25
+ #
26
+ # @param app [Sinatra::Base] A Sinatra app.
27
+ # @param config [Config, Hash] An instance of Config or a Hash config.
28
+ # @return [true, nil] true if the agent was started, nil otherwise.
29
+ def start(app, config = {})
30
+ config = Config.new(config) unless config.is_a?(Config)
31
+ configure_app(app, config)
32
+
33
+ Hoss.start(config)
34
+ Hoss.running?
35
+ rescue StandardError => e
36
+ config.logger.error format('Failed to start: %s', e.message)
37
+ config.logger.debug "Backtrace:\n" + e.backtrace.join("\n")
38
+ end
39
+
40
+ private
41
+
42
+ def configure_app(app, config)
43
+ config.service_name ||= format_name(app.to_s)
44
+ config.framework_name ||= 'Sinatra'
45
+ config.framework_version ||= ::Sinatra::VERSION
46
+ config.__root_path ||= Dir.pwd
47
+ end
48
+
49
+ def format_name(str)
50
+ str&.gsub('::', '_')
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,104 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ # frozen_string_literal: true
19
+
20
+ require 'hoss/util/inflector'
21
+
22
+ module Hoss
23
+ # @api private
24
+ module Spies
25
+ # @api private
26
+ class Registration
27
+ extend Forwardable
28
+
29
+ def initialize(const_name, require_paths, spy)
30
+ @const_name = const_name
31
+ @require_paths = Array(require_paths)
32
+ @spy = spy
33
+ end
34
+
35
+ attr_reader :const_name, :require_paths
36
+
37
+ def_delegator :@spy, :install
38
+ end
39
+
40
+ def self.require_hooks
41
+ @require_hooks ||= {}
42
+ end
43
+
44
+ def self.installed
45
+ @installed ||= {}
46
+ end
47
+
48
+ def self.register(*args)
49
+ registration = Registration.new(*args)
50
+
51
+ if safe_defined?(registration.const_name)
52
+ registration.install
53
+ installed[registration.const_name] = registration
54
+ else
55
+ register_require_hook registration
56
+ end
57
+ end
58
+
59
+ def self.register_require_hook(registration)
60
+ registration.require_paths.each do |path|
61
+ require_hooks[path] = registration
62
+ end
63
+ end
64
+
65
+ def self.hook_into(name)
66
+ return unless (registration = require_hooks[name])
67
+ return unless safe_defined?(registration.const_name)
68
+
69
+ installed[registration.const_name] = registration
70
+ registration.install
71
+
72
+ registration.require_paths.each do |path|
73
+ require_hooks.delete path
74
+ end
75
+ end
76
+
77
+ def self.safe_defined?(const_name)
78
+ Util::Inflector.safe_constantize(const_name)
79
+ end
80
+ end
81
+ end
82
+
83
+ unless ENV['HOSS_SKIP_REQUIRE_PATCH'] == '1'
84
+ # @api private
85
+ module Kernel
86
+ private
87
+
88
+ alias require_without_apm require
89
+
90
+ def require(path)
91
+ res = require_without_apm(path)
92
+
93
+ begin
94
+ Hoss::Spies.hook_into(path)
95
+ rescue ::Exception => e
96
+ puts "Failed hooking into '#{path}'. Please report this at " \
97
+ 'github.com/elastic/apm-agent-ruby'
98
+ puts e.backtrace.join("\n")
99
+ end
100
+
101
+ res
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,117 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ # frozen_string_literal: true
19
+
20
+ module Hoss
21
+ # @api private
22
+ module Spies
23
+ # @api private
24
+ class FaradaySpy
25
+ TYPE = 'ext'
26
+ SUBTYPE = 'faraday'
27
+
28
+ def self.without_net_http
29
+ return yield unless defined?(NetHTTPSpy)
30
+
31
+ Hoss::Spies::NetHTTPSpy.disable_in do
32
+ yield
33
+ end
34
+ end
35
+
36
+ # rubocop:disable Metrics/CyclomaticComplexity
37
+ def install
38
+ ::Faraday::Connection.class_eval do
39
+ alias run_request_without_apm run_request
40
+
41
+ def run_request(method, url, body, headers, &block)
42
+ result = nil
43
+ error_in_request = false
44
+ error_in_block = false
45
+ begin
46
+ Hoss.with_event do |event|
47
+ Hoss::Spies::FaradaySpy.without_net_http do
48
+ uri = URI(build_url(url))
49
+ begin
50
+ result = run_request_without_apm(method, url, body, headers) do |req|
51
+ if block_given?
52
+ yield req
53
+ begin
54
+ new_path = req.path
55
+ new_query = URI.encode_www_form(req.params)
56
+ if uri.path != new_path || uri.query != new_query
57
+ test_uri = uri
58
+ test_uri.query = nil
59
+ begin
60
+ test_uri.path = new_path
61
+ rescue Exception => e
62
+ end
63
+ # The original url can be set to path if Faraday.get used
64
+ if test_uri.to_s != uri.to_s
65
+ uri.path = new_path
66
+ end
67
+ uri.query = new_query
68
+ end
69
+ rescue
70
+ error_in_block = true
71
+ raise
72
+ end
73
+ end
74
+ begin
75
+ event.request.method = method.to_s.upcase
76
+ event.request.url = uri.to_s
77
+ event.request.received_at = DateTime.now.strftime('%Q').to_i
78
+ event.request.headers['host'] = uri.hostname
79
+ req.headers.each {|n,v| event.request.headers[n] = v}
80
+ event.request.url = uri.to_s
81
+ event.request.body = req.body
82
+ rescue
83
+ error_in_block = true
84
+ raise
85
+ end
86
+ end
87
+ rescue
88
+ error_in_request = true
89
+ event.error = Hoss::Event::Error.new(Hoss::Event::Error::ConnectionError)
90
+ event.error.received_at = DateTime.now.strftime('%Q').to_i
91
+ raise
92
+ end
93
+ if result
94
+ event.response = Hoss::Event::Response.new
95
+ event.response.received_at = DateTime.now.strftime('%Q').to_i
96
+ event.response.status_code = result.status.to_i
97
+ result.headers.each {|n,v| event.response.headers[n] = v}
98
+ event.response.body = result.body
99
+ end
100
+ result
101
+ end
102
+ end
103
+ rescue Exception => e
104
+ raise if error_in_request && !error_in_block
105
+ puts format('Hoss Error: %s %s', e.inspect, e.backtrace)
106
+ return result unless result.nil?
107
+ return run_request_without_apm(method, url, body, headers, &block)
108
+ end
109
+ end
110
+ end
111
+ end
112
+ # rubocop:enable Metrics/CyclomaticComplexity
113
+ end
114
+
115
+ register 'Faraday', 'faraday', FaradaySpy.new
116
+ end
117
+ end
@@ -0,0 +1,93 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ # frozen_string_literal: true
19
+
20
+ module Hoss
21
+ # @api private
22
+ module Spies
23
+ # @api private
24
+ class HTTPSpy
25
+ TYPE = 'ext'
26
+ SUBTYPE = 'http_rb'
27
+
28
+ def self.copy_request_body(body)
29
+ case body.source
30
+ when String
31
+ body.source
32
+ when nil
33
+ nil
34
+ else
35
+ ""
36
+ end
37
+ end
38
+
39
+ def install
40
+ ::HTTP::Client.class_eval do
41
+ alias perform_without_apm perform
42
+
43
+ def perform(req, options)
44
+ result = nil
45
+ error_in_request = false
46
+ begin
47
+ if req.headers['HOSS-SKIP-INSTRUMENTATION'] == 'true'
48
+ return perform_without_apm(req, options)
49
+ end
50
+
51
+ Hoss.with_event do |event|
52
+ event.request.headers['host'] = req.uri.host
53
+ req.headers.each {|n,v| event.request.headers[n] = v}
54
+ event.request.method = req.verb.to_s.upcase
55
+ event.request.url = req.uri.to_s
56
+ event.request.body = Hoss::Spies::HTTPSpy::copy_request_body(req.body)
57
+ event.request.received_at = DateTime.now.strftime('%Q').to_i
58
+ begin
59
+ result = perform_without_apm(req, options)
60
+ rescue HTTP::TimeoutError => e
61
+ error_in_request = true
62
+ event.error = Hoss::Event::Error.new(Hoss::Event::Error::ConnectionTimeout)
63
+ event.error.received_at = DateTime.now.strftime('%Q').to_i
64
+ raise
65
+ rescue Exception => e
66
+ error_in_request = true
67
+ event.error = Hoss::Event::Error.new(Hoss::Event::Error::ConnectionError)
68
+ event.error.received_at = DateTime.now.strftime('%Q').to_i
69
+ raise
70
+ end
71
+ if result
72
+ event.response = Hoss::Event::Response.new
73
+ event.response.received_at = DateTime.now.strftime('%Q').to_i
74
+ event.response.status_code = result.code.to_i
75
+ result.headers.each {|n,v| event.response.headers[n] = v}
76
+ event.response.body = result.body.to_s
77
+ end
78
+ result
79
+ end
80
+ rescue Exception => e
81
+ raise if error_in_request
82
+ puts format('Hoss Error: %s %s', e.inspect, e.backtrace)
83
+ return result unless result.nil?
84
+ return perform_without_apm(req, options)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ register 'HTTP', 'http', HTTPSpy.new
92
+ end
93
+ end
@@ -0,0 +1,113 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ # frozen_string_literal: true
19
+
20
+ module Hoss
21
+ # @api private
22
+ module Spies
23
+ # @api private
24
+ class NetHTTPSpy
25
+ KEY = :__hoss_net_http_disabled
26
+ # TYPE = 'ext'
27
+ # SUBTYPE = 'net_http'
28
+
29
+ class << self
30
+ def disabled=(disabled)
31
+ Thread.current[KEY] = disabled
32
+ end
33
+
34
+ def disabled?
35
+ Thread.current[KEY] ||= false
36
+ end
37
+
38
+ def disable_in
39
+ self.disabled = true
40
+
41
+ begin
42
+ yield
43
+ ensure
44
+ self.disabled = false
45
+ end
46
+ end
47
+ end
48
+
49
+ # rubocop:disable Metrics/CyclomaticComplexity
50
+ def install
51
+ Net::HTTP.class_eval do
52
+ alias request_without_apm request
53
+
54
+ def request(req, body = nil, &block)
55
+ result = nil
56
+ error_in_request = false
57
+ begin
58
+ if req['HOSS-SKIP-INSTRUMENTATION'] == 'true' || Hoss::Spies::NetHTTPSpy.disabled?
59
+ return request_without_apm(req, body, &block)
60
+ end
61
+
62
+ host = req['host']&.split(':')&.first || address
63
+ method = req.method.to_s.upcase
64
+ path, query = req.path.split('?')
65
+
66
+ url = use_ssl? ? +'https://' : +'http://'
67
+ url << host
68
+ url << ":#{port}" if port
69
+ url << path
70
+ url << "?#{query}" if query
71
+ uri = URI(url)
72
+
73
+ Hoss.with_event do |event|
74
+ # Record request
75
+ event.request.headers['host'] = uri.hostname
76
+ req.each_header {|n,v| event.request.headers[n] = v}
77
+ event.request.method = method
78
+ event.request.url = uri.to_s
79
+ event.request.body = req.body
80
+ event.request.received_at = DateTime.now.strftime('%Q').to_i
81
+
82
+ begin
83
+ result = request_without_apm(req, body, &block)
84
+ rescue
85
+ error_in_request = true
86
+ raise
87
+ end
88
+
89
+ if result
90
+ event.response = Hoss::Event::Response.new
91
+ event.response.received_at = DateTime.now.strftime('%Q').to_i
92
+ event.response.status_code = result.code.to_i
93
+ result.each_header {|n,v| event.response.headers[n] = v}
94
+ event.response.body = result.body
95
+ end
96
+
97
+ result
98
+ end
99
+ rescue Exception => e
100
+ raise if error_in_request
101
+ puts format('Hoss Error: %s %s', e.inspect, e.backtrace)
102
+ return result unless result.nil?
103
+ return request_without_apm(req, body, &block)
104
+ end
105
+ end
106
+ end
107
+ end
108
+ # rubocop:enable Metrics/CyclomaticComplexity
109
+ end
110
+
111
+ register 'Net::HTTP', 'net/http', NetHTTPSpy.new
112
+ end
113
+ end