atatus 1.7.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|