Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(495)

Side by Side Diff: infra/services/mastermon/pollers.py

Issue 1877953002: mastermon: collect step results count (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Refactoring + tests Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | infra/services/mastermon/test/pollers_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2015 The Chromium Authors. All rights reserved. 1 # Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import collections 5 import collections
6 import copy 6 import copy
7 import json 7 import json
8 import logging 8 import logging
9 import os 9 import os
10 import time 10 import time
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after
154 """Poll a file instead of an endpoint. 154 """Poll a file instead of an endpoint.
155 155
156 The base_url in __init__ must be the file path. The file is assumed 156 The base_url in __init__ must be the file path. The file is assumed
157 to contain JSON objects, one per line. 157 to contain JSON objects, one per line.
158 158
159 The poller will first rotate the file (rename), read it, and delete 159 The poller will first rotate the file (rename), read it, and delete
160 the file. The writer of the file is expected to create a new file if 160 the file. The writer of the file is expected to create a new file if
161 it was rotated or deleted. 161 it was rotated or deleted.
162 """ 162 """
163 endpoint = 'FILE' 163 endpoint = 'FILE'
164 field_keys = ('builder', 'slave', 'result', 'project_id', 'subproject_tag') 164 build_field_keys = ('builder', 'slave', 'result',
165 'project_id', 'subproject_tag')
166 step_field_keys = ('builder', 'slave', 'step_result',
167 'project_id', 'subproject_tag')
168
169 ### These metrics are sent when a build finishes.
165 result_count = ts_mon.CounterMetric('buildbot/master/builders/results/count', 170 result_count = ts_mon.CounterMetric('buildbot/master/builders/results/count',
166 description='Number of items consumed from ts_mon.log by mastermon') 171 description='Number of items consumed from ts_mon.log by mastermon')
167 # A custom bucketer with 12% resolution in the range of 1..10**5, 172 # A custom bucketer with 12% resolution in the range of 1..10**5,
168 # better suited for build cycle times. 173 # better suited for build cycle times.
169 bucketer = ts_mon.GeometricBucketer( 174 bucketer = ts_mon.GeometricBucketer(
170 growth_factor=10**0.05, num_finite_buckets=100) 175 growth_factor=10**0.05, num_finite_buckets=100)
171 cycle_times = ts_mon.CumulativeDistributionMetric( 176 cycle_times = ts_mon.CumulativeDistributionMetric(
172 'buildbot/master/builders/builds/durations', bucketer=bucketer, 177 'buildbot/master/builders/builds/durations', bucketer=bucketer,
173 description='Durations (in seconds) that slaves spent actively doing ' 178 description='Durations (in seconds) that slaves spent actively doing '
174 'work towards builds for each builder') 179 'work towards builds for each builder')
175 pending_times = ts_mon.CumulativeDistributionMetric( 180 pending_times = ts_mon.CumulativeDistributionMetric(
176 'buildbot/master/builders/builds/pending_durations', bucketer=bucketer, 181 'buildbot/master/builders/builds/pending_durations', bucketer=bucketer,
177 description='Durations (in seconds) that the master spent waiting for ' 182 description='Durations (in seconds) that the master spent waiting for '
178 'slaves to become available for each builder') 183 'slaves to become available for each builder')
179 total_times = ts_mon.CumulativeDistributionMetric( 184 total_times = ts_mon.CumulativeDistributionMetric(
180 'buildbot/master/builders/builds/total_durations', bucketer=bucketer, 185 'buildbot/master/builders/builds/total_durations', bucketer=bucketer,
181 description='Total duration (in seconds) that builds took to complete ' 186 description='Total duration (in seconds) that builds took to complete '
182 'for each builder') 187 'for each builder')
183 188
184 pre_test_times = ts_mon.CumulativeDistributionMetric( 189 pre_test_times = ts_mon.CumulativeDistributionMetric(
185 'buildbot/master/builders/builds/pre_test_durations', bucketer=bucketer, 190 'buildbot/master/builders/builds/pre_test_durations', bucketer=bucketer,
186 description='Durations (in seconds) that builds spent before their ' 191 description='Durations (in seconds) that builds spent before their '
187 '"before_tests" step') 192 '"before_tests" step')
188 193
194 ### This metric is sent when a step finishes.
195 step_results_count = ts_mon.CounterMetric(
196 'buildbot/master/builders/steps/results/count',
197 description='Count of step results, per builder')
198
189 def poll(self): 199 def poll(self):
190 LOGGER.info('Collecting results from %s', self._url) 200 LOGGER.info('Collecting results from %s', self._url)
191 201
192 if not os.path.isfile(self._url): 202 if not os.path.isfile(self._url):
193 LOGGER.info('No file found, assuming no data: %s', self._url) 203 LOGGER.info('No file found, assuming no data: %s', self._url)
194 return True 204 return True
195 205
196 try: 206 try:
197 rotated_name = rotated_filename(self._url) 207 rotated_name = rotated_filename(self._url)
198 # Remove the previous rotated file. We keep it on disk after 208 # Remove the previous rotated file. We keep it on disk after
199 # processing for debugging. 209 # processing for debugging.
200 safe_remove(rotated_name) 210 safe_remove(rotated_name)
201 os.rename(self._url, rotated_name) 211 os.rename(self._url, rotated_name)
202 with open(rotated_name, 'r') as f: 212 with open(rotated_name, 'r') as f:
203 for line in f: # pragma: no branch 213 for line in f: # pragma: no branch
204 self.handle_response(json.loads(line)) 214 self.handle_response(json.loads(line))
205 except (ValueError, OSError, IOError) as e: 215 except (ValueError, OSError, IOError) as e:
206 LOGGER.error('Could not collect or send results from %s: %s', 216 LOGGER.error('Could not collect or send results from %s: %s',
207 self._url, e) 217 self._url, e)
208 218
209 # Never return False - we don't know if master is down. 219 # Never return False - we don't know if master is down.
210 return True 220 return True
211 221
212 def handle_response(self, data): 222 def handle_response(self, data):
213 fields = self.fields({k: data.get(k, 'unknown') for k in self.field_keys}) 223 # We handle two cases here: whether the data was generated when a build
214 self.result_count.increment(fields) 224 # finished or when a step finished. We use the content of the json dict to
215 if 'duration_s' in data: 225 # tell the difference.
216 self.cycle_times.add(data['duration_s'], fields) 226
217 if 'pending_s' in data: 227 if 'step_result' in data: # generated when a step finishes
218 self.pending_times.add(data['pending_s'], fields) 228 fields = self.fields({k: data.get(k, 'unknown')
219 if 'total_s' in data: 229 for k in self.step_field_keys})
220 self.total_times.add(data['total_s'], fields) 230 self.step_results_count.increment(fields=fields)
221 if 'pre_test_time_s' in data: 231
222 self.pre_test_times.add(data['pre_test_time_s'], fields) 232 else: # otherwise it's generated after a build finishes
233 fields = self.fields({k: data.get(k, 'unknown')
234 for k in self.build_field_keys})
235 self.result_count.increment(fields)
236 if 'duration_s' in data:
237 self.cycle_times.add(data['duration_s'], fields)
238 if 'pending_s' in data:
239 self.pending_times.add(data['pending_s'], fields)
240 if 'total_s' in data:
241 self.total_times.add(data['total_s'], fields)
242 if 'pre_test_time_s' in data:
243 self.pre_test_times.add(data['pre_test_time_s'], fields)
OLDNEW
« no previous file with comments | « no previous file | infra/services/mastermon/test/pollers_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698