-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Fabio Todaro
committed
Jan 31, 2019
0 parents
commit b1845ab
Showing
23 changed files
with
811 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[run] | ||
source = celery_exporter | ||
|
||
[report] | ||
fail_under = 100 | ||
show_missing = True | ||
|
||
[paths] | ||
source = celery_exporter |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
* | ||
!setup.py | ||
!README.md | ||
!celery_exporter | ||
!requirements | ||
__pycache__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
*.img | ||
/dist | ||
/build | ||
/*.egg-info | ||
|
||
*.pyc | ||
__pycache__ | ||
.coverage | ||
.tox/ | ||
.cache/ | ||
.python-version | ||
.pytest_cache | ||
|
||
.vscode |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
sudo: false | ||
language: python | ||
|
||
python: | ||
- "3.4" | ||
- "3.5" | ||
- "3.6" | ||
|
||
install: pip install tox-travis tox | ||
script: tox |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Any PR is welcome! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
FROM python:3.6-alpine | ||
LABEL maintainer="Fabio Todaro <ft@ovalmoney.com>" | ||
|
||
ARG BUILD_DATE | ||
ARG DOCKER_REPO | ||
ARG VERSION | ||
ARG VCS_REF | ||
|
||
LABEL org.label-schema.schema-version="1.0" \ | ||
org.label-schema.build-date=$BUILD_DATE \ | ||
org.label-schema.name=$DOCKER_REPO \ | ||
org.label-schema.version=$VERSION \ | ||
org.label-schema.description="Prometheus metrics exporter for Celery" \ | ||
org.label-schema.vcs-ref=$VCS_REF \ | ||
org.label-schema.vcs-url="https://github.com/OvalMoney/celery-exporter" | ||
|
||
WORKDIR /app/ | ||
|
||
COPY requirements/ ./requirements | ||
|
||
RUN pip install -r ./requirements/requirements.txt | ||
|
||
COPY setup.py README.md ./ | ||
COPY celery_exporter/ ./celery_exporter/ | ||
RUN pip install --no-deps -e . | ||
|
||
ENTRYPOINT ["celery-exporter"] | ||
CMD [] | ||
|
||
EXPOSE 9540 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2019, Ovalmoney Ltd | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
include README.md | ||
include LICENSE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
.PHONY: help | ||
.DEFAULT_GOAL := help | ||
|
||
DOCKER_REPO="ovalmoney/celery-exporter" | ||
DOCKER_VERSION="latest" | ||
|
||
define PRINT_HELP_PYSCRIPT | ||
import re, sys | ||
|
||
for line in sys.stdin: | ||
match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line) | ||
if match: | ||
target, help = match.groups() | ||
print("%-20s %s" % (target, help)) | ||
endef | ||
export PRINT_HELP_PYSCRIPT | ||
|
||
all: clean docker_build ## Clean and Build | ||
|
||
clean: ## Clean folders | ||
rm -rf dist/ *.egg-info | ||
|
||
docker_build: ## Build Docker file | ||
export DOCKER_REPO | ||
export DOCKER_VERSION | ||
|
||
docker build \ | ||
--build-arg DOCKER_REPO=${DOCKER_REPO} \ | ||
--build-arg VERSION=${DOCKER_VERSION} \ | ||
--build-arg VCS_REF=`git rev-parse --short HEAD` \ | ||
--build-arg BUILD_DATE=`date -u +”%Y-%m-%dT%H:%M:%SZ”` \ | ||
-f ./Dockerfile \ | ||
-t ${DOCKER_REPO}:${DOCKER_VERSION} \ | ||
. | ||
|
||
help: ## Print this help | ||
@python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
# Celery Exporter | ||
|
||
![https://hub.docker.com/r/ovalmoney/celery-exporter/](https://img.shields.io/docker/automated/ovalmoney/celery-exporter.svg?maxAge=2592000) | ||
|
||
Celery Exporter is a Prometheus metrics exporter for Celery 4, written in python. | ||
|
||
Here the list of exposed metrics: | ||
|
||
* `celery_tasks` exposes the number of tasks currently known to the queue | ||
labeled by `name`, `state` and `namespace`. | ||
* `celery_tasks_runtime_seconds` tracks the number of seconds tasks take | ||
until completed as histogram labeled by `name` and `namespace` | ||
* `celery_task_latency` exposes a histogram of task latency, i.e. the time until | ||
tasks are picked up by a worker | ||
* `celery_workers` exposes the number of currently probably alive workers | ||
|
||
--- | ||
## Requirements | ||
|
||
|
||
### Dependencies | ||
The project comes with `redis` lib already installed, you have to install any other dependency in case you are using other brokers. | ||
|
||
### Celery app | ||
Celery workers have to be configured to send task-related events: | ||
http://docs.celeryproject.org/en/latest/userguide/configuration.html#worker-send-task-events. | ||
|
||
Celery Exporter is able to enable events on your workers (see _Command Options_). | ||
|
||
--- | ||
## Install and Run | ||
|
||
### Manual Setup | ||
```bash | ||
# Install | ||
$ pip install celery-exporter | ||
|
||
# Run | ||
$ celery-exporter | ||
``` | ||
|
||
### Docker | ||
```bash | ||
# Download image | ||
$ docker pull ovalmoney/celery-exporter | ||
|
||
# Run it | ||
$ docker run -it --rm ovalmoney/celery-exporter | ||
``` | ||
|
||
### Command Options | ||
|
||
```bash | ||
$ celery-exporter --help | ||
Usage: celery-exporter [OPTIONS] | ||
|
||
Options: | ||
-b, --broker-url TEXT URL to the Celery broker. [env var: | ||
CELERY_EXPORTER_BROKER_URL; default: | ||
redis://redis:6379/0] | ||
-l, --listen-address TEXT Address the HTTPD should listen on. [env var: | ||
CELERY_EXPORTER_LISTEN_ADDRESS; default: | ||
0.0.0.0:9540] | ||
-m, --max-tasks INTEGER Tasks cache size. [env var: | ||
CELERY_EXPORTER_MAX_TASKS; default: 10000] | ||
-n, --namespace TEXT Namespace for metrics. [env var: | ||
CELERY_EXPORTER_NAMESPACE; default: celery] | ||
--transport-options TEXT JSON object with additional options passed to the | ||
underlying transport. | ||
--enable-events Periodically enable Celery events. | ||
--tz TEXT Timezone used by the celery app. | ||
--verbose Enable verbose logging. | ||
--version Show the version and exit. | ||
--help Show this message and exit. | ||
``` | ||
|
||
|
||
If you then look at the exposed metrics, you should see something like this: | ||
```bash | ||
# HELP celery_workers Number of alive workers | ||
# TYPE celery_workers gauge | ||
celery_workers{namespace="celery"} 1.0 | ||
# HELP celery_tasks Number of tasks per state | ||
# TYPE celery_tasks gauge | ||
celery_tasks{name="my_app.tasks.calculate_something",namespace="celery",state="RECEIVED"} 0.0 | ||
celery_tasks{name="my_app.tasks.calculate_something",namespace="celery",state="PENDING"} 0.0 | ||
celery_tasks{name="my_app.tasks.calculate_something",namespace="celery",state="STARTED"} 0.0 | ||
celery_tasks{name="my_app.tasks.calculate_something",namespace="celery",state="RETRY"} 0.0 | ||
celery_tasks{name="my_app.tasks.calculate_something",namespace="celery",state="FAILURE"} 0.0 | ||
celery_tasks{name="my_app.tasks.calculate_something",namespace="celery",state="REVOKED"} 0.0 | ||
celery_tasks{name="my_app.tasks.calculate_something",namespace="celery",state="SUCCESS"} 1.0 | ||
celery_tasks{name="my_app.tasks.fetch_some_data",namespace="celery",state="RECEIVED"} 3.0 | ||
celery_tasks{name="my_app.tasks.fetch_some_data",namespace="celery",state="PENDING"} 0.0 | ||
celery_tasks{name="my_app.tasks.fetch_some_data",namespace="celery",state="STARTED"} 1.0 | ||
celery_tasks{name="my_app.tasks.fetch_some_data",namespace="celery",state="RETRY"} 2.0 | ||
celery_tasks{name="my_app.tasks.fetch_some_data",namespace="celery",state="FAILURE"} 1.0 | ||
celery_tasks{name="my_app.tasks.fetch_some_data",namespace="celery",state="REVOKED"} 0.0 | ||
celery_tasks{name="my_app.tasks.fetch_some_data",namespace="celery",state="SUCCESS"} 7.0 | ||
# HELP celery_tasks_runtime_seconds Task runtime (seconds) | ||
# TYPE celery_tasks_runtime_seconds histogram | ||
celery_tasks_runtime_seconds_bucket{le="0.005",name="my_app.tasks.calculate_something",namespace="celery"} 29.0 | ||
celery_tasks_runtime_seconds_bucket{le="0.01",name="my_app.tasks.calculate_something",namespace="celery"} 29.0 | ||
celery_tasks_runtime_seconds_bucket{le="0.025",name="my_app.tasks.calculate_something",namespace="celery"} 29.0 | ||
celery_tasks_runtime_seconds_bucket{le="0.05",name="my_app.tasks.calculate_something",namespace="celery"} 29.0 | ||
celery_tasks_runtime_seconds_bucket{le="0.075",name="my_app.tasks.calculate_something",namespace="celery"} 29.0 | ||
celery_tasks_runtime_seconds_bucket{le="0.1",name="my_app.tasks.calculate_something",namespace="celery"} 29.0 | ||
celery_tasks_runtime_seconds_bucket{le="0.25",name="my_app.tasks.calculate_something",namespace="celery"} 29.0 | ||
celery_tasks_runtime_seconds_bucket{le="0.5",name="my_app.tasks.calculate_something",namespace="celery"} 29.0 | ||
celery_tasks_runtime_seconds_bucket{le="0.75",name="my_app.tasks.calculate_something",namespace="celery"} 29.0 | ||
celery_tasks_runtime_seconds_bucket{le="1.0",name="my_app.tasks.calculate_something",namespace="celery"} 29.0 | ||
celery_tasks_runtime_seconds_bucket{le="2.5",name="my_app.tasks.calculate_something",namespace="celery"} 29.0 | ||
celery_tasks_runtime_seconds_bucket{le="5.0",name="my_app.tasks.calculate_something",namespace="celery"} 29.0 | ||
celery_tasks_runtime_seconds_bucket{le="7.5",name="my_app.tasks.calculate_something",namespace="celery"} 29.0 | ||
celery_tasks_runtime_seconds_bucket{le="10.0",name="my_app.tasks.calculate_something",namespace="celery"} 29.0 | ||
celery_tasks_runtime_seconds_bucket{le="+Inf",name="my_app.tasks.calculate_something",namespace="celery"} 29.0 | ||
celery_tasks_runtime_seconds_count{name="my_app.tasks.calculate_something",namespace="celery"} 29.0 | ||
celery_tasks_runtime_seconds_sum{name="my_app.tasks.calculate_something",namespace="celery"} 0.04020289977779612 | ||
celery_tasks_runtime_seconds_bucket{le="0.005",name="my_app.tasks.fetch_some_data",namespace="celery"} 2.0 | ||
celery_tasks_runtime_seconds_bucket{le="0.01",name="my_app.tasks.fetch_some_data",namespace="celery"} 2.0 | ||
celery_tasks_runtime_seconds_bucket{le="0.025",name="my_app.tasks.fetch_some_data",namespace="celery"} 2.0 | ||
celery_tasks_runtime_seconds_bucket{le="0.05",name="my_app.tasks.fetch_some_data",namespace="celery"} 2.0 | ||
celery_tasks_runtime_seconds_bucket{le="0.075",name="my_app.tasks.fetch_some_data",namespace="celery"} 2.0 | ||
celery_tasks_runtime_seconds_bucket{le="0.1",name="my_app.tasks.fetch_some_data",namespace="celery"} 2.0 | ||
celery_tasks_runtime_seconds_bucket{le="0.25",name="my_app.tasks.fetch_some_data",namespace="celery"} 2.0 | ||
celery_tasks_runtime_seconds_bucket{le="0.5",name="my_app.tasks.fetch_some_data",namespace="celery"} 2.0 | ||
celery_tasks_runtime_seconds_bucket{le="0.75",name="my_app.tasks.fetch_some_data",namespace="celery"} 2.0 | ||
celery_tasks_runtime_seconds_bucket{le="1.0",name="my_app.tasks.fetch_some_data",namespace="celery"} 2.0 | ||
celery_tasks_runtime_seconds_bucket{le="2.5",name="my_app.tasks.fetch_some_data",namespace="celery"} 2.0 | ||
celery_tasks_runtime_seconds_bucket{le="5.0",name="my_app.tasks.fetch_some_data",namespace="celery"} 2.0 | ||
celery_tasks_runtime_seconds_bucket{le="7.5",name="my_app.tasks.fetch_some_data",namespace="celery"} 2.0 | ||
celery_tasks_runtime_seconds_bucket{le="10.0",name="my_app.tasks.fetch_some_data",namespace="celery"} 2.0 | ||
celery_tasks_runtime_seconds_bucket{le="+Inf",name="my_app.tasks.fetch_some_data",namespace="celery"} 2.0 | ||
celery_tasks_runtime_seconds_count{name="my_app.tasks.fetch_some_data",namespace="celery"} 2.0 | ||
celery_tasks_runtime_seconds_sum{name="my_app.tasks.fetch_some_data",namespace="celery"} 0.00402028997777961 | ||
# TYPE celery_tasks_runtime_seconds_created gauge | ||
celery_tasks_runtime_seconds_created{name="my_app.tasks.calculate_something",namespace="celery"} 1.548944949810905e+09 | ||
celery_tasks_runtime_seconds_created{name="my_app.tasks.fetch_some_data",namespace="celery"} 1.5489449550243628e+09 | ||
# HELP celery_task_latency Seconds between a task is received and started. | ||
# TYPE celery_task_latency histogram | ||
celery_task_latency_bucket{namespace="celery",le="0.005"} 2.0 | ||
celery_task_latency_bucket{namespace="celery",le="0.01"} 3.0 | ||
celery_task_latency_bucket{namespace="celery",le="0.025"} 4.0 | ||
celery_task_latency_bucket{namespace="celery",le="0.05"} 4.0 | ||
celery_task_latency_bucket{namespace="celery",le="0.075"} 5.0 | ||
celery_task_latency_bucket{namespace="celery",le="0.1"} 5.0 | ||
celery_task_latency_bucket{namespace="celery",le="0.25"} 5.0 | ||
celery_task_latency_bucket{namespace="celery",le="0.5"} 5.0 | ||
celery_task_latency_bucket{namespace="celery",le="0.75"} 5.0 | ||
celery_task_latency_bucket{namespace="celery",le="1.0"} 5.0 | ||
celery_task_latency_bucket{namespace="celery",le="2.5"} 8.0 | ||
celery_task_latency_bucket{namespace="celery",le="5.0"} 11.0 | ||
celery_task_latency_bucket{namespace="celery",le="7.5"} 11.0 | ||
celery_task_latency_bucket{namespace="celery",le="10.0"} 11.0 | ||
celery_task_latency_bucket{namespace="celery",le="+Inf"} 11.0 | ||
celery_task_latency_count{namespace="celery"} 11.0 | ||
celery_task_latency_sum{namespace="celery"} 16.478713035583496 | ||
# TYPE celery_task_latency_created gauge | ||
celery_task_latency_created{namespace="celery"} 1.5489449475378375e+09 | ||
``` | ||
|
||
### Inspired by @zerok work | ||
https://github.com/zerok/celery-prometheus-exporter |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import json | ||
import logging | ||
import os | ||
import signal | ||
import sys | ||
import time | ||
|
||
import click | ||
from .core import CeleryExporter | ||
|
||
__VERSION__ = (2, 0, 0) | ||
|
||
LOG_FORMAT = '[%(asctime)s] %(name)s:%(levelname)s: %(message)s' | ||
|
||
@click.command(context_settings={ | ||
'auto_envvar_prefix':'CELERY_EXPORTER' | ||
}) | ||
@click.option('--broker-url', '-b', type=str, show_default=True, show_envvar=True, | ||
default='redis://redis:6379/0', | ||
help='URL to the Celery broker.') | ||
@click.option('--listen-address', '-l', type=str, show_default=True, show_envvar=True, | ||
default='0.0.0.0:9540', | ||
help='Address the HTTPD should listen on.') | ||
@click.option('--max-tasks', '-m', type=int, show_default=True, show_envvar=True, | ||
default='10000', | ||
help='Tasks cache size.') | ||
@click.option('--namespace', '-n', type=str, show_default=True, show_envvar=True, | ||
default='celery', | ||
help='Namespace for metrics.') | ||
@click.option('--transport-options', type=str, allow_from_autoenv=False, | ||
help='JSON object with additional options passed to the underlying transport.') | ||
@click.option('--enable-events', is_flag=True, allow_from_autoenv=False, | ||
help='Periodically enable Celery events.') | ||
@click.option('--tz', type=str, allow_from_autoenv=False, | ||
help='Timezone used by the celery app.') | ||
@click.option('--verbose', is_flag=True, allow_from_autoenv=False, | ||
help='Enable verbose logging.') | ||
@click.version_option(version='.'.join([str(x) for x in __VERSION__])) | ||
def main(broker_url, listen_address, max_tasks, namespace, transport_options, enable_events, tz, verbose): # pragma: no cover | ||
|
||
if verbose: | ||
logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT) | ||
else: | ||
logging.basicConfig(level=logging.INFO, format=LOG_FORMAT) | ||
|
||
if tz: | ||
os.environ['TZ'] = tz | ||
time.tzset() | ||
|
||
if transport_options: | ||
try: | ||
transport_options = json.loads(transport_options) | ||
except ValueError: | ||
print("Error parsing broker transport options from JSON '{}'" | ||
.format(transport_options), file=sys.stderr) | ||
sys.exit(1) | ||
|
||
celery_exporter = CeleryExporter(broker_url, listen_address, max_tasks, namespace, transport_options, enable_events) | ||
celery_exporter.start() | ||
|
||
def shutdown(signum, frame): # pragma: no cover | ||
""" | ||
Shutdown is called if the process receives a TERM/INT signal. | ||
""" | ||
logging.info("Shutting down") | ||
sys.exit(0) | ||
|
||
signal.signal(signal.SIGINT, shutdown) | ||
signal.signal(signal.SIGTERM, shutdown) | ||
|
||
while True: | ||
signal.pause() | ||
|
||
if __name__ == '__main__': # pragma: no cover | ||
main() # pylint: disable=E1120 |
Oops, something went wrong.