atatus 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: acfc54c3597def60fca53e99bf249711197735e0507b9bcade319f10182cbc26
4
- data.tar.gz: efb49084366463b5ed40d3b4c57609ada57e745f28f8ac5949456e242167ede1
3
+ metadata.gz: 0ebfb72781685c274fbd7f4516df59c2ba5dc6533bc887dfbad3a3b6b93e08f6
4
+ data.tar.gz: 756f210e8bb7136aa87806fbdb3dcf1e1a9bd5986e8717322b26fba4148a9613
5
5
  SHA512:
6
- metadata.gz: e6e2af0647be02da224d11dd9a45650c0c6cae63e10dbfaf81fd2d757fe8aa3414ab82ffad9eb880fc73f3340c145a175374be77a41f646febd621bf50502dca
7
- data.tar.gz: 170206df98d5e3b5ccf68271f9dfdb58042ce45b13129321105aac2cf4fa8a04095807e0adc4ed75589bd09b556284ea41ac5a2030565584d96640da58aac03c
6
+ metadata.gz: 0a309c3b2ad681d3dc7a0a95b655e80ef0184a8b6522bf3f724b1266f65a0904e963043e21518711e3582d10757a7ab0c8c8550fd4df53d0a68442496291a352
7
+ data.tar.gz: 65c1dd53f5b6115b27edded97df2240af6e2d6217517fb59641792a60d185b6916a1bf442cb5fa277ece9711bca96a6a56acec0948edc7d6282ece771140c64e
@@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## 1.0.2 (Thu, 21 Nov 2019)
8
+
9
+ - Changed Linux memory size to bytes.
10
+ - Set type to agent name if framework is not deducted.
11
+ - Code refactoring for checking existence of properties.
12
+
13
+
7
14
  ## 1.0.1 (Wed, 13 Nov 2019)
8
15
 
9
16
  - Fixed Instrumenting classes only when there are defined.
@@ -1,81 +1,19 @@
1
+ require 'json'
1
2
  require 'atatus'
2
- require 'atatus/transaction'
3
3
  require 'atatus/config'
4
- # require 'atatus/logger'
4
+ require 'atatus/transaction'
5
+ require 'atatus/collector/layer'
5
6
  require 'atatus/collector/transport'
6
- require 'json'
7
7
 
8
8
  module Atatus
9
9
  module Collector
10
- class Layer
11
- attr_reader :count, :min, :max, :total, :type, :kind, :id, :pid
12
- attr_writer :id, :pid
13
-
14
- def initialize(type, kind, duration)
15
- @type = type
16
- @kind = kind
17
- @count = 1
18
- @min = duration
19
- @max = duration
20
- @total = duration
21
- end
22
-
23
- def aggregate!(duration)
24
- @count += 1
25
- @min = duration if duration < @min
26
- @max = duration if duration > @max
27
- @total += duration
28
- end
29
-
30
- def self.span_kind(value)
31
- return KINDS.fetch(value, value)
32
- end
33
-
34
- def self.span_type(value)
35
- return TYPES.fetch(value, value)
36
- end
37
-
38
- private
39
- KINDS = {
40
- 'db' => 'Database',
41
- 'cache' => 'Database',
42
- 'ext' => 'Remote',
43
- 'websocket' => 'Remote',
44
- 'template' => 'Template'
45
- }.freeze
46
-
47
- TYPES = {
48
- 'mysql' => 'MySQL',
49
- 'mysql2' => 'MySQL',
50
- 'postgresql' => 'Postgres',
51
- 'mssql' => 'MS SQL',
52
- 'mongodb' => 'MongoDB',
53
- 'redis' => 'Redis',
54
- 'graphql' => 'GraphQL',
55
- 'elasticsearch' => 'Elasticsearch',
56
- 'cassandra' => 'Cassandra',
57
- 'http' => 'External Requests',
58
- 'http2' => 'External Requests',
59
- 'http_rb' => 'External Requests',
60
- 'net_http' => 'External Requests'
61
- }.freeze
62
- end
63
-
64
10
  class Txn < Layer
65
- attr_reader :spans
66
-
67
11
  def initialize(type, kind, duration)
68
12
  super(type, kind, duration)
69
- @spans = Hash.new {}
13
+ @spans = {}
70
14
  end
71
15
 
72
- def add_span(span_name, type, kind, duration)
73
- if !@spans.has_key?(span_name)
74
- @spans[key] = Layer.new(type, kind, duration)
75
- else
76
- @spans[key].aggregate! duration
77
- end
78
- end
16
+ attr_reader :spans
79
17
  end
80
18
 
81
19
  class Base
@@ -85,21 +23,22 @@ module Atatus
85
23
  # info 'Initializing Collector'
86
24
  @config = config
87
25
  @spans = Hash.new {|h,k| h[k]=[]}
88
- @running = false
89
- uri = URI("https://apm-rx.atatus.com/")
90
- @transport = Atatus::BaseTransport.new(config, uri.host, uri.port)
91
- @txn_lock = Mutex.new
92
- @txn_aggs = Hash.new {}
93
- @trace_aggs = Array.new()
94
- @error_metrics = Hash.new {}
95
- @error_requests = Array.new()
96
26
 
97
- @error_lock = Mutex.new
98
- @error_aggs = Array.new()
27
+ @txns_lock = Mutex.new
28
+ @txns_agg = {}
29
+ @traces_agg = []
30
+ @error_metrics_agg = {}
31
+ @error_requests_agg = []
32
+
33
+ @errors_lock = Mutex.new
34
+ @errors_aggs = []
99
35
 
100
36
  @metrics_lock = Mutex.new
