mobile_metrics 0.0.5 → 0.0.6

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: 9a6fa6adfc5e4ae4346d9657ba05ab248c2a308513e70f6d56543892b1d6ebff
4
- data.tar.gz: 1dc82bbd8dc653c5b9b5bc00dd2a01a26749b2eb3f3003781e3429628da462b0
3
+ metadata.gz: 3517102712246e11c22fe0bdf3484fbae5f6fd80d88895da29e7075de7c2a6fb
4
+ data.tar.gz: 151b0833679f05c680cfbe81c74641794b103056e1627af4ee1c16abad60e66f
5
5
  SHA512:
6
- metadata.gz: cd93876206d714b35ba0a5d515aa866319f73f1463b26e60a34418404b38dd8762c4b3eb585b7162a3ef406dd7fd1b75d561f683a9a26da275444af3eb658a20
7
- data.tar.gz: fe8426f6a47bbef1c0b285e295728634361a920915d848e728d907b397940cedb2a5af5a0b877b87ef35a6e968c303f23a728e646a2a9dad2ec10079f9f05006
6
+ metadata.gz: e26d2c6e2ffc1f5b24b4e32edc0519bf575660296f4c711df8ae2158e41173da1b576de4fb96ecade1b305310f1fcd008f7f3d0caec1caca1776e8d3d159eb83
7
+ data.tar.gz: c38dde7199e4637cde07fbb14506a045761312ddf724258e1433714946fa4485c6dcebd22f5c00e2f553bc63646679e0273b5feff80aa566249d014a5f92cac2
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mobile_metrics (0.0.4)
4
+ mobile_metrics (0.0.6)
5
5
  ox (~> 2.0)
6
6
 
7
7
  GEM
@@ -1,5 +1,5 @@
1
- require "mobile_metrics/version"
2
- require "mobile_metrics/junit"
1
+ require 'mobile_metrics/version'
2
+ require 'mobile_metrics/junit'
3
3
 
4
4
  require 'time'
5
5
  require 'json'
@@ -8,18 +8,18 @@ require 'fileutils'
8
8
  require 'pathname'
9
9
 
10
10
  # Metric types
11
- METRIC_TIMER = 'x.mobile_metric.timer'
12
- METRIC_COUNTER = 'x.mobile_metric.counter'
13
- METRIC_RATIO = 'x.mobile_metric.ratio'
14
- METRIC_SIZE = 'x.mobile_metric.size'
11
+ METRIC_TIMER = 'x.mobile_metric.timer'.freeze
12
+ METRIC_COUNTER = 'x.mobile_metric.counter'.freeze
13
+ METRIC_RATIO = 'x.mobile_metric.ratio'.freeze
14
+ METRIC_SIZE = 'x.mobile_metric.size'.freeze
15
15
 
16
16
  # Misc Constants
17
- UNKNOWN = 'UNKNOWN'
18
- LOG_PREFIX = 'CI-METRICS:'
19
- LOGGING_DIR = '/tmp/cimetricslogs'
20
- UPLOAD_ENV_VAR = 'MOBILE_METRICS_UPLOAD_LOCATION'
17
+ UNKNOWN = 'UNKNOWN'.freeze
18
+ LOG_PREFIX = 'CI-METRICS:'.freeze
19
+ LOGGING_DIR = '/tmp/cimetricslogs'.freeze
20
+ UPLOAD_ENV_VAR = 'MOBILE_METRICS_UPLOAD_LOCATION'.freeze
21
21
 
22
- module MobileMetrics
22
+ module MobileMetrics
23
23
  class Log
24
24
 
25
25
  @@in_progress_timers = {}
@@ -27,63 +27,63 @@ module MobileMetrics
27
27
  @@env_values = nil
28
28
  @@verbose = false
29
29
  @@upload_location = nil
30
-
30
+
31
31
  #========================================
32
32
  # Configuration
33
33
  #=========================================
34
-
34
+
35
35
  # Named params, with ability to override / leave out stuff we don't care about.
