mobile_metrics 0.0.5 → 0.0.6

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