101
- @metrics = Array.new()
37
+ @metrics_agg = []
38
+
39
+ @transport = Atatus::BaseTransport.new(config)
102
40
  @collect_counter = 0
41
+ @running = false
103
42
  end
104
43
 
105
44
  attr_reader :config
@@ -130,80 +69,113 @@ module Atatus
130
69
  def add_error(error)
131
70
  ensure_worker_running
132
71
 
133
- @error_lock.synchronize do
134
- if @error_aggs.length < 20
135
- @error_aggs.push(error)
72
+ @errors_lock.synchronize do
73
+ if @errors_aggs.length < 20
74
+ @errors_aggs.push(error)
136
75
  else
137
76
  i = rand(20)
138
- @error_aggs[i] = error
77
+ @errors_aggs[i] = error
139
78
  end
140
79
  end
141
- # puts error.to_yaml
142
80
  end
143
81
 
144
82
  def add_metrics(metricset)
145
83
  ensure_worker_running
84
+
85
+ metric_added = false
146
86
  metric = {}
147
- if metricset.samples.key?(:'system.memory.total')
87
+
88
+ if %i[system.cpu.total.norm.pct system.memory.actual.free system.memory.total system.process.cpu.total.norm.pct system.process.memory.size system.process.memory.rss.bytes].all? {|s| metricset.samples.key? s}
89
+ then
148
90
  metric[:'system.cpu.total.norm.pct'] = metricset.samples[:'system.cpu.total.norm.pct']
149
91
  metric[:'system.memory.actual.free'] = metricset.samples[:'system.memory.actual.free']
150
92
  metric[:'system.memory.total'] = metricset.samples[:'system.memory.total']
151
93
  metric[:'system.process.cpu.total.norm.pct'] = metricset.samples[:'system.process.cpu.total.norm.pct']
152
94
  metric[:'system.process.memory.size'] = metricset.samples[:'system.process.memory.size']
153
95
  metric[:'system.process.memory.rss.bytes'] = metricset.samples[:'system.process.memory.rss.bytes']
96
+ metric_added = true
154
97
  end
155
98
 
156
- if metricset.samples.key?(:'ruby.gc.count')
99
+ if %i[ruby.gc.count ruby.threads ruby.heaps.slots.live ruby.heap.slots.free ruby.heap.allocations.total].all? {|s| metricset.samples.key? s}
100
+ then
157
101
  metric[:'ruby.gc.count'] = metricset.samples[:'ruby.gc.count']
158
102
  metric[:'ruby.threads'] = metricset.samples[:'ruby.threads']
159
103
  metric[:'ruby.heap.slots.live'] = metricset.samples[:'ruby.heap.slots.live']
160
104
  metric[:'ruby.heap.slots.free'] = metricset.samples[:'ruby.heap.slots.free']
161
105
  metric[:'ruby.heap.allocations.total'] = metricset.samples[:'ruby.heap.allocations.total']
106
+ metric_added = true
162
107
  end
163
108
 
164
- @metrics_lock.synchronize do
165
- @metrics << metric
109
+ if metric_added
110
+ @metrics_lock.synchronize do
111
+ @metrics_agg << metric
112
+ end
166
113
  end
167
- # puts metric.to_yaml
168
114
  end
169
115
 
170
116
  def add_span(span)
171
117
  ensure_worker_running
172
118
 
119
+ if
120
+ span.transaction_id.nil? ||
121
+ span.name.nil? ||
122
+ span.type.nil? ||
123
+ span.subtype.nil? ||
124
+ span.duration.nil?
125
+ then
126
+ return
127
+ end
128
+
173
129
  @spans[span.transaction_id] << span if span.transaction_id
174
- # puts span.to_yaml
175
130
  end
176
131
 
177
132
  def add_txn(txn)
178
133
  ensure_worker_running
179
134
 
180
- # puts txn.to_yaml
181
- txn_name = txn.name
182
- return unless txn_name
183
- @txn_lock.synchronize do
184
- if !@txn_aggs.has_key?(txn_name)
185
- @txn_aggs[txn_name] = Txn.new(@config.framework_name, "Ruby", txn.duration)
186
- @txn_aggs[txn_name].id = txn.id
187
- @txn_aggs[txn_name].pid = txn.id
135
+ if
136
+ txn.name.nil? ||
137
+ txn.id.nil? ||
138
+ txn.duration.nil?
139
+ then
140
+ return
141
+ end
142
+
143
+ return if txn.name.empty?
144
+
145
+ @txns_lock.synchronize do
146
+ if !@txns_agg.key?(txn.name)
147
+ @txns_agg[txn.name] = Txn.new(@config.framework_name, "Ruby", txn.duration)
148
+ @txns_agg[txn.name].id = txn.id
149
+ @txns_agg[txn.name].pid = txn.id
188
150
  else
189
- @txn_aggs[txn_name].aggregate! txn.duration
151
+ @txns_agg[txn.name].aggregate! txn.duration
190
152
  end
191
153
 
192
154
  spans_present = false
193
- if @spans.has_key?(txn.id)
155
+ if @spans.key?(txn.id)
194
156
  @spans[txn.id].each do |span|
195
- span_name = span.name
196
- if !@txn_aggs[txn_name].spans.has_key?(span_name)
157
+ if
158
+ span.name.nil? ||
159
+ span.type.nil? ||
160
+ span.subtype.nil? ||
161
+ span.duration.nil?
162
+ then
163
+ next
164
+ end
165
+
166
+ next if span.name.empty?
167
+
168
+ if !@txns_agg[txn.name].spans.key?(span.name)
197
169
  kind = Layer.span_kind(span.type)
198
170
  type = Layer.span_type(span.subtype)