36
36
  def self.set_default_values(
37
37
  # Required params
38
38
  project:,
39
39
  # These params either aren't required, or have sensible defaults.
40
- level: "info",
41
- platform: "iOS",
42
- buildUrl: ENV['BUILD_URL'],
43
- gitUrl: ENV['GIT_URL'],
44
- gitBranch: ENV['DOTCI_BRANCH'],
45
- gitSha: ENV['DOTCI_SHA'],
40
+ level: "info",
41
+ platform: "iOS",
42
+ buildUrl: ENV['BUILD_URL'],
43
+ gitUrl: ENV['GIT_URL'],
44
+ gitBranch: ENV['DOTCI_BRANCH'],
45
+ gitSha: ENV['DOTCI_SHA'],
46
46
  # no sensible defaults for these, we'll strip them later
47
- buildType: UNKNOWN,
47
+ buildType: UNKNOWN,
48
48
  brand: UNKNOWN,
49
49
  # Controls log levels within this class
50
50
  verbose: false,
51
51
  # Upload location for logs
52
52
  upload_location: nil
53
- )
54
-
53
+ )
54
+
55
55
  #TODO: May be able to support overridding these at some point in the future.
56
56
  # It won't be threadsafe, but our builds aren't parallelized at the build script level anyway.
57
57
  if @@env_values
58
58
  print 'Can only override default values once! Aborting!'
59
59
  return
60
60
  end
61
-
62
- if project.nil?
61
+
62
+ if project.nil?
63
63
  print 'Project value for logging MUST be non-nil, failing build.'
64
64
  exit 14
65
65
  return
66
66
  end
67
-
67
+
68
68
  @@verbose = verbose
69
-
69
+
70
70
  # Upload location
71
71
  @@upload_location = (upload_location || ENV[UPLOAD_ENV_VAR])
72
- if @@upload_location.nil?
72
+ if @@upload_location.nil?
73
73
  print 'Upload location value for logging MUST not be nil, exiting.'
74
74
  exit 15
75
75
  return
76
76
  end
77
-
77
+
78
78
  # Create the logging dir + filename
79
79
  FileUtils.mkdir_p(LOGGING_DIR)
80
80
  filename = "metrics_#{project}_#{build_number}.log"
81
81
  @@log_file = Pathname.new(LOGGING_DIR) + filename
82
82
  print @@log_file if @@verbose
83
-
83
+
84
84
  # Populate our env values
85
85
  @@env_values = {}
86
-
86
+
87
87
  # Required
88
88
  @@env_values[:project] = project
89
89
  # Optional
@@ -93,131 +93,150 @@ module MobileMetrics
93
93
  @@env_values[:gitBranch] = gitBranch
94
94
  @@env_values[:gitSha] = gitSha
95
95
  @@env_values[:buildUrl] = buildUrl
96
-
96
+
97
97
  #TODO: Should any of these be required?
98
98
  @@env_values[:buildType] = buildType if buildType != UNKNOWN
99
99
  @@env_values[:brand] = brand if brand != UNKNOWN
100
100
  end
101
-
102
- # =begin
103
- # # Sample timer metric
104
- # {
105
- # "time": "<yyyy-MM-ddThh:mm:ss[.sss]Z>",
106
- # "name": "x.mobile_metric.<metric_type>",
107
- # "level": "info",
108
- # "data": {
109
- # "name": "<metric_name>",
110
- # … // additional data per metric type
111
- # }
112
- # "context": {
113
- # "platform": "<ios_or_android>",
114
- # "project": "<project_name>",
115
- # "gitUrl": "<git_url>",
116
- # "gitBranch": "<git_branch>",
117
- # "gitSha": "<git_sha>",
118
- # "buildType": "<build_type>",
119
- # "brand": "<brand>",
120
- # "host": "<machine_dns_name_or_device_name>",
121
- # "processId": "<process_id_or_build_number>"
122
- # }
123
- # }
124
- # =end
101
+
102
+ # =begin
103
+ # # Sample timer metric
104
+ # {
105
+ # "time": "<yyyy-MM-ddThh:mm:ss[.sss]Z>",
106
+ # "name": "x.mobile_metric.<metric_type>",
107
+ # "level": "info",
108
+ # "data": {
109
+ # "name": "<metric_name>",
110
+ # … // additional data per metric type
111
+ # }
112
+ # "context": {
113
+ # "platform": "<ios_or_android>",
114
+ # "project": "<project_name>",
115
+ # "gitUrl": "<git_url>",
116
+ # "gitBranch": "<git_branch>",
117
+ # "gitSha": "<git_sha>",
118
+ # "buildType": "<build_type>",
119
+ # "brand": "<brand>",
120
+ # "host": "<machine_dns_name_or_device_name>",
121
+ # "processId": "<process_id_or_build_number>"
122
+ # }
123
+ # }
124
+ # =end
125
125
 
126
126
  #=========================================
