hoss-agent 1.0.6

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 +313 -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 +102 -0
  51. data/lib/hoss/spies/http.rb +81 -0
  52. data/lib/hoss/spies/net_http.rb +97 -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 +326 -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,102 @@
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
+ Hoss.with_event do |event|
43
+ Hoss::Spies::FaradaySpy.without_net_http do
44
+ begin
45
+ uri = URI(build_url(url))
46
+ result = run_request_without_apm(method, url, body, headers) do |req|
47
+ if block_given?
48
+ yield req
49
+ new_path = req.path
50
+ new_query = URI.encode_www_form(req.params)
51
+ if uri.path != new_path || uri.query != new_query
52
+ test_uri = uri
53
+ test_uri.query = nil
54
+ begin
55
+ test_uri.path = new_path
56
+ rescue Exception => e
57
+ end
58
+ # The original url can be set to path if Faraday.get used
59
+ if test_uri.to_s != uri.to_s
60
+ uri.path = new_path
61
+ end
62
+ uri.query = new_query
63
+ end
64
+ end
65
+ event.request.method = method.to_s.upcase
66
+ event.request.url = uri.to_s
67
+ event.request.received_at = DateTime.now.strftime('%Q').to_i
68
+ event.request.headers['host'] = uri.hostname
69
+ req.headers.each {|n,v| event.request.headers[n] = v}
70
+ event.request.url = uri.to_s
71
+ event.request.body = req.body
72
+ end
73
+
74
+ if result
75
+ event.response = Hoss::Event::Response.new
76
+ event.response.received_at = DateTime.now.strftime('%Q').to_i
77
+ event.response.status_code = result.status.to_i
78
+ result.headers.each {|n,v| event.response.headers[n] = v}
79
+ event.response.body = result.body
80
+ end
81
+
82
+ result
83
+ rescue Exception => e
84
+ if e.wrapped_exception.is_a? Net::OpenTimeout
85
+ event.error = Hoss::Event::Error.new(Hoss::Event::Error::ConnectionTimeout)
86
+ else
87
+ event.error = Hoss::Event::Error.new(Hoss::Event::Error::ConnectionError)
88
+ end
89
+ event.error.received_at = DateTime.now.strftime('%Q').to_i
90
+ raise
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ # rubocop:enable Metrics/CyclomaticComplexity
98
+ end
99
+
100
+ register 'Faraday', 'faraday', FaradaySpy.new
101
+ end
102
+ end
@@ -0,0 +1,81 @@
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
+ if req.headers['HOSS-SKIP-INSTRUMENTATION'] == 'true'
45
+ return perform_without_apm(req, options)
46
+ end
47
+ Hoss.with_event do |event|
48
+ event.request.headers['host'] = req.uri.host
49
+ req.headers.each {|n,v| event.request.headers[n] = v}
50
+ event.request.method = req.verb.to_s.upcase
51
+ event.request.url = req.uri.to_s
52
+ event.request.body = Hoss::Spies::HTTPSpy::copy_request_body(req.body)
53
+ event.request.received_at = DateTime.now.strftime('%Q').to_i
54
+ begin
55
+ result = perform_without_apm(req, options)
56
+ if result
57
+ event.response = Hoss::Event::Response.new
58
+ event.response.received_at = DateTime.now.strftime('%Q').to_i
59
+ event.response.status_code = result.code.to_i
60
+ result.headers.each {|n,v| event.response.headers[n] = v}
61
+ event.response.body = result.body.dup.to_s
62
+ end
63
+ result
64
+ rescue HTTP::TimeoutError => e
65
+ event.error = Hoss::Event::Error.new(Hoss::Event::Error::ConnectionTimeout)
66
+ event.error.received_at = DateTime.now.strftime('%Q').to_i
67
+ raise
68
+ rescue Exception => e
69
+ event.error = Hoss::Event::Error.new(Hoss::Event::Error::ConnectionError)
70
+ event.error.received_at = DateTime.now.strftime('%Q').to_i
71
+ raise
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ register 'HTTP', 'http', HTTPSpy.new
80
+ end
81
+ end
@@ -0,0 +1,97 @@
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
+ if req['HOSS-SKIP-INSTRUMENTATION'] == 'true' || Hoss::Spies::NetHTTPSpy.disabled?
56
+ return request_without_apm(req, body, &block)
57
+ end
58
+
59
+ host = req['host']&.split(':')&.first || address
60
+ method = req.method.to_s.upcase
61
+ path, query = req.path.split('?')
62
+
63
+ url = use_ssl? ? +'https://' : +'http://'
64
+ url << host
65
+ url << ":#{port}" if port
66
+ url << path
67
+ url << "?#{query}" if query
68
+ uri = URI(url)
69
+
70
+ Hoss.with_event do |event|
71
+ # Record request
72
+ event.request.headers['host'] = uri.hostname
73
+ req.each_header {|n,v| event.request.headers[n] = v}
74
+ event.request.method = method
75
+ event.request.url = uri.to_s
76
+ event.request.body = req.body
77
+ event.request.received_at = DateTime.now.strftime('%Q').to_i
78
+
79
+ result = request_without_apm(req, body, &block)
80
+ if result
81
+ event.response = Hoss::Event::Response.new
82
+ event.response.received_at = DateTime.now.strftime('%Q').to_i
83
+ event.response.status_code = result.code.to_i
84
+ result.each_header {|n,v| event.response.headers[n] = v}
85
+ event.response.body = result.body
86
+ end
87
+ result
88
+ end
89
+ end
90
+ end
91
+ end
92
+ # rubocop:enable Metrics/CyclomaticComplexity
93
+ end
94
+
95
+ register 'Net::HTTP', 'net/http', NetHTTPSpy.new
96
+ end
97
+ end