hoss-agent 1.0.7

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 +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 +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,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.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