127
127
  # Public Logging Methods
128
128
  #=========================================
129
-
129
+
130
130
  def self.log_ratio_metric(name:, ratio:)
131
- overrides = {name: METRIC_RATIO, data: {name: name, ratio: ratio.to_f }}
131
+ overrides = { name: METRIC_RATIO, data: { name: name, ratio: ratio.to_f } }
132
132
  log_partial_metric(overrides)
133
133
  end
134
-
134
+
135
135
  def self.log_counter_metric(name:, count:)
136
- overrides = {name: METRIC_COUNTER, data: {name: name, count: count.to_i }}
136
+ overrides = { name: METRIC_COUNTER, data: {name: name, count: count.to_i } }
137
137
  log_partial_metric(overrides)
138
138
  end
139
-
139
+
140
140
  def self.log_size_metric(name:, sizeInBytes:, filename:, artifactUrl:)
141
- overrides = {name: METRIC_SIZE, data: {name: name, filename: filename, size: sizeInBytes, artifactUrl: artifactUrl }}
141
+ overrides = { name: METRIC_SIZE, data: { name: name, filename: filename, size: sizeInBytes, artifactUrl: artifactUrl } }
142
142
  log_partial_metric(overrides)
143
143
  end
144
-
144
+
145
145
  def self.start_timer_metric(name:)
146
- if @@in_progress_timers.has_key?(name)
146
+ if @@in_progress_timers.has_key?(name)
147
147
  print "WARNING: #{name} already has a running timer, refusing to start a new timer"
148
148
  return
149
149
  end
150
150
  @@in_progress_timers[name] = monotonic_timestamp
151
151
  end
152
-
152
+
153
153
  def self.end_timer_metric(name:)
154
154
  if !@@in_progress_timers.has_key?(name)
155
155
  print "WARNING: #{name} does not have a running timer, the end_timer_metric call has no effect"
156
156
  return
157
157
  end
158
-
158
+
159
159
  # Calculate delta
160
160
  start = @@in_progress_timers[name]
161
161
  now = monotonic_timestamp
162
162
  delta_in_ms = ((now - start) * 1000).to_i
163
-
163
+
164
164
  # remove existing timer
165
165
  @@in_progress_timers.delete(name)
166
-
166
+
167
167
  # log to file
168
168
  overrides = {name: METRIC_TIMER, data: {name: name, duration: delta_in_ms }}
169
169
  log_partial_metric(overrides)
170
170
  end
171
-
171
+
172
172
  # Block based timer. This is the recommended way to timer operations.
173
- def self.time(name:, &block)
173
+ def self.time(name:, &_block)
174
174
  self.start_timer_metric(name: name)
175
175
  yield
176
176
  self.end_timer_metric(name: name)
177
177
  end
178
-
179
- def self.upload_logs()
180
- # Already called upload logs before, second time is a no-op
181
- if @@log_file.nil? || !@@log_file.file?
182
- print "WARN: Log file is empty or doesn't exist. Was upload_logs called previously?"
183
- return
178
+
179
+ def self.log_unit_test_count_from_file(junit_file:, fail_build_on_file_not_found: false)
180
+ if !File.exist?(junit_file)
181
+ print "WARN: JUnit file #{junit_file} does not exist on disk"
182
+ exit 16 if fail_build_on_file_not_found
183
+ return if !fail_build_on_file_not_found
184
+ end
185
+
186
+ parser = MobileMetrics::JunitParser.new
187
+ parser.parse(junit_file)
188
+ unit_test_count = parser.tests.count
189
+
190
+ # Only log values > 0 to avoid any parser issues.
191
+ if unit_test_count == 0
192
+ print 'WARN: Parsed unit test count is zero, avoiding logging.'
193
+ else
194
+ self.log_counter_metric(name: 'unit_tests_count', count: unit_test_count)
184
195
  end
185
-
196
+ end
197
+
198
+ def self.upload_logs
199
+ # Already called upload logs before, second time is a no-op
200
+ if @@log_file.nil? || !@@log_file.file?
201
+ print 'WARN: Log file is empty or doesn\'t exist. Was upload_logs called previously?'
202
+ return
203
+ end
204
+
186
205
  # Skip uploads for local dev machines
187
- if !should_upload_logs()
206
+ if !should_upload_logs
188
207
  print 'Detected local machine, refusing to upload build metrics. Removing intermediate log file'
189
- remove_log_file()
208
+ remove_log_file
190
209
  return
191
210
  end
