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