hoss-agent 1.0.11

Sign up to get free protection for your applications and to get access to all the features.
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