199
- @txn_aggs[txn_name].spans[span_name] = Layer.new(type, kind, span.duration)
200
- @txn_aggs[txn_name].spans[span_name].id = span.id
201
- @txn_aggs[txn_name].spans[span_name].pid = span.transaction_id
171
+ @txns_agg[txn.name].spans[span.name] = Layer.new(type, kind, span.duration)
172
+ @txns_agg[txn.name].spans[span.name].id = span.id
173
+ @txns_agg[txn.name].spans[span.name].pid = span.transaction_id
202
174
  else
203
- @txn_aggs[txn_name].spans[span_name].aggregate! span.duration
175
+ @txns_agg[txn.name].spans[span.name].aggregate! span.duration
204
176
  end
177
+ spans_present = true
205
178
  end
206
- spans_present = true
207
179
  end
208
180
 
209
181
  if spans_present
@@ -211,11 +183,11 @@ module Atatus
211
183
  trace_txn = txn
212
184
  trace_txn.spans = @spans[txn.id]
213
185
 
214
- if @trace_aggs.length < 5
215
- @trace_aggs.push(trace_txn)
186
+ if @traces_agg.length < 5
187
+ @traces_agg.push(trace_txn)
216
188
  else
217
189
  i = rand(5)
218
- @trace_aggs[i] = trace_txn
190
+ @traces_agg[i] = trace_txn
219
191
  end
220
192
  end
221
193
  end
@@ -224,25 +196,30 @@ module Atatus
224
196
  @spans.delete(txn.id)
225
197
  end
226
198
 
227
- status_code = txn.context.response.status_code
199
+ if
200
+ !txn.context.nil? &&
201
+ !txn.context.response.nil? &&
202
+ !txn.context.response.status_code.nil?
203
+ then
204
+ status_code = txn.context.response.status_code
228
205
 
229
- if status_code >= 400 && status_code != 404
230
- if !@error_metrics.has_key?(txn_name)
231
- @error_metrics[txn_name] = {status_code => 1}
232
- else
233
- if !@error_metrics[txn_name].has_key?(status_code)
234
- @error_metrics[txn_name][status_code] = 1
206
+ if status_code >= 400 && status_code != 404
207
+ if !@error_metrics_agg.key?(txn.name)
208
+ @error_metrics_agg[txn.name] = {status_code => 1}
235
209
  else
236
- @error_metrics[txn_name][status_code] += 1
210
+ if !@error_metrics_agg[txn.name].key?(status_code)
211
+ @error_metrics_agg[txn.name][status_code] = 1
212
+ else
213
+ @error_metrics_agg[txn.name][status_code] += 1
214
+ end
237
215
  end
238
- end
239
216
 
240
- # puts txn.context.to_yaml
241
- if @error_requests.length < 20
242
- @error_requests.push({'name' => txn_name, 'context' => txn.context})
243
- else
244
- i = rand(20)
245
- @error_requests[i] = {'name' => txn_name, 'context' => txn.context}
217
+ if @error_requests_agg.length < 20
218
+ @error_requests_agg.push({'name' => txn.name, 'context' => txn.context})
219
+ else
220
+ i = rand(20)
221
+ @error_requests_agg[i] = {'name' => txn.name, 'context' => txn.context}
222
+ end
246
223
  end
247
224
  end
248
225
 
@@ -257,7 +234,7 @@ module Atatus
257
234
  debug '%s: Starting collector worker', pid_str
258
235
 
259
236
  while @running
260
- start_time = Time.now
237
+ start_time = (Time.now.to_f * 1000).to_i
261
238
  sleep(60)
262
239
  collect start_time
263
240
  end
@@ -269,8 +246,6 @@ module Atatus
269
246
  end
270
247
 
271
248
  def collect(start_time)
272
- # puts @config.to_yaml
273
- # return
274
249
  if @config.license_key.nil? || @config.app_name.nil?
275
250
  if @config.license_key.nil? && @config.app_name.nil?
276
251
  error '%s: Atatus configuration license_key and app_name are missing', pid_str
@@ -290,44 +265,48 @@ module Atatus
290
265
  end
291
266
  @collect_counter += 1
292
267
 
293
- end_time = Time.now
268
+ end_time = (Time.now.to_f * 1000).to_i
294
269
  debug '%s: Collecting transactions', pid_str
295
270
 
296
- txn_data = nil
297
- trace_data = nil
298
- error_data = nil
271
+ txns_data = nil
272
+ traces_data = nil
299
273
  error_metrics_data = nil
300
274
  error_requests_data = nil
275
+ errors_data = nil
301
276
  metrics_data = nil
302
277
 
303
- @txn_lock.synchronize do
304
- txn_data = @txn_aggs
305
- @txn_aggs = Hash.new {}
278
+ @txns_lock.synchronize do
279
+ txns_data = @txns_agg
280
+ @txns_agg = {}
306
281
 
307
- trace_data = @trace_aggs
308
- @trace_aggs = Array.new()
282
+ traces_data = @traces_agg
283
+ @traces_agg = []
309
284
 
310
- error_metrics_data = @error_metrics
311
- @error_metrics = Hash.new {}
285
+ error_metrics_data = @error_metrics_agg
286
+ @error_metrics_agg = {}
312
287
 
313
- error_requests_data = @error_requests
314
- @error_requests = Array.new()
288
+ error_requests_data = @error_requests_agg
289
+ @error_requests_agg = []
315
290
  end
316
291
 
317
- @error_lock.synchronize do
318
- error_data = @error_aggs
319
- @error_aggs = Array.new()
292
+ @errors_lock.synchronize do
293
+ errors_data = @errors_aggs
294
+ @errors_aggs = []
320
295
  end
321
296
 
322
297
  @metrics_lock.synchronize do
