Index: appengine_module/gae_ts_mon/interface.py |
diff --git a/appengine_module/gae_ts_mon/interface.py b/appengine_module/gae_ts_mon/interface.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e9590b7f05ff2ef16ea191f843a8461472852fa8 |
--- /dev/null |
+++ b/appengine_module/gae_ts_mon/interface.py |
@@ -0,0 +1,124 @@ |
+# Copyright 2015 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+"""Classes representing the monitoring interface for tasks or devices. |
+ |
+Usage: |
+ # symlink appengine/modules/gae_ts_mon into the top level directory |
+ # of your appengine app |
+ |
+ import gae_ts_mon |
+ |
+ # Sets up default target |
+ gae_ts_mon.initialize(job_name='job', instance='12d2e1', |
Sergey Berezin
2015/08/18 00:40:38
nit: is the instance value supposed to be a consta
jshu
2015/08/18 01:15:55
should be a real instance id, which is an int. fix
Sergey Berezin
2015/08/18 17:31:38
Two comments then:
1. In README, let's use <insta
jshu
2015/08/20 00:23:34
fixed
|
+ service_name='service', endpoint='endpoint') |
+ |
+ # Will use the default Target set up with initialize |
+ count_metric = gae_ts_mon.CounterMetric('my/metric/name', fields={}) |
+ count_metric.set(0) |
+ for x in range(100): |
+ count_metric.increment() |
+ |
+ # Use a custom Target: |
+ t = ts_mon.TaskTarget('service', 'job', 'region', 'host') |
+ g_metric = ts_mon.GaugeMetric('/my/metric/name2', fields={'asdf': 'qwer'}, |
+ target=t) |
Sergey Berezin
2015/08/18 00:40:37
nit: indentation.
|
+ g_metric.set(5) |
+ |
+ # Flush (send metrics to monarch) |
+ gae_ts_mon.flush() |
Sergey Berezin
2015/08/18 00:40:38
nit: I believe this should be called automatically
jshu
2015/08/18 01:15:55
called from a cron job. leaving that in to give th
Sergey Berezin
2015/08/18 17:31:38
I'd still mention what you said in the comment; ot
|
+ |
+""" |
+ |
+import logging |
+import os |
+import random |
+import threading |
+import time |
+ |
+from proto import metrics_pb2 |
+ |
+from common import errors |
+ |
+# The maximum number of MetricsData messages to include in each HTTP request. |
+# MetricsCollections larger than this will be split into multiple requests. |
+METRICS_DATA_LENGTH_LIMIT = 5000 |
Sergey Berezin
2015/08/18 00:40:38
nit: we are reducing this to 1000 - see https://co
|
+ |
+ |
+class State(object): |
+ """Package-level state is stored here so that it is easily accessible. |
+ |
+ Configuration is kept in this one object at the global level so that all |
+ libraries in use by the same tool or service can all take advantage of the |
+ same configuration. |
+ """ |
+ |
+ def __init__(self): |
+ # The Monitor object that will be used to send all metrics. |
+ self.global_monitor = None |
+ # The Target object that will be paired with all metrics that don't supply |
+ # their own. |
+ self.default_target = None |
+ # The flush mode being used to control when metrics are pushed. |
+ self.flush_mode = None |
+ # All metrics created by this application. |
+ self.metrics = set() |
+ |
+state = State() |
+ |
+ |
+def send(metric): |
+ """Send a single metric to the monitoring api. |
+ |
+ This is called automatically by Metric.set - you don't need to call it |
+ manually. |
+ """ |
+ if state.flush_mode != 'all': |
+ return |
+ |
+ if not state.global_monitor: |
+ raise errors.MonitoringNoConfiguredMonitorError(metric._name) |
+ |
+ proto = metrics_pb2.MetricsCollection() |
+ metric.serialize_to(proto, default_target=state.default_target) |
+ state.global_monitor.send(proto) |
+ |
+ |
+def flush(): |
+ """Send all metrics that are registered in the application.""" |
+ if not state.global_monitor: |
+ raise errors.MonitoringNoConfiguredMonitorError(None) |
+ |
+ proto = metrics_pb2.MetricsCollection() |
+ |
+ def loop_action(proto): |
+ if len(proto.data) >= METRICS_DATA_LENGTH_LIMIT: |
+ state.global_monitor.send(proto) |
+ del proto.data[:] |
+ |
+ for metric in state.metrics: |
+ metric.serialize_to(proto, default_target=state.default_target, |
+ loop_action=loop_action) |
+ |
+ state.global_monitor.send(proto) |
+ |
+ |
+def register(metric): |
+ """Adds the metric to the list of metrics sent by flush(). |
+ |
+ This is called automatically by Metric's constructor. |
+ """ |
+ # If someone is registering the same metric object twice, that's okay, but |
+ # registering two different metric objects with the same metric name is not. |
+ if metric in state.metrics: |
+ return |
+ if any([metric._name == m._name for m in state.metrics]): |
+ raise errors.MonitoringDuplicateRegistrationError(metric._name) |
+ |
+ state.metrics.add(metric) |
+ |
+ |
+def unregister(metric): |
+ """Removes the metric from the list of metrics sent by flush().""" |
+ state.metrics.remove(metric) |