192
-
211
+
193
212
  # Warn for any open timers
194
- if @@in_progress_timers.size > 0
195
- @@in_progress_timers.each { |k,v|
213
+ if @@in_progress_timers.size > 0
214
+ @@in_progress_timers.each { |k, _v|
196
215
  print "WARN: Timer not closed when upload_logs was called: #{k}"
197
216
  }
198
217
  end
199
-
200
- # Upload
201
- upload_log_file()
202
-
218
+
219
+ # Upload
220
+ upload_log_file
221
+
203
222
  # Remove log file
204
- remove_log_file()
223
+ remove_log_file
205
224
  end
206
-
207
- def self.log_file()
225
+
226
+ def self.log_file
208
227
  @@log_file
209
228
  end
210
-
211
- def self.upload_location()
229
+
230
+ def self.upload_location
212
231
  @@upload_location
213
232
  end
214
-
233
+
215
234
  # Tears down logging instance, removes any intermediate log files.
216
235
  # Does not finalize or upload any in-progress logs.
217
236
  # To use the logger again, you'll have to call set_default_values before any other methods.
218
- def self.reset()
219
- if @@log_file
220
- remove_log_file()
237
+ def self.reset
238
+ if @@log_file
239
+ remove_log_file
221
240
  end
222
241
  @@log_file = nil
223
242
  @@env_values = nil
@@ -225,15 +244,15 @@ module MobileMetrics
225
244
  @@verbose = false
226
245
  @@upload_location = nil
227
246
  end
228
-
229
- def self.remove_all_log_files()
230
- FileUtils.rm_rf(LOGGING_DIR)
247
+
248
+ def self.remove_all_log_files
249
+ FileUtils.rm_rf(LOGGING_DIR)
231
250
  end
232
-
251
+
233
252
  private
234
253
 
235
254
  # Creates a stubbed metric with common fields
236
- def self.default_metric()
255
+ def self.default_metric
237
256
  if !@@env_values
238
257
  print 'default_metric called before env_values initialized, aborting!'
239
258
  exit 12
@@ -251,25 +270,25 @@ module MobileMetrics
251
270
  buildUrl: @@env_values[:buildUrl],
252
271
  host: hostname(),
253
272
  processId: build_number()
254
- }.select { |k,v| !v.nil? }
273
+ }.select { |k,v| !v.nil? }
255
274
  {time: timestamp(), level:@@env_values[:level], context: context}
256
275
  end
257
-
276
+
258
277
  def self.append_to_log(value)
259
278
  # Create the log file if it doesn't exist
260
279
  if !@@log_file.file?
261
- FileUtils.touch(@@log_file)
280
+ FileUtils.touch(@@log_file)
262
281
  end
263
282
  File.open(@@log_file, 'a+') { |f| f.puts(value + "\n") }
264
283
  end
265
-
266
- def self.remove_log_file()
284
+
285
+ def self.remove_log_file
267
286
  FileUtils.rm_rf(@@log_file)
268
287
  end
269
-
270
- def self.upload_log_file()
288
+
289
+ def self.upload_log_file
271
290
  destination = @@upload_location + "metrics_#{@@env_values[:project]}_#{build_number}.log"
272
- verbose_flag = @@verbose ? "-v" : ""
291
+ verbose_flag = @@verbose ? '-v' : ''
273
292
  command = "scp #{verbose_flag} -o StrictHostKeyChecking=no #{@@log_file} #{destination}"
274
293
  # This feels icky
275
294
  print `#{command}`
@@ -279,17 +298,17 @@ module MobileMetrics
279
298
  # Note: This format doesn't currently add milliseconds.
280
299
  # Example: "2018-03-27T21:12:21Z"
281
300
  # Note: DO NOT use this for benchmarking / measuring timers, see monotonic_timestamp instead.
282
- def self.timestamp()
301
+ def self.timestamp
283
302
  Time.now.utc.iso8601.to_s
284
303
  end
285
-
304
+
286
305
  # Returns a floating point monotonic timestamp suitable for timers
287
306
  # Monotonic means we won't ever go back for things like leap seconds
288
307
  # Unit is a floating point in seconds.
289
- def self.monotonic_timestamp()
308
+ def self.monotonic_timestamp
290
309
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
291
310
  end
292
-
311
+
293
312
  # Each metric should call this method.
294
313
  # This method handles merging the metric hash with the default hash,
295
314
  # Generates the JSON with the keys in the right order, then appends to the log.
