-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Constance metrics #73
base: master
Are you sure you want to change the base?
Constance metrics #73
Conversation
Variables from CONSTANCE_CONFIG will be exported as Prometheus metrics, starting with the default prefix of "constance_config_"
Not all string characters are allowed in metric names. Try to replace them with underscores.
The Enum type is being dropped in multiprocess mode and Gauge is perfectly enough to represent booleans
This pull request introduces 3 alerts and fixes 1 when merging e8aa7a7 into c85cafe - view on LGTM.com new alerts:
fixed alerts:
|
This pull request introduces 2 alerts and fixes 1 when merging 95da931 into c85cafe - view on LGTM.com new alerts:
fixed alerts:
|
...utter.django_project_name}}/{{cookiecutter.django_default_app_name}}/constance_prometheus.py
Outdated
Show resolved
Hide resolved
...utter.django_project_name}}/{{cookiecutter.django_default_app_name}}/constance_prometheus.py
Outdated
Show resolved
Hide resolved
...utter.django_project_name}}/{{cookiecutter.django_default_app_name}}/constance_prometheus.py
Outdated
Show resolved
Hide resolved
...utter.django_project_name}}/{{cookiecutter.django_default_app_name}}/constance_prometheus.py
Outdated
Show resolved
Hide resolved
...utter.django_project_name}}/{{cookiecutter.django_default_app_name}}/constance_prometheus.py
Outdated
Show resolved
Hide resolved
n = f"{n}_{v}" | ||
return n | ||
|
||
def _unregister(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mpnowacki-reef I think you might know what to do here. I mean for me it's pretty clear that unregistering should be implemented in metrics.py
and not in constance_prometheus.py
, registry seems to be a singleton so ideally we'd see some CollectorRegistryProvider which would just return the exiting object, rather than the class magically returning the same object as created somewhere else or the class somehow working consistently across threads despite having multiple copies...
But other than that I don't know how to deal with the multiprocess unregistering.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
registry seems to not be a singleton, according to prometheus-client howto: https://github.com/prometheus/client_python#multiprocess-mode-eg-gunicorn
I'm a bit lost, it is my understanding that registry.unregister
is for unregistering Collectors, not individual metrics, what ma I missing?
Nevermind that ^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My question is: does it actually work? When collecting metrics in multiprocessing mode, their values are read from .db files. I don't se how unregister
would remove entries from those files but I can be wrong.
Frankly speaking, from what I see, removing metrics does not really work when in multiprocessing mode. But again, I could be wrong. This whole thing is a little bit spaghetti in prometheus-client. Removing labels, however, should work, so a single metric with labels for constance param name and value could be a quasi-supported solution. And it would, I think, solve the duplicates problem
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's kind of fixed right now. I say kind because 15cd1fc is nothing but a hack. It seems to work in my local test project but will likely bite our collective ass in the future. What do you think?
...utter.django_project_name}}/{{cookiecutter.django_default_app_name}}/constance_prometheus.py
Outdated
Show resolved
Hide resolved
def _store_str(self, value): | ||
value = str(value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make the caller pass the correct type to the function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The caller would have to do another type check then, which would make the code unnecessarily complex.
ATM the only additional types allowed by constance are those from datetime
and str
works on them pretty well.
...utter.django_project_name}}/{{cookiecutter.django_default_app_name}}/constance_prometheus.py
Outdated
Show resolved
Hide resolved
...utter.django_project_name}}/{{cookiecutter.django_default_app_name}}/constance_prometheus.py
Outdated
Show resolved
Hide resolved
This pull request introduces 2 alerts and fixes 1 when merging a1b416d into b54e5b6 - view on LGTM.com new alerts:
fixed alerts:
|
As ideated in the CR, there could be two functions: - one which stores a single monitored variable, - another which stores all of them. This commit implements them.
This pull request introduces 2 alerts and fixes 1 when merging c02cd1c into b54e5b6 - view on LGTM.com new alerts:
fixed alerts:
|
I updated the code after your review. Please re-check. The important outstanding issue is unregistering metrics in multiprocess environment, however for that I'd like to consult someone with practical experience. |
This pull request introduces 2 alerts and fixes 1 when merging 8f48671 into b54e5b6 - view on LGTM.com new alerts:
fixed alerts:
|
Another idea here is to keep the following types permitted by constance as:
The advantage is that we could keep them as @ppolewicz what do you think about it? |
Always use UTC time. I'd rather cast it to |
# a str or custom type; cast everything to str | ||
value = str(value) | ||
self.name = self._get_name(value) | ||
self._metric = Gauge(self.name, self.description) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm worried about the multiprocess_mode
being let default here, which is all
. I think what we need is livemax
. The current behaviour will be:
'all': Default. Return a timeseries per process (alive or dead), labelled by the process's pid (the label is added internally).
If constance yields different values in different processes, it's either a misconfiguration or a bug, not sure we should be detecting it here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't help there, either. Adding the mode, I still get a duplicate error when changing the variable back to previously used value:
Environment:
Request Method: POST
Request URL: http://localhost:8000/admin/constance/config/
Django Version: 3.2.12
Python Version: 3.10.4
Installed Applications:
['django_prometheus',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django_extensions',
'django_probes',
'constance',
'project.core',
'corsheaders']
Installed Middleware:
['corsheaders.middleware.CorsMiddleware',
'django_prometheus.middleware.PrometheusBeforeMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django_prometheus.middleware.PrometheusAfterMiddleware']
Traceback (most recent call last):
File "/home/emes/venvs/reef/lib/python3.10/site-packages/django/core/handlers/exception.py", line 47, in inner
response = get_response(request)
File "/home/emes/venvs/reef/lib/python3.10/site-packages/django/core/handlers/base.py", line 181, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/emes/venvs/reef/lib/python3.10/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "/home/emes/venvs/reef/lib/python3.10/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func
response = view_func(request, *args, **kwargs)
File "/home/emes/venvs/reef/lib/python3.10/site-packages/django/contrib/admin/sites.py", line 232, in inner
return view(request, *args, **kwargs)
File "/home/emes/venvs/reef/lib/python3.10/site-packages/django/utils/decorators.py", line 43, in _wrapper
return bound_method(*args, **kwargs)
File "/home/emes/venvs/reef/lib/python3.10/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "/home/emes/venvs/reef/lib/python3.10/site-packages/constance/admin.py", line 256, in changelist_view
form.save()
File "/home/emes/venvs/reef/lib/python3.10/site-packages/constance/admin.py", line 157, in save
setattr(config, name, new)
File "/home/emes/venvs/reef/lib/python3.10/site-packages/django/utils/functional.py", line 277, in __setattr__
setattr(self._wrapped, name, value)
File "/home/emes/venvs/reef/lib/python3.10/site-packages/constance/base.py", line 29, in __setattr__
self._backend.set(key, value)
File "/home/emes/venvs/reef/lib/python3.10/site-packages/constance/backends/redisd.py", line 50, in set
signals.config_updated.send(
File "/home/emes/venvs/reef/lib/python3.10/site-packages/django/dispatch/dispatcher.py", line 180, in send
return [
File "/home/emes/venvs/reef/lib/python3.10/site-packages/django/dispatch/dispatcher.py", line 181, in <listcomp>
(receiver, receiver(signal=self, sender=sender, **named))
File "/home/emes/devel/reef/cct/project/app/src/project/core/constance_prometheus.py", line 134, in on_constance_config_updated
store_monitored_metric(key, new_value)
File "/home/emes/devel/reef/cct/project/app/src/project/core/constance_prometheus.py", line 117, in store_monitored_metric
METRICS[key].store(new_value)
File "/home/emes/devel/reef/cct/project/app/src/project/core/constance_prometheus.py", line 106, in _store_str
self._metric = Gauge(self.name, self.description)
File "/home/emes/venvs/reef/lib/python3.10/site-packages/prometheus_client/metrics.py", line 365, in __init__
super().__init__(
File "/home/emes/venvs/reef/lib/python3.10/site-packages/prometheus_client/metrics.py", line 143, in __init__
registry.register(self)
File "/home/emes/venvs/reef/lib/python3.10/site-packages/prometheus_client/registry.py", line 43, in register
raise ValueError(
Exception Type: ValueError at /admin/constance/config/
Exception Value: Duplicated timeseries in CollectorRegistry: {'constance_config_SOME_STRING_hello_string'}
I wonder if we shouldn't drop the str
(and other non-standard type) monitoring until we figure out a way to handle it better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nevermind the above comment. I'll address the "livemax"
mode later on.
BTW, have you noticed that "livemax"
is available only in unreleased code? Latest official release (0.14.1) doesn't have it.
This pull request introduces 3 alerts and fixes 1 when merging 016937f into b54e5b6 - view on LGTM.com new alerts:
fixed alerts:
|
This pull request introduces 2 alerts and fixes 1 when merging 740fd69 into b54e5b6 - view on LGTM.com new alerts:
fixed alerts:
|
THIS IS A HACK: To avoid key clash, try to reuse an existing metric by using undocumented mapping in the registry.
This pull request introduces 3 alerts and fixes 1 when merging 15cd1fc into b54e5b6 - view on LGTM.com new alerts:
fixed alerts:
|
This pull request introduces 2 alerts and fixes 1 when merging 38690b1 into b54e5b6 - view on LGTM.com new alerts:
fixed alerts:
|
Any progress with reviewing this, guys? |
This PR allows for exporting
django-constance
config variables as Prometheus metrics.int
,float
,bool
anddecimal.Decimal
types by default.CONSTANCE_PROMETHEUS_BLACKLIST
setting.CONSTANCE_PROMETHEUS_WHITELIST
setting by adding their string representation to the metric's name. (May cause problems in multiprocess mode.)This is a review request, esp. to @ppolewicz