atatus 1.0.1 → 1.0.2

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