323
- metrics_data = @metrics
324
- @metrics = Array.new()
298
+ metrics_data = @metrics_agg
299
+ @metrics_agg = []
325
300
  end
326
301
 
327
- @transport.txn(start_time, end_time, txn_data) unless txn_data.empty?
328
- @transport.trace(start_time, end_time, trace_data) unless trace_data.empty?
302
+ @transport.txns(start_time, end_time, txns_data) unless txns_data.empty?
303
+
304
+ @transport.traces(start_time, end_time, traces_data) unless traces_data.empty?
305
+
329
306
  @transport.error_metrics(start_time, end_time, error_metrics_data, error_requests_data) unless error_metrics_data.empty?
330
- @transport.errors(start_time, end_time, error_data) unless error_data.empty?
307
+
308
+ @transport.errors(start_time, end_time, errors_data) unless errors_data.empty?
309
+
331
310
  @transport.metrics(start_time, end_time, metrics_data) unless metrics_data.empty?
332
311
  end
333
312
  end
@@ -1,6 +1,6 @@
1
1
  require 'socket'
2
2
  require 'atatus/metadata'
3
-
3
+ require 'yaml'
4
4
  module Atatus
5
5
  class Builder
6
6
  AGENT_NAME = 'Ruby'
@@ -8,6 +8,15 @@ module Atatus
8
8
  def initialize(config)
9
9
  @config = config
10
10
  @metadata = Metadata::SystemInfo.new(config)
11
+ @container_id = nil
12
+ if
13
+ !@metadata.container.nil? &&
14
+ @metadata.container.key?(:id)
15
+ then
16
+ @container_id = @metadata.container[:id]
17
+ end
18
+
19
+ @real_hostname = Socket.gethostname
11
20
  end
12
21
 
13
22
  attr_reader :config
@@ -20,49 +29,47 @@ module Atatus
20
29
  name: AGENT_NAME,
21
30
  version: VERSION
22
31
  },
23
- # tags: @config.default_tags,
24
- uniqueHostname: Socket.gethostname,
25
32
  hostname: @metadata.hostname,
26
33
  hostId: @metadata.hwinfo.hostid
27
34
  }
28
- unless @metadata.container.nil?
29
- common[:containerId] = @metadata.container[:id] if @metadata.container.key?(:id)
35
+ if !@container_id.nil?
36
+ common[:containerId] = @container_id
30
37
  end
31
38
  common
32
39
  end
33
40
 
41
+ def hostinfo(start_time)
42
+ payload = common()
43
+ payload[:timestamp] = start_time
44
+ payload[:environment] = build_hostinfo_obj()
45
+ payload
46
+ end
47
+
34
48
  def txns(start_time, end_time, data)
35
49
  payload = common()
36
- payload[:startTime] = (start_time.to_f * 1000).to_i
37
- payload[:endTime] = (end_time.to_f * 1000).to_i
50
+ payload[:startTime] = start_time
51
+ payload[:endTime] = end_time
38
52
  payload[:transactions] = build_txns_obj(data)
39
53
  payload
40
54
  end
41
55
 
42
56
  def traces(start_time, end_time, data)
43
57
  payload = common()
44
- payload[:startTime] = (start_time.to_f * 1000).to_i
45
- payload[:endTime] = (end_time.to_f * 1000).to_i
58
+ payload[:startTime] = start_time
59
+ payload[:endTime] = end_time
46
60
  payload[:traces] = build_traces_obj(data)
47
61
  payload
48
62
  end
49
63
 
50
64
  def error_metrics(start_time, end_time, metrics_data, requests_data)
51
65
  payload = common()
52
- payload[:startTime] = (start_time.to_f * 1000).to_i
53
- payload[:endTime] = (end_time.to_f * 1000).to_i
66
+ payload[:startTime] = start_time
67
+ payload[:endTime] = end_time
54
68
  payload[:errorMetrics] = build_error_metrics_obj(metrics_data)
55
69
  payload[:errorRequests] = build_error_requests_obj(requests_data)
56
70
  payload
57
71
  end
58
72
 
59
- def hostinfo(start_time)
60
- payload = common()
61
- payload[:timestamp] = (start_time.to_f * 1000).to_i
62
- payload[:environment] = build_hostinfo_obj()
63
- payload
64
- end
65
-
66
73
  def errors(start_time, end_time, error_data)
67
74
  payload = common()
68
75
  payload[:errors] = build_errors_obj(error_data)
@@ -71,8 +78,8 @@ module Atatus
71
78
 
72
79
  def metrics(start_time, end_time, metric_data)
73
80
  payload = common()
74
- payload[:startTime] = (start_time.to_f * 1000).to_i
75
- payload[:endTime] = (end_time.to_f * 1000).to_i
81
+ payload[:startTime] = start_time
82
+ payload[:endTime] = end_time
76
83
  payload[:ruby] = metric_data
77
84
  payload
78
85
  end
@@ -83,99 +90,45 @@ module Atatus
83
90
  Util.truncate(value)
84
91
  end
85
92
 
86
- def build_request(context)
87
- return unless context
88
- return unless context.request
89
-
90
- request = {
91
- accept: context.request.headers['Accept'],
92
- 'accept-encoding': context.request.headers['Accept-Encoding'],
93
- 'accept-language': context.request.headers['Accept-Language'],
94
- host: context.request.url.hostname,
95
- port: (context.request.url.port).to_i,
96
- method: context.request.method,
97
- userAgent: context.request.headers['User-Agent'],
98
- path: context.request.url.pathname
99
- }
100
- unless context.response.nil?
101
- unless context.response.status_code.nil?
102
- request[:statusCode] = context.response.status_code
103
- end
104
- end
105
- request
93
+ def build_hostinfo_obj()
94
+ environment = {}
95
+ environment[:gems] = build_hostinfo_gems()
96
+ environment[:host] = build_hostinfo_env_host()
97
+ environment[:settings] = build_hostinfo_env_settings()
98
+ environment
106
99
  end