@@ -300,40 +319,40 @@ module MobileMetrics
300
319
  metric = ordered_hash.to_json
301
320
  append_to_log(metric)
302
321
  end
303
-
322
+
304
323
  # Util method for merging the default hash values and overrides recursively as we want them
305
324
  def self.merge_hashes(default, overrides)
306
- default.merge(overrides) { |key, oldval, newval|
307
- if key == :data || key == :contenxt
325
+ default.merge(overrides) { |key, oldval, newval|
326
+ if key == :data || key == :context
308
327
  oldval.merge(newval)
309
328
  else
310
329
  newval
311
330
  end
312
331
  }
313
332
  end
314
-
333
+
315
334
  # Returns the host name of the machine we are currently running on
316
- def self.hostname()
335
+ def self.hostname
317
336
  ENV['NODE_NAME'] || 'local-dev'
318
337
  end
319
-
320
- def self.build_number()
338
+
339
+ def self.build_number
321
340
  ENV['BUILD_NUMBER']
322
341
  end
323
-
342
+
324
343
  # This method checks against a bunch of default environment variables available through DOTCI
325
344
  # The intent is never to upload timing logs from a local machine, so we make it unlikely that these
326
345
  # env variables are present on a dev's local machine
327
- def self.should_upload_logs()
346
+ def self.should_upload_logs
328
347
  have_workspace = ENV['WORKSPACE'] && !ENV['WORKSPACE'].empty?
329
348
  have_build_number = ENV['BUILD_NUMBER'] && !ENV['BUILD_NUMBER'].empty?
330
349
  have_jenkins_url = ENV['JENKINS_URL'] && !ENV['JENKINS_URL'].empty?
331
350
  have_ci_var = ENV['CI'] && !ENV['CI'].empty?
332
351
  have_workspace && have_build_number && have_jenkins_url && have_ci_var
333
352
  end
334
-
353
+
335
354
  def self.print(val)
336
355
  puts("#{LOG_PREFIX} #{val}")
337
356
  end
338
357
  end
339
- end
358
+ end
@@ -1,6 +1,5 @@
1
1
 
2
2
  module MobileMetrics
3
-
4
3
  # Adapted from: https://github.com/orta/danger-junit/blob/master/lib/junit/plugin.rb
5
4
  class JunitParser
6
5
  # All the tests for introspection
@@ -55,23 +54,23 @@ module MobileMetrics
55
54
  failed_suites = suite_root.nodes.select { |suite| suite[:failures].to_i > 0 || suite[:errors].to_i > 0 }
56
55
  failed_tests = failed_suites.map(&:nodes).flatten.select { |node| node.kind_of?(Ox::Element) && node.value == 'testcase' }
57
56
 
58
- @failures = failed_tests.select do |test|
57
+ @failures = failed_tests.select do |test|
59
58
  test.nodes.count > 0
60
59
  end.select do |test|
61
60
  node = test.nodes.first
62
61
  node.kind_of?(Ox::Element) && node.value == 'failure'
63
62
  end
64
63
 
65
- @errors = failed_tests.select do |test|
64
+ @errors = failed_tests.select do |test|
66
65
  test.nodes.count > 0
67
- end.select do |test|
66
+ end.select do |test|
68
67
  node = test.nodes.first
69
68
  node.kind_of?(Ox::Element) && node.value == 'error'
70
69
  end
71
70
 
72
- @skipped = tests.select do |test|
71
+ @skipped = tests.select do |test|
73
72
  test.nodes.count > 0
74
- end.select do |test|
73
+ end.select do |test|
75
74
  node = test.nodes.first
76
75
  node.kind_of?(Ox::Element) && node.value == 'skipped'
77
76
  end
@@ -79,4 +78,4 @@ module MobileMetrics
79
78
  @passes = tests - @failures - @errors - @skipped
80
79
  end
81
80
  end
82
- end
81
+ end
@@ -1,3 +1,3 @@
1
1
  module MobileMetrics
2
- VERSION = "0.0.5"
2
+ VERSION = '0.0.6'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mobile_metrics
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - dbeard
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-01-22 00:00:00.000000000 Z
11
+ date: 2019-07-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ox
@@ -104,7 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
104
104
  version: '0'
105
105
  requirements: []
106
106
  rubyforge_project:
107
- rubygems_version: 2.7.6
107
+ rubygems_version: 2.7.9
108
108
  signing_key:
109
109
  specification_version: 4
110
110
  summary: Mobile logging format library