atatus 1.7.0 → 2.0.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/Gemfile +49 -13
- data/LICENSE +1 -1
- data/atatus.gemspec +3 -3
- data/lib/atatus/agent.rb +10 -7
- data/lib/atatus/central_config.rb +19 -8
- data/lib/atatus/collector/layer.rb +1 -1
- data/lib/atatus/{sql_summarizer.rb → config/log_level_map.rb} +22 -28
- data/lib/atatus/config/options.rb +2 -1
- data/lib/atatus/config/regexp_list.rb +1 -1
- data/lib/atatus/config/round_float.rb +31 -0
- data/lib/atatus/config/server_info.rb +50 -0
- data/lib/atatus/config/wildcard_pattern_list.rb +3 -1
- data/lib/atatus/config.rb +91 -70
- data/lib/atatus/context/request/socket.rb +1 -2
- data/lib/atatus/context/response.rb +1 -3
- data/lib/atatus/context.rb +3 -10
- data/lib/atatus/context_builder.rb +3 -3
- data/lib/atatus/error.rb +2 -1
- data/lib/atatus/error_builder.rb +1 -1
- data/lib/atatus/fields.rb +98 -0
- data/lib/atatus/graphql.rb +2 -0
- data/lib/atatus/grpc.rb +5 -7
- data/lib/atatus/instrumenter.rb +29 -25
- data/lib/atatus/metadata/cloud_info.rb +156 -0
- data/lib/atatus/metadata/service_info.rb +3 -3
- data/lib/atatus/metadata/system_info/container_info.rb +20 -8
- data/lib/atatus/metadata/system_info.rb +20 -5
- data/lib/atatus/metadata.rb +3 -1
- data/lib/atatus/metrics/cpu_mem_set.rb +10 -38
- data/lib/atatus/metrics/jvm_set.rb +88 -0
- data/lib/atatus/metrics/metric.rb +2 -0
- data/lib/atatus/metrics.rb +33 -16
- data/lib/atatus/middleware.rb +8 -3
- data/lib/atatus/naively_hashable.rb +1 -0
- data/lib/atatus/normalizers/rails/active_record.rb +25 -7
- data/lib/atatus/normalizers.rb +2 -2
- data/lib/atatus/opentracing.rb +5 -3
- data/lib/atatus/rails.rb +1 -1
- data/lib/atatus/span/context/db.rb +1 -1
- data/lib/atatus/span/context/destination.rb +58 -32
- data/lib/atatus/span/context/http.rb +2 -0
- data/lib/atatus/span/context/links.rb +32 -0
- data/lib/atatus/{sql.rb → span/context/message.rb} +16 -12
- data/lib/atatus/span/context/service.rb +55 -0
- data/lib/atatus/span/context.rb +28 -3
- data/lib/atatus/span.rb +35 -5
- data/lib/atatus/span_helpers.rb +12 -23
- data/lib/atatus/spies/action_dispatch.rb +10 -13
- data/lib/atatus/spies/azure_storage_table.rb +148 -0
- data/lib/atatus/spies/delayed_job.rb +19 -13
- data/lib/atatus/spies/dynamo_db.rb +56 -15
- data/lib/atatus/spies/elasticsearch.rb +54 -39
- data/lib/atatus/spies/faraday.rb +92 -58
- data/lib/atatus/spies/http.rb +33 -37
- data/lib/atatus/spies/json.rb +5 -9
- data/lib/atatus/spies/mongo.rb +26 -19
- data/lib/atatus/spies/net_http.rb +53 -51
- data/lib/atatus/spies/racecar.rb +77 -0
- data/lib/atatus/spies/rake.rb +27 -27
- data/lib/atatus/spies/redis.rb +11 -12
- data/lib/atatus/spies/resque.rb +18 -23
- data/lib/atatus/spies/s3.rb +132 -0
- data/lib/atatus/spies/sequel.rb +11 -2
- data/lib/atatus/spies/shoryuken.rb +4 -6
- data/lib/atatus/spies/sidekiq.rb +23 -31
- data/lib/atatus/spies/sinatra.rb +20 -28
- data/lib/atatus/spies/sneakers.rb +2 -0
- data/lib/atatus/spies/sns.rb +126 -0
- data/lib/atatus/spies/sqs.rb +231 -0
- data/lib/atatus/spies/sucker_punch.rb +20 -22
- data/lib/atatus/spies/tilt.rb +10 -13
- data/lib/atatus/spies.rb +20 -0
- data/lib/atatus/sql/signature.rb +4 -2
- data/lib/atatus/sql/tokenizer.rb +23 -7
- data/lib/atatus/stacktrace/frame.rb +1 -0
- data/lib/atatus/stacktrace_builder.rb +12 -16
- data/lib/atatus/subscriber.rb +1 -0
- data/lib/atatus/trace_context/traceparent.rb +5 -8
- data/lib/atatus/trace_context/tracestate.rb +16 -14
- data/lib/atatus/trace_context.rb +6 -16
- data/lib/atatus/transaction.rb +17 -4
- data/lib/atatus/transport/base.rb +1 -3
- data/lib/atatus/transport/connection/http.rb +11 -3
- data/lib/atatus/transport/connection/proxy_pipe.rb +1 -2
- data/lib/atatus/transport/connection.rb +3 -2
- data/lib/atatus/transport/filters/hash_sanitizer.rb +16 -34
- data/lib/atatus/transport/filters/secrets_filter.rb +35 -12
- data/lib/atatus/transport/serializers/context_serializer.rb +1 -2
- data/lib/atatus/transport/serializers/metadata_serializer.rb +54 -8
- data/lib/atatus/transport/serializers/metricset_serializer.rb +2 -2
- data/lib/atatus/transport/serializers/span_serializer.rb +55 -9
- data/lib/atatus/transport/serializers/transaction_serializer.rb +1 -0
- data/lib/atatus/transport/serializers.rb +9 -6
- data/lib/atatus/transport/user_agent.rb +16 -9
- data/lib/atatus/transport/worker.rb +2 -1
- data/lib/atatus/util/deep_dup.rb +65 -0
- data/lib/atatus/util/precision_validator.rb +46 -0
- data/lib/atatus/util.rb +2 -0
- data/lib/atatus/version.rb +1 -1
- data/lib/atatus.rb +32 -5
- metadata +40 -11
|
@@ -0,0 +1,156 @@
|
|
|
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 "http"
|
|
21
|
+
|
|
22
|
+
module Atatus
|
|
23
|
+
class Metadata
|
|
24
|
+
# @api private
|
|
25
|
+
class CloudInfo
|
|
26
|
+
include Logging
|
|
27
|
+
|
|
28
|
+
AWS_URI = "http://169.254.169.254/latest/dynamic/instance-identity/document"
|
|
29
|
+
GCP_URI = "http://metadata.google.internal/computeMetadata/v1/?recursive=true"
|
|
30
|
+
AZURE_URI = "http://169.254.169.254/metadata/instance/compute?api-version=2019-08-15"
|
|
31
|
+
|
|
32
|
+
def initialize(config)
|
|
33
|
+
@config = config
|
|
34
|
+
@client = HTTP.timeout(connect: 0.1, read: 0.1)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
attr_reader :config
|
|
38
|
+
|
|
39
|
+
attr_accessor(
|
|
40
|
+
:account_id,
|
|
41
|
+
:account_name,
|
|
42
|
+
:instance_id,
|
|
43
|
+
:instance_name,
|
|
44
|
+
:machine_type,
|
|
45
|
+
:project_id,
|
|
46
|
+
:project_name,
|
|
47
|
+
:availability_zone,
|
|
48
|
+
:provider,
|
|
49
|
+
:region
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
53
|
+
def fetch!
|
|
54
|
+
case config.cloud_provider
|
|
55
|
+
when "aws"
|
|
56
|
+
fetch_aws
|
|
57
|
+
when "gcp"
|
|
58
|
+
fetch_gcp
|
|
59
|
+
when "azure"
|
|
60
|
+
fetch_azure || read_azure_app_services
|
|
61
|
+
when "auto"
|
|
62
|
+
fetch_aws || fetch_gcp || fetch_azure || read_azure_app_services
|
|
63
|
+
when "none"
|
|
64
|
+
nil
|
|
65
|
+
else
|
|
66
|
+
error("Unknown setting for cloud_provider '#{config.cloud_provider}'")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
self
|
|
70
|
+
end
|
|
71
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def fetch_aws
|
|
76
|
+
resp = @client.get(AWS_URI)
|
|
77
|
+
|
|
78
|
+
return unless resp.status == 200
|
|
79
|
+
return unless (metadata = JSON.parse(resp.body.to_s))
|
|
80
|
+
|
|
81
|
+
self.provider = "aws"
|
|
82
|
+
self.account_id = metadata["accountId"]
|
|
83
|
+
self.instance_id = metadata["instanceId"]
|
|
84
|
+
self.availability_zone = metadata["availabilityZone"]
|
|
85
|
+
self.machine_type = metadata["instanceType"]
|
|
86
|
+
self.region = metadata["region"]
|
|
87
|
+
rescue HTTP::TimeoutError, HTTP::ConnectionError
|
|
88
|
+
nil
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def fetch_gcp
|
|
92
|
+
resp = @client.headers("Metadata-Flavor" => "Google").get(GCP_URI)
|
|
93
|
+
|
|
94
|
+
return unless resp.status == 200
|
|
95
|
+
return unless (metadata = JSON.parse(resp.body.to_s))
|
|
96
|
+
|
|
97
|
+
zone = metadata["instance"]["zone"]&.split("/")&.at(-1)
|
|
98
|
+
|
|
99
|
+
self.provider = "gcp"
|
|
100
|
+
self.instance_id = metadata["instance"]["id"].to_s
|
|
101
|
+
self.instance_name = metadata["instance"]["name"]
|
|
102
|
+
self.project_id = metadata["project"]["projectId"]
|
|
103
|
+
self.availability_zone = zone
|
|
104
|
+
self.region = zone.split("-")[0..-2].join("-")
|
|
105
|
+
self.machine_type = metadata["instance"]["machineType"].split("/")[-1]
|
|
106
|
+
rescue HTTP::TimeoutError, HTTP::ConnectionError
|
|
107
|
+
nil
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def fetch_azure
|
|
111
|
+
resp = @client.headers("Metadata" => "true").get(AZURE_URI)
|
|
112
|
+
|
|
113
|
+
return unless resp.status == 200
|
|
114
|
+
return unless (metadata = JSON.parse(resp.body.to_s))
|
|
115
|
+
|
|
116
|
+
self.provider = 'azure'
|
|
117
|
+
self.account_id = metadata["subscriptionId"]
|
|
118
|
+
self.instance_id = metadata["vmId"]
|
|
119
|
+
self.instance_name = metadata["name"]
|
|
120
|
+
self.project_name = metadata["resourceGroupName"]
|
|
121
|
+
self.availability_zone = metadata["zone"]
|
|
122
|
+
self.machine_type = metadata["vmSize"]
|
|
123
|
+
self.region = metadata["location"]
|
|
124
|
+
rescue HTTP::TimeoutError, HTTP::ConnectionError
|
|
125
|
+
nil
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def read_azure_app_services
|
|
129
|
+
owner_name, instance_id, site_name, resource_group =
|
|
130
|
+
ENV.values_at(
|
|
131
|
+
'WEBSITE_OWNER_NAME',
|
|
132
|
+
'WEBSITE_INSTANCE_ID',
|
|
133
|
+
'WEBSITE_SITE_NAME',
|
|
134
|
+
'WEBSITE_RESOURCE_GROUP'
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
return unless owner_name && instance_id && site_name && resource_group
|
|
138
|
+
|
|
139
|
+
self.provider = 'azure'
|
|
140
|
+
self.instance_id = instance_id
|
|
141
|
+
self.instance_name = site_name
|
|
142
|
+
self.project_name = resource_group
|
|
143
|
+
self.account_id, self.region = parse_azure_app_services_owner_name(owner_name)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
private
|
|
147
|
+
|
|
148
|
+
def parse_azure_app_services_owner_name(owner_name)
|
|
149
|
+
id, rest = owner_name.split('+')
|
|
150
|
+
*_, region = rest.split('-')
|
|
151
|
+
region.gsub!(/webspace.*$/, '')
|
|
152
|
+
[id, region]
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
@@ -49,11 +49,11 @@ module Atatus
|
|
|
49
49
|
)
|
|
50
50
|
@language = Language.new(name: 'ruby', version: RUBY_VERSION)
|
|
51
51
|
@runtime = lookup_runtime
|
|
52
|
-
@version = @config.service_version
|
|
52
|
+
@version = @config.service_version
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
attr_reader :name, :node_name, :environment, :agent, :framework,
|
|
56
|
-
:runtime, :version
|
|
55
|
+
attr_reader :name, :node_name, :environment, :agent, :framework,
|
|
56
|
+
:language, :runtime, :version
|
|
57
57
|
|
|
58
58
|
private
|
|
59
59
|
|
|
@@ -33,14 +33,15 @@ module Atatus
|
|
|
33
33
|
|
|
34
34
|
attr_reader :cgroup_path
|
|
35
35
|
|
|
36
|
-
def read!
|
|
36
|
+
def read!(hostname)
|
|
37
37
|
read_from_cgroup!
|
|
38
|
+
self.kubernetes_pod_name = hostname if kubernetes_pod_uid
|
|
38
39
|
read_from_env!
|
|
39
40
|
self
|
|
40
41
|
end
|
|
41
42
|
|
|
42
|
-
def self.read!
|
|
43
|
-
new.read!
|
|
43
|
+
def self.read!(hostname)
|
|
44
|
+
new.read!(hostname)
|
|
44
45
|
end
|
|
45
46
|
|
|
46
47
|
def container
|
|
@@ -81,15 +82,23 @@ module Atatus
|
|
|
81
82
|
ENV.fetch('KUBERNETES_POD_UID', kubernetes_pod_uid)
|
|
82
83
|
end
|
|
83
84
|
|
|
85
|
+
# rubocop:disable Style/RegexpLiteral
|
|
84
86
|
CONTAINER_ID_REGEXES = [
|
|
85
87
|
%r{^[[:xdigit:]]{64}$},
|
|
86
|
-
%r{
|
|
87
|
-
|
|
88
|
+
%r{
|
|
89
|
+
^[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]
|
|
90
|
+
{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4,}$
|
|
91
|
+
}x
|
|
92
|
+
].freeze
|
|
88
93
|
KUBEPODS_REGEXES = [
|
|
89
94
|
%r{(?:^/kubepods[^\s]*/pod([^/]+)$)},
|
|
90
|
-
%r{
|
|
91
|
-
|
|
95
|
+
%r{
|
|
96
|
+
(?:^/kubepods\.slice/(kubepods-[^/]+\.slice/)?
|
|
97
|
+
kubepods[^/]*-pod([^/]+)\.slice$)
|
|
98
|
+
}x
|
|
99
|
+
].freeze
|
|
92
100
|
SYSTEMD_SCOPE_SUFFIX = '.scope'
|
|
101
|
+
# rubocop:enable Style/RegexpLiteral
|
|
93
102
|
|
|
94
103
|
# rubocop:disable Metrics/PerceivedComplexity
|
|
95
104
|
# rubocop:disable Metrics/CyclomaticComplexity
|
|
@@ -125,7 +134,10 @@ module Atatus
|
|
|
125
134
|
end
|
|
126
135
|
|
|
127
136
|
if (kubepods_match = match_kubepods(directory))
|
|
128
|
-
pod_id = kubepods_match[1]
|
|
137
|
+
unless (pod_id = kubepods_match[1])
|
|
138
|
+
pod_id = kubepods_match[2]
|
|
139
|
+
pod_id&.tr!('_', '-')
|
|
140
|
+
end
|
|
129
141
|
|
|
130
142
|
self.container_id = container_id
|
|
131
143
|
self.kubernetes_pod_uid = pod_id
|
|
@@ -24,11 +24,13 @@ module Atatus
|
|
|
24
24
|
def initialize(config)
|
|
25
25
|
@config = config
|
|
26
26
|
|
|
27
|
-
@
|
|
27
|
+
@configured_hostname = @config.hostname
|
|
28
|
+
@detected_hostname = detect_hostname
|
|
29
|
+
@hostname = @configured_hostname || @detected_hostname
|
|
28
30
|
@architecture = gem_platform.cpu
|
|
29
31
|
@platform = gem_platform.os
|
|
30
32
|
|
|
31
|
-
container_info = ContainerInfo.read!
|
|
33
|
+
container_info = ContainerInfo.read!(@detected_hostname)
|
|
32
34
|
@container = container_info.container
|
|
33
35
|
@kubernetes = container_info.kubernetes
|
|
34
36
|
|
|
@@ -36,14 +38,27 @@ module Atatus
|
|
|
36
38
|
@osinfo = OSInfo.read!
|
|
37
39
|
end
|
|
38
40
|
|
|
39
|
-
attr_reader
|
|
41
|
+
attr_reader(
|
|
42
|
+
:detected_hostname,
|
|
43
|
+
:configured_hostname,
|
|
44
|
+
:architecture,
|
|
45
|
+
:platform,
|
|
46
|
+
:container,
|
|
47
|
+
:kubernetes,
|
|
48
|
+
:hwinfo,
|
|
49
|
+
:osinfo,
|
|
50
|
+
:hostname
|
|
51
|
+
)
|
|
40
52
|
|
|
41
53
|
def gem_platform
|
|
42
54
|
@gem_platform ||= Gem::Platform.local
|
|
43
55
|
end
|
|
44
56
|
|
|
45
|
-
|
|
46
|
-
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def detect_hostname
|
|
60
|
+
Socket.gethostname.chomp
|
|
61
|
+
rescue
|
|
47
62
|
end
|
|
48
63
|
end
|
|
49
64
|
end
|
data/lib/atatus/metadata.rb
CHANGED
|
@@ -25,12 +25,14 @@ module Atatus
|
|
|
25
25
|
@process = ProcessInfo.new(config)
|
|
26
26
|
@system = SystemInfo.new(config)
|
|
27
27
|
@labels = config.global_labels
|
|
28
|
+
@cloud = CloudInfo.new(config).fetch!
|
|
28
29
|
end
|
|
29
30
|
|
|
30
|
-
attr_reader :service, :process, :system, :labels
|
|
31
|
+
attr_reader :service, :process, :system, :cloud, :labels
|
|
31
32
|
end
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
require 'atatus/metadata/service_info'
|
|
35
36
|
require 'atatus/metadata/system_info'
|
|
36
37
|
require 'atatus/metadata/process_info'
|
|
38
|
+
require 'atatus/metadata/cloud_info'
|
|
@@ -60,11 +60,10 @@ module Atatus
|
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def initialize(config)
|
|
63
|
-
@vmset_disabled = false
|
|
64
63
|
super
|
|
65
64
|
|
|
66
|
-
@sampler =
|
|
67
|
-
read! # set
|
|
65
|
+
@sampler = sampler_for_os(Metrics.os)
|
|
66
|
+
read! # set initial values to calculate deltas from
|
|
68
67
|
end
|
|
69
68
|
|
|
70
69
|
attr_reader :config
|
|
@@ -76,19 +75,11 @@ module Atatus
|
|
|
76
75
|
|
|
77
76
|
private
|
|
78
77
|
|
|
79
|
-
def
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def vmset_disabled?
|
|
84
|
-
@vmset_disabled
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def sampler_for_platform(platform)
|
|
88
|
-
case platform
|
|
89
|
-
when :linux then Linux.new
|
|
78
|
+
def sampler_for_os(os)
|
|
79
|
+
case os
|
|
80
|
+
when /^linux/ then Linux.new
|
|
90
81
|
else
|
|
91
|
-
warn "
|
|
82
|
+
warn "Disabling system metrics, unsupported host OS '#{os}'"
|
|
92
83
|
disable!
|
|
93
84
|
nil
|
|
94
85
|
end
|
|
@@ -115,29 +106,6 @@ module Atatus
|
|
|
115
106
|
current.process_memory_rss * current.page_size
|
|
116
107
|
|
|
117
108
|
@previous = current
|
|
118
|
-
|
|
119
|
-
return if vmset_disabled?
|
|
120
|
-
|
|
121
|
-
stat = GC.stat
|
|
122
|
-
|
|
123
|
-
gauge(:'ruby.gc.count').value = stat[:count]
|
|
124
|
-
gauge(:'ruby.threads').value = Thread.list.count
|
|
125
|
-
gauge(:'ruby.heap.slots.live').value = stat[:heap_live_slots]
|
|
126
|
-
|
|
127
|
-
gauge(:'ruby.heap.slots.free').value = stat[:heap_free_slots]
|
|
128
|
-
gauge(:'ruby.heap.allocations.total').value =
|
|
129
|
-
stat[:total_allocated_objects]
|
|
130
|
-
|
|
131
|
-
return unless GC::Profiler.enabled?
|
|
132
|
-
@total_time ||= 0
|
|
133
|
-
@total_time += GC::Profiler.total_time
|
|
134
|
-
GC::Profiler.clear
|
|
135
|
-
gauge(:'ruby.gc.time').value = @total_time
|
|
136
|
-
rescue TypeError => e
|
|
137
|
-
error 'VM metrics encountered error: %s', e
|
|
138
|
-
debug('Backtrace:') { e.backtrace.join("\n") }
|
|
139
|
-
|
|
140
|
-
vmset_disable!
|
|
141
109
|
end
|
|
142
110
|
|
|
143
111
|
def calculate_deltas(current, previous)
|
|
@@ -148,6 +116,9 @@ module Atatus
|
|
|
148
116
|
process_cpu_usage =
|
|
149
117
|
current.process_cpu_usage - previous.process_cpu_usage
|
|
150
118
|
|
|
119
|
+
# No change / avoid dividing by 0
|
|
120
|
+
return [0, 0] if system_cpu_total == 0
|
|
121
|
+
|
|
151
122
|
cpu_usage_pct = system_cpu_usage.to_f / system_cpu_total
|
|
152
123
|
cpu_process_pct = process_cpu_usage.to_f / system_cpu_total
|
|
153
124
|
|
|
@@ -246,6 +217,7 @@ module Atatus
|
|
|
246
217
|
# @api private
|
|
247
218
|
class Meminfo
|
|
248
219
|
attr_reader :total, :available, :page_size
|
|
220
|
+
|
|
249
221
|
# rubocop:disable Metrics/PerceivedComplexity
|
|
250
222
|
# rubocop:disable Metrics/CyclomaticComplexity
|
|
251
223
|
def read!
|
|
@@ -0,0 +1,88 @@
|
|
|
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 'java'
|
|
21
|
+
|
|
22
|
+
module Atatus
|
|
23
|
+
module Metrics
|
|
24
|
+
# @api private
|
|
25
|
+
class JVMSet < Set
|
|
26
|
+
include Logging
|
|
27
|
+
|
|
28
|
+
MAX_TRIES = 3
|
|
29
|
+
|
|
30
|
+
def initialize(*args)
|
|
31
|
+
super
|
|
32
|
+
|
|
33
|
+
@error_count = 0
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def collect
|
|
37
|
+
read!
|
|
38
|
+
super
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def read!
|
|
42
|
+
return if disabled?
|
|
43
|
+
|
|
44
|
+
heap = platform_bean.get_heap_memory_usage
|
|
45
|
+
non_heap = platform_bean.get_non_heap_memory_usage
|
|
46
|
+
|
|
47
|
+
gauge(:"jvm.memory.heap.used").value = heap.get_used
|
|
48
|
+
gauge(:"jvm.memory.heap.committed").value = heap.get_committed
|
|
49
|
+
gauge(:"jvm.memory.heap.max").value = heap.get_max
|
|
50
|
+
|
|
51
|
+
gauge(:"jvm.memory.non_heap.used").value = non_heap.get_used
|
|
52
|
+
gauge(:"jvm.memory.non_heap.committed").value = non_heap.get_committed
|
|
53
|
+
gauge(:"jvm.memory.non_heap.max").value = non_heap.get_max
|
|
54
|
+
|
|
55
|
+
pool_beans.each do |bean|
|
|
56
|
+
next unless bean.type.name == "HEAP"
|
|
57
|
+
|
|
58
|
+
tags = { name: bean.get_name }
|
|
59
|
+
|
|
60
|
+
gauge(:"jvm.memory.heap.pool.used", tags: tags).value = bean.get_usage.get_used
|
|
61
|
+
gauge(:"jvm.memory.heap.pool.committed", tags: tags).value = bean.get_usage.get_committed
|
|
62
|
+
gauge(:"jvm.memory.heap.pool.max", tags: tags).value = bean.get_usage.get_max
|
|
63
|
+
end
|
|
64
|
+
rescue Exception => e
|
|
65
|
+
error("JVM metrics encountered error: %s", e)
|
|
66
|
+
debug("Backtrace:") { e.backtrace.join("\n") }
|
|
67
|
+
|
|
68
|
+
@error_count += 1
|
|
69
|
+
if @error_count >= MAX_TRIES
|
|
70
|
+
disable!
|
|
71
|
+
error("Disabling JVM metrics after #{MAX_TRIES} errors", e)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def platform_bean
|
|
78
|
+
@platform_bean ||= java.lang.management.ManagementFactory.getPlatformMXBean(
|
|
79
|
+
java.lang.management.MemoryMXBean.java_class
|
|
80
|
+
)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def pool_beans
|
|
84
|
+
@pool_beans ||= java.lang.management.ManagementFactory.getMemoryPoolMXBeans()
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
data/lib/atatus/metrics.rb
CHANGED
|
@@ -28,44 +28,38 @@ module Atatus
|
|
|
28
28
|
@platform ||= Gem::Platform.local.os.to_sym
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
def self.os
|
|
32
|
+
@os ||= RbConfig::CONFIG.fetch('host_os', 'unknown').to_sym
|
|
33
|
+
end
|
|
34
|
+
|
|
31
35
|
# @api private
|
|
32
36
|
class Registry
|
|
33
37
|
include Logging
|
|
34
38
|
|
|
35
|
-
TIMEOUT_INTERVAL = 5 # seconds
|
|
36
|
-
|
|
37
39
|
def initialize(config, &block)
|
|
38
40
|
@config = config
|
|
39
41
|
@callback = block
|
|
42
|
+
@sets = nil
|
|
40
43
|
end
|
|
41
44
|
|
|
42
45
|
attr_reader :config, :sets, :callback
|
|
46
|
+
|
|
43
47
|
def start
|
|
44
48
|
unless config.collect_metrics?
|
|
45
49
|
debug 'Skipping metrics'
|
|
46
50
|
return
|
|
47
51
|
end
|
|
48
52
|
|
|
49
|
-
|
|
53
|
+
define_sets
|
|
50
54
|
|
|
51
|
-
|
|
52
|
-
# and start again.
|
|
53
|
-
@sets ||= {
|
|
54
|
-
system: CpuMemSet,
|
|
55
|
-
# vm: VMSet,
|
|
56
|
-
breakdown: BreakdownSet,
|
|
57
|
-
transaction: TransactionSet
|
|
58
|
-
}.each_with_object({}) do |(key, kls), sets|
|
|
59
|
-
debug "Adding metrics collector '#{kls}'"
|
|
60
|
-
sets[key] = kls.new(config)
|
|
61
|
-
end
|
|
55
|
+
debug 'Starting metrics'
|
|
62
56
|
|
|
63
57
|
@timer_task = Concurrent::TimerTask.execute(
|
|
64
58
|
run_now: true,
|
|
65
|
-
execution_interval: config.metrics_interval
|
|
66
|
-
timeout_interval: TIMEOUT_INTERVAL
|
|
59
|
+
execution_interval: config.metrics_interval
|
|
67
60
|
) do
|
|
68
61
|
begin
|
|
62
|
+
debug 'Collecting metrics'
|
|
69
63
|
collect_and_send
|
|
70
64
|
true
|
|
71
65
|
rescue StandardError => e
|
|
@@ -123,6 +117,28 @@ module Atatus
|
|
|
123
117
|
arr.concat(samples)
|
|
124
118
|
end
|
|
125
119
|
end
|
|
120
|
+
|
|
121
|
+
def define_sets
|
|
122
|
+
# Only set the @sets once, in case we stop
|
|
123
|
+
# and start again.
|
|
124
|
+
return unless @sets.nil?
|
|
125
|
+
|
|
126
|
+
sets = {
|
|
127
|
+
system: CpuMemSet,
|
|
128
|
+
vm: VMSet,
|
|
129
|
+
breakdown: BreakdownSet,
|
|
130
|
+
transaction: TransactionSet
|
|
131
|
+
}
|
|
132
|
+
if defined?(JVMSet)
|
|
133
|
+
debug "Enabling JVM metrics collection"
|
|
134
|
+
sets[:jvm] = JVMSet
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
@sets = sets.each_with_object({}) do |(key, kls), _sets_|
|
|
138
|
+
debug "Adding metrics collector '#{kls}'"
|
|
139
|
+
_sets_[key] = kls.new(config)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
126
142
|
end
|
|
127
143
|
end
|
|
128
144
|
end
|
|
@@ -134,6 +150,7 @@ require 'atatus/metrics/set'
|
|
|
134
150
|
|
|
135
151
|
require 'atatus/metrics/cpu_mem_set'
|
|
136
152
|
require 'atatus/metrics/vm_set'
|
|
153
|
+
require 'atatus/metrics/jvm_set' if defined?(JRUBY_VERSION)
|
|
137
154
|
require 'atatus/metrics/span_scoped_set'
|
|
138
155
|
require 'atatus/metrics/transaction_set'
|
|
139
156
|
require 'atatus/metrics/breakdown_set'
|
data/lib/atatus/middleware.rb
CHANGED
|
@@ -41,9 +41,14 @@ module Atatus
|
|
|
41
41
|
Atatus.report(e, context: context, handled: false)
|
|
42
42
|
raise
|
|
43
43
|
ensure
|
|
44
|
-
if
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
if transaction
|
|
45
|
+
if resp
|
|
46
|
+
status, headers, _body = resp
|
|
47
|
+
transaction.add_response(status, headers: headers.dup)
|
|
48
|
+
transaction&.outcome = Transaction::Outcome.from_http_status(status)
|
|
49
|
+
else
|
|
50
|
+
transaction&.outcome = Transaction::Outcome::FAILURE
|
|
51
|
+
end
|
|
47
52
|
end
|
|
48
53
|
|
|
49
54
|
Atatus.end_transaction http_result(status)
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
# frozen_string_literal: true
|
|
19
19
|
|
|
20
|
-
require 'atatus/sql'
|
|
20
|
+
require 'atatus/sql/signature'
|
|
21
21
|
|
|
22
22
|
module Atatus
|
|
23
23
|
module Normalizers
|
|
@@ -34,7 +34,7 @@ module Atatus
|
|
|
34
34
|
def initialize(*args)
|
|
35
35
|
super
|
|
36
36
|
|
|
37
|
-
@summarizer = Sql.
|
|
37
|
+
@summarizer = Sql::Signature::Summarizer.new
|
|
38
38
|
|
|
39
39
|
@adapters = {}
|
|
40
40
|
end
|
|
@@ -48,7 +48,7 @@ module Atatus
|
|
|
48
48
|
context =
|
|
49
49
|
Span::Context.new(
|
|
50
50
|
db: { statement: payload[:sql], type: 'sql' },
|
|
51
|
-
destination: { name: subtype, resource: subtype, type: TYPE }
|
|
51
|
+
destination: { service: { name: subtype, resource: subtype, type: TYPE } }
|
|
52
52
|
)
|
|
53
53
|
|
|
54
54
|
[name, TYPE, subtype, ACTION, context]
|
|
@@ -57,10 +57,21 @@ module Atatus
|
|
|
57
57
|
private
|
|
58
58
|
|
|
59
59
|
def subtype_for(payload)
|
|
60
|
-
|
|
61
|
-
payload[:connection]
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
if payload[:connection]
|
|
61
|
+
return cached_adapter_name(payload[:connection].adapter_name)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
if can_attempt_connection_id_lookup?(payload)
|
|
65
|
+
begin
|
|
66
|
+
loaded_object = ObjectSpace._id2ref(payload[:connection_id])
|
|
67
|
+
if loaded_object.respond_to?(:adapter_name)
|
|
68
|
+
return cached_adapter_name(loaded_object.adapter_name)
|
|
69
|
+
end
|
|
70
|
+
rescue RangeError # if connection object has somehow been garbage collected
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
cached_adapter_name(::ActiveRecord::Base.connection_config[:adapter])
|
|
64
75
|
end
|
|
65
76
|
|
|
66
77
|
def summarize(sql)
|
|
@@ -69,11 +80,18 @@ module Atatus
|
|
|
69
80
|
|
|
70
81
|
def cached_adapter_name(adapter_name)
|
|
71
82
|
return UNKNOWN if adapter_name.nil? || adapter_name.empty?
|
|
83
|
+
|
|
72
84
|
@adapters[adapter_name] ||
|
|
73
85
|
(@adapters[adapter_name] = adapter_name.downcase)
|
|
74
86
|
rescue StandardError
|
|
75
87
|
nil
|
|
76
88
|
end
|
|
89
|
+
|
|
90
|
+
def can_attempt_connection_id_lookup?(payload)
|
|
91
|
+
RUBY_ENGINE == "ruby" &&
|
|
92
|
+
payload[:connection_id] &&
|
|
93
|
+
ObjectSpace.respond_to?(:_id2ref)
|
|
94
|
+
end
|
|
77
95
|
end
|
|
78
96
|
end
|
|
79
97
|
end
|
data/lib/atatus/normalizers.rb
CHANGED
|
@@ -39,8 +39,8 @@ module Atatus # :nodoc:
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
def self.build(config)
|
|
42
|
-
normalizers = @registered.
|
|
43
|
-
|
|
42
|
+
normalizers = @registered.transform_values do |klass|
|
|
43
|
+
klass.new(config)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
Collection.new(normalizers)
|