107
100
 
108
- def build_error_requests_obj(requests_data)
109
- error_requests = []
110
- requests_data.each do |v|
111
- error_request = {}
112
- error_request[:name] = v['name']
113
- error_request[:type] = @config.framework_name
114
- error_request[:kind] = "Ruby"
115
- error_request[:request] = build_request(v['context'])
116
- error_requests << error_request
101
+ def build_hostinfo_env_host()
102
+ host_info = {
103
+ cpu: [@metadata.hwinfo.cpuinfo],
104
+ ram: @metadata.hwinfo.meminfo,
105
+ hostname: @real_hostname,
106
+ bootId: @metadata.hwinfo.hostid,
107
+ hostId: @metadata.hwinfo.hostid,
108
+ os: @metadata.osinfo.os,
109
+ kernel: @metadata.osinfo.kernel
110
+ }
111
+ if !@container_id.nil?
112
+ host_info[:containerId] = @container_id
117
113
  end
118
- error_requests
114
+ host_info
119
115
  end
120
116
 
121
- def build_errors_obj(data)
122
- errors = []
123
- data.each do |v|
124
- error = {}
125
- error[:timestamp] = v.timestamp
126
- unless v.transaction.nil?
127
- error[:transaction] = v.transaction[:name] if v.transaction.key?(:name)
128
- end
129
- error[:request] = build_request(v.context)
130
- error[:exceptions] = []
131
- exception = {}
132
- exception[:class] = v.exception.type
133
- exception[:message] = v.exception.message
134
- exception[:stacktrace] = []
135
- unless v.exception.stacktrace.nil?
136
- unless v.exception.stacktrace.frames.nil?
137
- v.exception.stacktrace.frames.each do |f|
138
- frame = {}
139
- frame[:f] = f.filename
140
- frame[:m] = f.function
141
- frame[:ln] = f.lineno
142
- if f.library_frame == false
143
- frame[:inp] = true
144
- unless f.context_line.nil?
145
- frame[:code] = []
146
-
147
- unless f.pre_context.nil?
148
- psize = f.pre_context.size
149
- lineno = 0
150
- if f.lineno - psize >= 0
151
- lineno = f.lineno - psize
152
- end
153
- f.pre_context.each do |c|
154
- frame[:code].push([lineno.to_s, c])
155
- lineno += 1
156
- end
157
- end
158
-
159
- frame[:code].push([f.lineno.to_s, f.context_line])
117
+ def build_hostinfo_env_settings()
118
+ {
119
+ agentVersion: VERSION,
120
+ appName: @config.app_name,
121
+ framework: @config.framework_name,
122
+ frameworkVersion: @config.framework_version,
123
+ logLevel: @config.log_level,
124
+ ruby: RUBY_VERSION
125
+ }
126
+ end
160
127
 
161
- unless f.post_context.nil?
162
- psize = f.post_context.size
163
- lineno = f.lineno + 1
164
- f.post_context.each do |c|
165
- frame[:code].push([lineno.to_s, c])
166
- lineno += 1
167
- end
168
- end
169
- end
170
- end
171
- exception[:stacktrace] << frame
172
- end
173
- end
174
- end
175
- error[:exceptions] << exception
176
- errors << error
177
- end
178
- errors
128
+ def build_hostinfo_gems()
129
+ Bundler.rubygems.all_specs.map {|spec| [spec.name, spec.version.to_s]}.sort.to_h
130
+ rescue => e
131
+ {}
179
132
  end
180
133
 
181
134
  def build_metric(name, value)
@@ -190,6 +143,38 @@ module Atatus
190
143
  }
191
144
  end
192
145
 
146
+ def build_request(context)
147
+ request = {}
148
+ return request unless context
149
+
150
+ if !context.request.nil?
151
+ r = context.request
152
+ request[:method] = r.method if !r.method.nil?
153
+ if !r.headers.nil?
154
+ h = r.headers
155
+ request[:accept] = h['Accept'] if h.key?('Accept')
156
+ request[:'accept-encoding'] = h['Accept-Encoding'] if h.key?('Accept-Encoding')
157
+ request[:'accept-language'] = h['Accept-Language'] if h.key?('Accept-Language')
158
+ request[:'userAgent'] = h['User-Agent'] if h.key?('User-Agent')
159
+ end
160
+
161
+ if !r.url.nil?
162
+ u = r.url
163
+ request[:host] = u.hostname if !u.hostname.nil?
164
+ request[:port] = (u.port).to_i if !u.port.nil?
165
+ request[:path] = u.pathname if !u.pathname.nil?
166
+ end
167
+ end
168
+
169
+ if !context.response.nil?
170
+ if !context.response.status_code.nil?
171
+ request[:statusCode] = context.response.status_code
172
+ end
173
+ end
174
+
175
+ request
176
+ end
177
+
193
178
  def build_txns_obj(data)
194
179
  txns = []
195
180
  data.each do |t, v|
@@ -207,49 +192,60 @@ module Atatus
207
192
  def build_traces_obj(data)
208
193
  traces = []
209
194
  data.each do |txn|
195
+ if
196
+ txn.name.nil? ||
197
+ txn.timestamp.nil? ||
198
+ txn.duration.nil?
199
+ then
200
+ next
201
+ end
202
+
210
203
  trace = {}
211
204
  trace[:name] = txn.name
