|
1 | 1 | from prometheus_client import CollectorRegistry, Counter, Gauge, generate_latest, CONTENT_TYPE_LATEST
|
| 2 | +from luigi import parameter |
2 | 3 | from luigi.metrics import MetricsCollector
|
| 4 | +from luigi.task import Config |
| 5 | + |
| 6 | + |
| 7 | +class prometheus(Config): |
| 8 | + use_task_family_in_labels = parameter.BoolParameter( |
| 9 | + default=True, parsing=parameter.BoolParameter.EXPLICIT_PARSING |
| 10 | + ) |
| 11 | + task_parameters_to_use_in_labels = parameter.ListParameter(default=[]) |
3 | 12 |
|
4 | 13 |
|
5 | 14 | class PrometheusMetricsCollector(MetricsCollector):
|
6 | 15 |
|
7 |
| - def __init__(self): |
| 16 | + def _generate_task_labels(self, task): |
| 17 | + return { |
| 18 | + label: task.family if label == "family" else task.params.get(label) |
| 19 | + for label in self.labels |
| 20 | + } |
| 21 | + |
| 22 | + def __init__(self, *args, **kwargs): |
8 | 23 | super(PrometheusMetricsCollector, self).__init__()
|
9 | 24 | self.registry = CollectorRegistry()
|
| 25 | + config = prometheus(**kwargs) |
| 26 | + self.labels = list(config.task_parameters_to_use_in_labels) |
| 27 | + if config.use_task_family_in_labels: |
| 28 | + self.labels += ["family"] |
| 29 | + if not self.labels: |
| 30 | + raise ValueError("Prometheus labels cannot be empty (see prometheus configuration)") |
10 | 31 | self.task_started_counter = Counter(
|
11 | 32 | 'luigi_task_started_total',
|
12 | 33 | 'number of started luigi tasks',
|
13 |
| - ['family'], |
| 34 | + self.labels, |
14 | 35 | registry=self.registry
|
15 | 36 | )
|
16 | 37 | self.task_failed_counter = Counter(
|
17 | 38 | 'luigi_task_failed_total',
|
18 | 39 | 'number of failed luigi tasks',
|
19 |
| - ['family'], |
| 40 | + self.labels, |
20 | 41 | registry=self.registry
|
21 | 42 | )
|
22 | 43 | self.task_disabled_counter = Counter(
|
23 | 44 | 'luigi_task_disabled_total',
|
24 | 45 | 'number of disabled luigi tasks',
|
25 |
| - ['family'], |
| 46 | + self.labels, |
26 | 47 | registry=self.registry
|
27 | 48 | )
|
28 | 49 | self.task_done_counter = Counter(
|
29 | 50 | 'luigi_task_done_total',
|
30 | 51 | 'number of done luigi tasks',
|
31 |
| - ['family'], |
| 52 | + self.labels, |
32 | 53 | registry=self.registry
|
33 | 54 | )
|
34 | 55 | self.task_execution_time = Gauge(
|
35 | 56 | 'luigi_task_execution_time_seconds',
|
36 | 57 | 'luigi task execution time in seconds',
|
37 |
| - ['family'], |
| 58 | + self.labels, |
38 | 59 | registry=self.registry
|
39 | 60 | )
|
40 | 61 |
|
41 | 62 | def generate_latest(self):
|
42 | 63 | return generate_latest(self.registry)
|
43 | 64 |
|
44 | 65 | def handle_task_started(self, task):
|
45 |
| - self.task_started_counter.labels(family=task.family).inc() |
46 |
| - self.task_execution_time.labels(family=task.family) |
| 66 | + self.task_started_counter.labels(**self._generate_task_labels(task)).inc() |
| 67 | + self.task_execution_time.labels(**self._generate_task_labels(task)) |
47 | 68 |
|
48 | 69 | def handle_task_failed(self, task):
|
49 |
| - self.task_failed_counter.labels(family=task.family).inc() |
50 |
| - self.task_execution_time.labels(family=task.family).set(task.updated - task.time_running) |
| 70 | + self.task_failed_counter.labels(**self._generate_task_labels(task)).inc() |
| 71 | + self.task_execution_time.labels(**self._generate_task_labels(task)).set(task.updated - task.time_running) |
51 | 72 |
|
52 | 73 | def handle_task_disabled(self, task, config):
|
53 |
| - self.task_disabled_counter.labels(family=task.family).inc() |
54 |
| - self.task_execution_time.labels(family=task.family).set(task.updated - task.time_running) |
| 74 | + self.task_disabled_counter.labels(**self._generate_task_labels(task)).inc() |
| 75 | + self.task_execution_time.labels(**self._generate_task_labels(task)).set(task.updated - task.time_running) |
55 | 76 |
|
56 | 77 | def handle_task_done(self, task):
|
57 |
| - self.task_done_counter.labels(family=task.family).inc() |
| 78 | + self.task_done_counter.labels(**self._generate_task_labels(task)).inc() |
58 | 79 | # time_running can be `None` if task was already complete
|
59 | 80 | if task.time_running is not None:
|
60 |
| - self.task_execution_time.labels(family=task.family).set(task.updated - task.time_running) |
| 81 | + self.task_execution_time.labels(**self._generate_task_labels(task)).set(task.updated - task.time_running) |
61 | 82 |
|
62 | 83 | def configure_http_handler(self, http_handler):
|
63 | 84 | http_handler.set_header('Content-Type', CONTENT_TYPE_LATEST)
|
0 commit comments