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,35 @@
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
+ class Metadata
22
+ # @api private
23
+ class ProcessInfo
24
+ def initialize(config)
25
+ @config = config
26
+
27
+ @argv = ARGV
28
+ @pid = $PID || Process.pid
29
+ @title = $PROGRAM_NAME
30
+ end
31
+
32
+ attr_reader :argv, :pid, :title
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,76 @@
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
+ class Metadata
22
+ # @api private
23
+ class ServiceInfo
24
+ # @api private
25
+ class Versioned
26
+ def initialize(name: nil, version: nil)
27
+ @name = name
28
+ @version = version
29
+ end
30
+
31
+ attr_reader :name, :version
32
+ end
33
+
34
+ class Agent < Versioned; end
35
+ class Framework < Versioned; end
36
+ class Language < Versioned; end
37
+ class Runtime < Versioned; end
38
+
39
+ def initialize(config)
40
+ @config = config
41
+
42
+ @name = @config.service_name
43
+ @node_name = @config.service_node_name
44
+ @environment = @config.environment
45
+ @agent = Agent.new(name: 'ruby', version: VERSION)
46
+ @framework = Framework.new(
47
+ name: @config.framework_name,
48
+ version: @config.framework_version
49
+ )
50
+ @language = Language.new(name: 'ruby', version: RUBY_VERSION)
51
+ @runtime = lookup_runtime
52
+ @version = @config.service_version || Util.git_sha
53
+ end
54
+
55
+ attr_reader :name, :node_name, :environment, :agent, :framework, :language,
56
+ :runtime, :version
57
+
58
+ private
59
+
60
+ def lookup_runtime
61
+ case RUBY_ENGINE
62
+ when 'ruby'
63
+ Runtime.new(
64
+ name: RUBY_ENGINE,
65
+ version: RUBY_VERSION || RUBY_ENGINE_VERSION
66
+ )
67
+ when 'jruby'
68
+ Runtime.new(
69
+ name: RUBY_ENGINE,
70
+ version: JRUBY_VERSION || RUBY_ENGINE_VERSION
71
+ )
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,47 @@
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
+ class Metadata
22
+ # @api private
23
+ class SystemInfo
24
+ def initialize(config)
25
+ @config = config
26
+
27
+ @hostname = @config.hostname || `hostname`.chomp
28
+ @architecture = gem_platform.cpu
29
+ @platform = gem_platform.os
30
+
31
+ container_info = ContainerInfo.read!
32
+ @container = container_info.container
33
+ @kubernetes = container_info.kubernetes
34
+ end
35
+
36
+ attr_reader :hostname, :architecture, :platform, :container, :kubernetes
37
+
38
+ private
39
+
40
+ def gem_platform
41
+ @gem_platform ||= Gem::Platform.local
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ require 'hoss/metadata/system_info/container_info'
@@ -0,0 +1,136 @@
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
+ class Metadata
22
+ class SystemInfo
23
+ # @api private
24
+ class ContainerInfo
25
+ CGROUP_PATH = '/proc/self/cgroup'
26
+
27
+ attr_accessor :container_id, :kubernetes_namespace,
28
+ :kubernetes_node_name, :kubernetes_pod_name, :kubernetes_pod_uid
29
+
30
+ def initialize(cgroup_path: CGROUP_PATH)
31
+ @cgroup_path = cgroup_path
32
+ end
33
+
34
+ attr_reader :cgroup_path
35
+
36
+ def read!
37
+ read_from_cgroup!
38
+ read_from_env!
39
+ self
40
+ end
41
+
42
+ def self.read!
43
+ new.read!
44
+ end
45
+
46
+ def container
47
+ @container ||=
48
+ begin
49
+ return unless container_id
50
+ { id: container_id }
51
+ end
52
+ end
53
+
54
+ def kubernetes
55
+ @kubernetes =
56
+ begin
57
+ kubernetes = {
58
+ namespace: kubernetes_namespace,
59
+ node: { name: kubernetes_node_name },
60
+ pod: {
61
+ name: kubernetes_pod_name,
62
+ uid: kubernetes_pod_uid
63
+ }
64
+ }
65
+ return nil if kubernetes.values.all?(&:nil?)
66
+
67
+ kubernetes
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def read_from_env!
74
+ self.kubernetes_namespace =
75
+ ENV.fetch('KUBERNETES_NAMESPACE', kubernetes_namespace)
76
+ self.kubernetes_node_name =
77
+ ENV.fetch('KUBERNETES_NODE_NAME', kubernetes_node_name)
78
+ self.kubernetes_pod_name =
79
+ ENV.fetch('KUBERNETES_POD_NAME', kubernetes_pod_name)
80
+ self.kubernetes_pod_uid =
81
+ ENV.fetch('KUBERNETES_POD_UID', kubernetes_pod_uid)
82
+ end
83
+
84
+ CONTAINER_ID_REGEX = /^[0-9A-Fa-f]{64}$/.freeze
85
+ KUBEPODS_REGEX = %r{(?:^/kubepods/[^/]+/pod([^/]+)$)|(?:^/kubepods\.slice/kubepods-[^/]+\.slice/kubepods-[^/]+-pod([^/]+)\.slice$)}.freeze # rubocop:disable Metrics/LineLength
86
+ SYSTEMD_SCOPE_SUFFIX = '.scope'
87
+
88
+ # rubocop:disable Metrics/PerceivedComplexity
89
+ # rubocop:disable Metrics/CyclomaticComplexity
90
+ def read_from_cgroup!
91
+ return unless File.exist?(cgroup_path)
92
+ IO.readlines(cgroup_path).each do |line|
93
+ parts = line.strip.split(':')
94
+ next if parts.length != 3
95
+
96
+ cgroup_path = parts[2]
97
+
98
+ # Depending on the filesystem driver used for cgroup
99
+ # management, the paths in /proc/pid/cgroup will have
100
+ # one of the following formats in a Docker container:
101
+ #
102
+ # systemd: /system.slice/docker-<container-ID>.scope
103
+ # cgroupfs: /docker/<container-ID>
104
+ #
105
+ # In a Kubernetes pod, the cgroup path will look like:
106
+ #
107
+ # systemd:
108
+ # /kubepods.slice/kubepods-<QoS-class>.slice/kubepods-\
109
+ # <QoS-class>-pod<pod-UID>.slice/<container-iD>.scope
110
+ # cgroupfs:
111
+ # /kubepods/<QoS-class>/pod<pod-UID>/<container-iD>
112
+ directory, container_id = File.split(cgroup_path)
113
+
114
+ if container_id.end_with?(SYSTEMD_SCOPE_SUFFIX)
115
+ container_id = container_id[0...-SYSTEMD_SCOPE_SUFFIX.length]
116
+ if container_id.include?('-')
117
+ container_id = container_id.split('-', 2)[1]
118
+ end
119
+ end
120
+
121
+ if (kubepods_match = KUBEPODS_REGEX.match(directory))
122
+ pod_id = kubepods_match[1] || kubepods_match[2]
123
+
124
+ self.container_id = container_id
125
+ self.kubernetes_pod_uid = pod_id
126
+ elsif CONTAINER_ID_REGEX.match(container_id)
127
+ self.container_id = container_id
128
+ end
129
+ end
130
+ end
131
+ # rubocop:enable Metrics/PerceivedComplexity
132
+ # rubocop:enable Metrics/CyclomaticComplexity
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,38 @@
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 NaivelyHashable
23
+ def naively_hashable?
24
+ true
25
+ end
26
+
27
+ def to_h
28
+ instance_variables.each_with_object({}) do |name, h|
29
+ key = name.to_s.delete('@').to_sym
30
+ value = instance_variable_get(name)
31
+ is_hashable =
32
+ value.respond_to?(:naively_hashable?) && value.naively_hashable?
33
+
34
+ h[key] = is_hashable ? value.to_h : value
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,68 @@
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/railtie'
21
+
22
+ module Hoss
23
+ # Module for explicitly starting the Hoss agent and hooking into Rails.
24
+ # It is recommended to use the Railtie instead.
25
+ module Rails
26
+ extend self
27
+ # Start the Hoss agent and hook into Rails.
28
+ # Note that the agent won't be started if the Rails console is being used.
29
+ #
30
+ # @param config [Config, Hash] An instance of Config or a Hash config.
31
+ # @return [true, nil] true if the agent was started, nil otherwise.
32
+ def start(config)
33
+ config = Config.new(config) unless config.is_a?(Config)
34
+
35
+ if (reason = should_skip?(config))
36
+ unless config.disable_start_message?
37
+ config.logger.info "Skipping because: #{reason}. " \
38
+ "Start manually with `Hoss.start'"
39
+ end
40
+
41
+ return
42
+ end
43
+
44
+ Hoss.start(config).tap do |agent|
45
+ end
46
+
47
+ Hoss.running?
48
+ rescue StandardError => e
49
+ if config.disable_start_message?
50
+ config.logger.error format('Failed to start: %s', e.message)
51
+ config.logger.debug "Backtrace:\n" + e.backtrace.join("\n")
52
+ else
53
+ puts format('Failed to start: %s', e.message)
54
+ puts "Backtrace:\n" + e.backtrace.join("\n")
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def should_skip?(_config)
61
+ if ::Rails.const_defined?('Console', false)
62
+ return 'Rails console'
63
+ end
64
+
65
+ nil
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,42 @@
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
+ class Railtie < ::Rails::Railtie
23
+ config.hoss = ActiveSupport::OrderedOptions.new
24
+
25
+ Config.schema.each do |key, args|
26
+ next unless args.length > 1
27
+ config.hoss[key] = args[:default]
28
+ end
29
+
30
+ initializer 'hoss.initialize' do |app|
31
+ config = Config.new(app.config.hoss.merge(app: app)).tap do |c|
32
+ # Prepend Rails.root to log_path if present
33
+ if c.log_path && !c.log_path.start_with?('/')
34
+ c.log_path = ::Rails.root.join(c.log_path)
35
+ end
36
+ end
37
+
38
+ if Rails.start(config)
39
+ end
40
+ end
41
+ end
42
+ end