212
- trace[:type] = @config.framework_name
213
- trace[:kind] = "Ruby"
205
+ trace[:type] = @config.framework_name || AGENT_NAME
206
+ trace[:kind] = AGENT_NAME
214
207
  trace[:start] = txn.timestamp
215
208
  trace[:duration] = Util.ms(txn.duration)
216
- trace[:request] = build_request(txn.context)
209
+ if !txn.context.nil?
210
+ trace[:request] = build_request(txn.context)
211
+ end
217
212
  trace[:entries] = []
218
213
  trace[:funcs] = []
219
214
  i = 0
220
215
 
221
- unless txn.spans.nil?
222
- unless txn.spans.empty?
223
- txn.spans.each do |span|
224
- entry = {}
225
- entry[:lv] = 1
226
- if span.timestamp >= txn.timestamp
227
- entry[:so] = Util.ms(span.timestamp - txn.timestamp)
228
- else
229
- entry[:so] = 0
230
- end
231
- entry[:du] = Util.ms(span.duration)
232
- entry[:ly] = {}
233
- entry[:ly][:name] = span.name
234
- entry[:ly][:type] = Atatus::Collector::Layer.span_type(span.subtype)
235
- entry[:ly][:kind] = Atatus::Collector::Layer.span_kind(span.type)
236
- unless span.context.nil?
237
- unless span.context.db.nil?
238
- unless span.context.db.statement.nil?
239
- entry[:dt] = {}
240
- entry[:dt][:query] = span.context.db.statement
241
- end
242
- end
243
- end
244
- trace[:entries] << entry
245
- func_index = trace[:funcs].index(span.name)
246
- if func_index.nil?
247
- trace[:funcs] << span.name
248
- func_index = i
249
- i = i + 1
250
- end
251
- entry[:i] = func_index
216
+ if
217
+ !txn.spans.nil? &&
218
+ !txn.spans.empty?
219
+ then
220
+ txn.spans.each do |span|
221
+ entry = {}
222
+ entry[:lv] = 1
223
+ if span.timestamp >= txn.timestamp
224
+ entry[:so] = Util.ms(span.timestamp - txn.timestamp)
225
+ else
226
+ entry[:so] = 0
252
227
  end
228
+ entry[:du] = Util.ms(span.duration)
229
+ entry[:ly] = {}
230
+ entry[:ly][:name] = span.name
231
+ entry[:ly][:type] = Atatus::Collector::Layer.span_type(span.subtype)
232
+ entry[:ly][:kind] = Atatus::Collector::Layer.span_kind(span.type)
233
+ if
234
+ !span.context.nil? &&
235
+ !span.context.db.nil? &&
236
+ !span.context.db.statement.nil?
237
+ then
238
+ entry[:dt] = {}
239
+ entry[:dt][:query] = span.context.db.statement
240
+ end
241
+ trace[:entries] << entry
242
+ func_index = trace[:funcs].index(span.name)
243
+ if func_index.nil?
244
+ trace[:funcs] << span.name
245
+ func_index = i
246
+ i = i + 1
247
+ end
248
+ entry[:i] = func_index
253
249
  end
254
250
  end
255
251
 
@@ -263,55 +259,123 @@ module Atatus
263
259
  metrics_data.each do |t, v|
264
260
  error_metric = {}
265
261
  error_metric[:name] = t
266
- error_metric[:type] = @config.framework_name
267
- error_metric[:kind] = "Ruby"
262
+ error_metric[:type] = @config.framework_name || AGENT_NAME
263
+ error_metric[:kind] = AGENT_NAME
268
264
  error_metric[:statusCodes] = v
269
265
  error_metrics << error_metric
270
266
  end
271
267
  error_metrics
272
268
  end
273
269
 
274
- def build_hostinfo_obj()
275
- environment = {}
276
- environment[:gems] = get_gem_list
277
- environment[:host] = build_hostinfo_env_host()
278
- environment[:settings] = build_hostinfo_env_settings()
279
- environment
280
- end
281
-
282
- def build_hostinfo_env_host()
283
- host_info = {
284
- cpu: [@metadata.hwinfo.cpuinfo],
285
- ram: @metadata.hwinfo.meminfo,
286
- hostname: @metadata.hostname,
287
- uniqueHostname: Socket.gethostname,
288
- bootId: @metadata.hwinfo.hostid,
289
- hostId: @metadata.hwinfo.hostid,
290
- os: @metadata.osinfo.os,
291
- kernel: @metadata.osinfo.kernel
292
- }
293
- unless @metadata.container.nil?
294
- host_info[:containerId] = @metadata.container[:id] if @metadata.container.key?(:id)
270
+ def build_error_requests_obj(requests_data)
271
+ error_requests = []
272
+ requests_data.each do |v|
273
+ if
274
+ !v.key?('name') ||
275
+ !v.key?('context')
276
+ then
277
+ next
278
+ end
279
+ error_request = {}
280
+ error_request[:name] = v['name']
281
+ error_request[:type] = @config.framework_name || AGENT_NAME
282
+ error_request[:kind] = AGENT_NAME
283
+ error_request[:request] = build_request(v['context'])
284
+ error_requests << error_request
295
285
  end
296
- host_info
286
+ error_requests
297
287
  end
298
288
 
299
- def build_hostinfo_env_settings()
300
- {
301
- agentVersion: VERSION,
302
- appName: @config.app_name,
303
- framework: @config.framework_name,
304
- frameworkVersion: @config.framework_version,
305
- logLevel: @config.log_level,
306
- ruby: RUBY_VERSION
307
- }
308
- end
289
+ def build_errors_obj(data)
290
+ errors = []
291
+ data.each do |v|
292
+ if
293
+ v.timestamp.nil? ||
294
+ v.exception.nil?
295
+ then
296
+ next
297
+ end
309
298
 
310
- def get_gem_list()
311
- Bundler.rubygems.all_specs.map {|spec| [spec.name, spec.version.to_s]}.sort.to_h
312
- rescue => e
313
- # logger.warn("Couldn't fetch Gem information: #{e.message}")
314
- {}
299
+ if
300
+ v.exception.type.nil? ||
301
+ v.exception.message.nil?
302
+ then
303
+ next
304
+ end
305
+
306
+ error = {}
307
+ error[:timestamp] = v.timestamp
308
+
309
+ if
310
+ !v.transaction.nil? &&
311
+ v.transaction.key?(:name)
312
+ then
313
+ error[:transaction] = v.transaction[:name]
314
+ end
315
+
316
+ if !v.context.nil?
317
+ error[:request] = build_request(v.context)
318
+ end
319
+ error[:exceptions] = []
320
+ exception = {}
321
+ exception[:class] = v.exception.type
322
+ exception[:message] = v.exception.message
323
+ exception[:stacktrace] = []
324
+ if
325
+ !v.exception.stacktrace.nil? &&
326
+ !v.exception.stacktrace.frames.nil?
327
+ then
328
+ v.exception.stacktrace.frames.each do |f|
329
+ if
330
+ f.filename.nil? ||
331
+ f.function.nil? ||
332
+ f.lineno.nil? ||
333
+ f.library_frame.nil?
334
+ then
335
+ next
336
+ end
337
+
338
+ frame = {}
339
+ frame[:f] = f.filename
340
+ frame[:m] = f.function
341
+ frame[:ln] = f.lineno
342
+ if f.library_frame == false
343
+ frame[:inp] = true
344
+ if !f.context_line.nil?
345
+ frame[:code] = []
346
+
347
+ if !f.pre_context.nil?
348
+ psize = f.pre_context.size
349
+ lineno = 0
350
+ if f.lineno - psize >= 0
351
+ lineno = f.lineno - psize
352
+ end
353
+ f.pre_context.each do |c|
354
+ frame[:code].push([lineno.to_s, c])
355
+ lineno += 1
356
+ end
357
+ end
358
+
359
+ frame[:code].push([f.lineno.to_s, f.context_line])
360
+
361
+ if !f.post_context.nil?
362
+ psize = f.post_context.size
363
+ lineno = f.lineno + 1
364
+ f.post_context.each do |c|
365
+ frame[:code].push([lineno.to_s, c])
366
+ lineno += 1
367
+ end
368
+ end
369
+ end
370
+ end
371
+ exception[:stacktrace] << frame
372
+ end
373
+ end
374
+ error[:exceptions] << exception
375
+ errors << error
376
+ end
377
+ errors
315
378
  end
379
+
316
380
  end
317
381
  end
@@ -0,0 +1,58 @@
1
+
2
+ module Atatus
3
+ module Collector
4
+ class Layer
5
+ def initialize(type, kind, duration)
6
+ @type = type
7
+ @kind = kind
8
+ @count = 1
9
+ @min = duration
10
+ @max = duration
11
+ @total = duration
12
+ end
13
+
14
+ attr_reader :type, :kind, :count, :min, :max, :total
15
+ attr_accessor :id, :pid
16
+
17
+ def aggregate!(duration)
18
+ @count += 1
19
+ @min = duration if duration < @min
20
+ @max = duration if duration > @max
21
+ @total += duration
22
+ end
23
+
24
+ def self.span_kind(value)
25
+ return KINDS.fetch(value, value)
26
+ end
27
+
28
+ def self.span_type(value)
29
+ return TYPES.fetch(value, value)
30
+ end
31
+
32
+ private
33
+ KINDS = {
34
+ 'db' => 'Database',
35
+ 'cache' => 'Database',
36
+ 'ext' => 'Remote',
37
+ 'websocket' => 'Remote',
38
+ 'template' => 'Template'
39
+ }.freeze
40
+
41
+ TYPES = {
42
+ 'mysql' => 'MySQL',
43
+ 'mysql2' => 'MySQL',
44
+ 'postgresql' => 'Postgres',
45
+ 'mssql' => 'MS SQL',
46
+ 'mongodb' => 'MongoDB',
47
+ 'redis' => 'Redis',
48
+ 'graphql' => 'GraphQL',
49
+ 'elasticsearch' => 'Elasticsearch',
50
+ 'cassandra' => 'Cassandra',
51
+ 'http' => 'External Requests',
52
+ 'http2' => 'External Requests',
53
+ 'http_rb' => 'External Requests',
54
+ 'net_http' => 'External Requests'
55
+ }.freeze
56
+ end
57
+ end
58
+ end
@@ -2,11 +2,10 @@ require 'json'
2
2
  require 'thread'
3
3
  require 'net/http'
4
4
  require 'atatus/collector/builder'
5
- require 'json'
6
5
 
7
6
  module Atatus
8
7
  class BaseTransport
9
- # include Logging
8
+ include Logging
10
9
 
11
10
  TXN_ENDPOINT = '/track/apm/txn'.freeze
12
11
  TRACE_ENDPOINT = '/track/apm/trace'.freeze
@@ -15,23 +14,32 @@ module Atatus
15
14
  ERROR_METRIC_ENDPOINT = "/track/apm/error_metric".freeze
16
15
  METRIC_ENDPOINT = "/track/apm/metric".freeze
17
16
 
18
- def initialize(config, hostname, port)
17
+ def initialize(config)
19
18
  @config = config
20
- @hostname = hostname
21
- @port = port
22
19
 
23
- @builder = Atatus::Builder.new(config)
20
+ @notify_host = @config.notify_host
21
+ @uri = URI(@notify_host)
22
+ if not @uri.kind_of?(URI::HTTPS) and not @uri.kind_of?(URI::HTTP)
23
+ @notify_host = "https://apm-rx.atatus.com"
24
+ @uri = URI(@notify_host)
25
+ end
24
26
 
27
+ @builder = Atatus::Builder.new(config)
25
28
  @headers = {}
26
29
  @headers['Content-Type'] = "application/json"
27
30
  end
28
31
 
29
- def txn(start_time, end_time, data)
32
+ def hostinfo(start_time)
33
+ payload = @builder.hostinfo(start_time)
34
+ post(HOSTINFO_ENDPOINT, payload)
35
+ end
36
+
37
+ def txns(start_time, end_time, data)
30
38
  payload = @builder.txns(start_time, end_time, data)
31
39
  post(TXN_ENDPOINT, payload)
32
40
  end
33
41
 
34
- def trace(start_time, end_time, data)
42
+ def traces(start_time, end_time, data)
35
43
  payload = @builder.traces(start_time, end_time, data)
36
44
  post(TRACE_ENDPOINT, payload)
37
45
  end
@@ -41,11 +49,6 @@ module Atatus
41
49
  post(ERROR_METRIC_ENDPOINT, payload)
42
50
  end
43
51
 
44
- def hostinfo(start_time)
45
- payload = @builder.hostinfo(start_time)
46
- post(HOSTINFO_ENDPOINT, payload)
47
- end
48
-
49
52
  def errors(start_time, end_time, error_data)
50
53
  payload = @builder.errors(start_time, end_time, error_data)
51
54
  post(ERROR_ENDPOINT, payload)
@@ -59,14 +62,51 @@ module Atatus
59
62
  private
60
63
 
61
64
  def post(endpoint, data)
62
- request = Net::HTTP::Post.new(endpoint, @headers)
63
- request.body = ::JSON.dump(data)
64
- http = Net::HTTP.new(@hostname, @port)
65
- http.use_ssl = true
66
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
67
- response = http.start { |http| http.request(request) }
68
- rescue StandardError => e
69
- puts "Sending to Atatus backend failed: ", e.inspect, response # To fix
65
+ # puts ::JSON.pretty_generate(data, :max_nesting => false)
66
+ if @blocked == true and endpoint != HOSTINFO_ENDPOINT
67
+ return
68
+ end
69
+
70
+ begin
71
+ request = Net::HTTP::Post.new(endpoint, @headers)
72
+ request.body = ::JSON.dump(data)
73
+ http = Net::HTTP.new(@uri.host, @uri.port)
74
+ http.use_ssl = true
75
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
76
+ response = http.start { |http| http.request(request) }
77
+ rescue SystemCallError, Timeout::Error, EOFError, SocketError => e
78
+ puts "Atatus transport [#{@notify_host}#{endpoint}] failed with exception: #{e}"
79
+ end
80
+
81
+ if @blocked == true
82
+ @blocked = false
83
+ end
84
+
85
+ case response
86
+ when Net::HTTPSuccess
87
+ true
88
+ when Net::HTTPBadRequest
89
+ if not response.body
90
+ error format('Atatus transport status 400, failed without content')
91
+ return
92
+ end
93
+
94
+ resp = JSON.parse response.body
95
+ if resp
96
+ if resp.key?(:blocked)
97
+ @blocked = resp[:blocked]
98
+ if @blocked == true
99
+ if resp.key?(:errorMessage)
100
+ error format('Atatus blocked from sending data as: %s', resp[:errorMessage])
101
+ return
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ error format('Atatus transport status 400, failed with content: %s', response.message)
108
+ else
109
+ end
70
110
  end
71
111
  end
72
112
  end
@@ -22,9 +22,10 @@ module Atatus
22
22
  # rubocop:disable Metrics/LineLength, Layout/ExtraSpacing
23
23
  option :app_name, type: :string
24
24
  option :license_key, type: :string
25
+ option :notify_host, type: :string, default: 'https://apm-rx.atatus.com'
25
26
  option :trace_threshold, type: :int, default: 2000
26
27
  option :config_file, type: :string, default: 'config/atatus.yml'
27
- option :server_url, type: :string, default: 'http://localhost:8200'
28
+ option :server_url, type: :string, default: ''
28
29
  option :secret_token, type: :string
29
30
 
30
31
  option :active, type: :bool, default: true
@@ -92,7 +92,7 @@ module Atatus
92
92
  def read_from_meminfo!
93
93
  return unless File.exist?(LINUX_MEMINFO_PATH)
94
94
  meminfo = File.read(LINUX_MEMINFO_PATH)
95
- self.meminfo_total = meminfo.scan(TOTAL_MEMORY_REGEX).flatten.first.to_i
95
+ self.meminfo_total = (meminfo.scan(TOTAL_MEMORY_REGEX).flatten.first.to_i) * 1024 # to bytes
96
96
  end
97
97
  # rubocop:enable Metrics/MethodLength, Metrics/PerceivedComplexity
98
98
  # rubocop:enable Metrics/CyclomaticComplexity, Metrics/AbcSize
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Atatus
4
- VERSION = '1.0.1'
4
+ VERSION = '1.0.2'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: atatus
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Atatus
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-11-13 00:00:00.000000000 Z
11
+ date: 2019-11-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -74,6 +74,7 @@ files:
74
74
  - lib/atatus/central_config/cache_control.rb
75
75
  - lib/atatus/collector/base.rb
76
76
  - lib/atatus/collector/builder.rb
77
+ - lib/atatus/collector/layer.rb
77
78
  - lib/atatus/collector/transport.rb
78
79
  - lib/atatus/config.rb
79
80
  - lib/atatus/config/bytes.rb