-
Notifications
You must be signed in to change notification settings - Fork 2
feat: Prometheus support, core entrypoint, packaging improvements #17
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
Merged
Changes from 28 commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
37d65fe
initial
khvn26 fab1c04
fix gauges, add `with_labels`
khvn26 a364aa8
don't resolve route twice
khvn26 97ff321
add docs
khvn26 ef5de6f
ignore testing database
khvn26 69707e2
docs touchup
khvn26 09d27bf
less code
khvn26 148172e
fix tests
khvn26 18ffe03
nicer code
khvn26 1c3946b
drop the extra type
khvn26 c50c383
improve readme readability
khvn26 23cc146
fix types
khvn26 aca03b9
improve prometheus_multiproc_dir
khvn26 4c6d30a
consistent usage of `pid` in logging
khvn26 ab4b7ed
fix test
khvn26 3d6a3d1
use `os.devnull`
khvn26 7aa1190
apply README suggestions
khvn26 b976e60
clarify authors
khvn26 547eaf1
improve metric naming
khvn26 73c70f4
stop encouraging gauges
khvn26 e986e6f
bump Python version
khvn26 6557b4a
remove extra setting
khvn26 d0415ef
why won't poetry do this for me?
khvn26 aa3989e
add `PROMETHEUS_ENABLED`
khvn26 28c47d0
Add `common.prometheus.Histogram` and document it
khvn26 fa9b358
remove `with_labels`
khvn26 26d8d17
"path" -> "route"
khvn26 585be56
remove unused constant
khvn26 81a5fe1
reset registry state
khvn26 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 |
|---|---|---|
| @@ -1,4 +1,6 @@ | ||
| .venv/ | ||
| .env | ||
| .idea/ | ||
| *.pyc | ||
| *.pyc | ||
| .coverage | ||
| common.sqlite3 |
This file contains hidden or 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
This file contains hidden or 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
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or 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 |
|---|---|---|
| @@ -1,29 +1,70 @@ | ||
| [tool.poetry] | ||
| name = "flagsmith_common" | ||
| [project] | ||
| name = "flagsmith-common" | ||
| version = "1.5.0" | ||
| description = "A repository for including code that is required in multiple flagsmith repositories" | ||
| authors = ["Matthew Elwell <[email protected]>"] | ||
| description = "Flagsmith's common library" | ||
| requires-python = ">=3.11,<4.0" | ||
| dependencies = [ | ||
| "django (<5)", | ||
| "django-health-check", | ||
| "djangorestframework-recursive", | ||
| "djangorestframework", | ||
| "drf-writable-nested", | ||
| "flagsmith-flag-engine", | ||
| "gunicorn (>=19.1)", | ||
| "prometheus-client (>=0.0.16)", | ||
| "environs (<15)", | ||
| ] | ||
| authors = [ | ||
| { name = "Matthew Elwell" }, | ||
| { name = "Gagan Trivedi" }, | ||
| { name = "Kim Gustyr" }, | ||
| { name = "Zach Aysan" }, | ||
| { name = "Francesco Lo Franco" }, | ||
| { name = "Rodrigo López Dato" }, | ||
| ] | ||
| maintainers = [{ name = "Flagsmith Team", email = "[email protected]" }] | ||
| license = "BSD-3-Clause" | ||
| license-files = ["LICENSE"] | ||
| readme = "README.md" | ||
| packages = [{ include = "common", from = "src" }] | ||
| dynamic = ["classifiers"] | ||
|
|
||
| [project.urls] | ||
| Download = "https://github.com/flagsmith/flagsmith-common/releases" | ||
| Homepage = "https://flagsmith.com" | ||
| Issues = "https://github.com/flagsmith/flagsmith-common/issues" | ||
| Repository = "https://github.com/flagsmith/flagsmith-common" | ||
|
|
||
| [tool.poetry.dependencies] | ||
| python = "^3.10" | ||
| django = "<5.0.0" | ||
| djangorestframework = "*" | ||
| drf-writable-nested = "*" | ||
| flagsmith-flag-engine = "*" | ||
| djangorestframework-recursive = "*" | ||
| django-health-check = "^3.18.3" | ||
| [project.scripts] | ||
| flagsmith = "common.core.main:main" | ||
|
|
||
| [project.entry-points.pytest11] | ||
| flagsmith-test-tools = "common.test_tools.plugin" | ||
|
|
||
| [tool.poetry] | ||
| requires-poetry = ">=2.0" | ||
| classifiers = [ | ||
| "Framework :: Django", | ||
| "Framework :: Pytest", | ||
| "Intended Audience :: Developers", | ||
| "License :: OSI Approved :: BSD License", | ||
| ] | ||
| packages = [{ include = "common", from = "src" }] | ||
|
|
||
| [tool.poetry.group.dev.dependencies] | ||
| django-stubs = "^5.1.3" | ||
| djangorestframework-stubs = "^3.15.3" | ||
| mypy = "^1.15.0" | ||
| pre-commit = "*" | ||
| ruff = "*" | ||
| pytest = "^8.3.4" | ||
| pyfakefs = "^5.7.4" | ||
| pytest = "^8.3.4" | ||
| pytest-asyncio = "^0.25.3" | ||
| pytest-cov = "^6.0.0" | ||
| pytest-django = "^4.10.0" | ||
| mypy = "^1.15.0" | ||
| django-stubs = "^5.1.3" | ||
| djangorestframework-stubs = "^3.15.3" | ||
| pytest-freezegun = "^0.4.2" | ||
| pytest-mock = "^3.14.0" | ||
| requests = "^2.32.3" | ||
| ruff = "*" | ||
| setuptools = "^77.0.3" | ||
|
|
||
| [build-system] | ||
| requires = ["poetry-core"] | ||
|
|
||
This file contains hidden or 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 |
|---|---|---|
| @@ -1,16 +1,28 @@ | ||
| import prometheus_client | ||
|
|
||
| # Settings expected by `mypy_django_plugin` | ||
| SENDGRID_API_KEY: str | ||
| AWS_SES_REGION_ENDPOINT: str | ||
| SEGMENT_RULES_CONDITIONS_LIMIT: int | ||
| SENDGRID_API_KEY: str | ||
| PROMETHEUS_ENABLED = True | ||
|
|
||
| # Settings required for tests | ||
| INSTALLED_APPS = [ | ||
| "django.contrib.auth", | ||
| "django.contrib.contenttypes", | ||
| ] | ||
| DATABASES = { | ||
| "default": { | ||
| "ENGINE": "django.db.backends.sqlite3", | ||
| "NAME": "common", | ||
| "NAME": "common.sqlite3", | ||
| } | ||
| } | ||
| INSTALLED_APPS = [ | ||
| "django.contrib.auth", | ||
| "django.contrib.contenttypes", | ||
| "common.core", | ||
| ] | ||
| MIDDLEWARE = [ | ||
| "common.gunicorn.middleware.RouteLoggerMiddleware", | ||
| ] | ||
| LOG_FORMAT = "json" | ||
| PROMETHEUS_HISTOGRAM_BUCKETS = prometheus_client.Histogram.DEFAULT_BUCKETS | ||
| ROOT_URLCONF = "common.core.urls" | ||
| TIME_ZONE = "UTC" | ||
| USE_TZ = True |
This file was deleted.
Oops, something went wrong.
File renamed without changes.
This file contains hidden or 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 @@ | ||
| from django.apps import AppConfig | ||
|
|
||
|
|
||
| class CoreConfig(AppConfig): | ||
| name = "common.core" | ||
| label = "common_core" |
This file contains hidden or 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,24 @@ | ||
| import json | ||
| import logging | ||
| from typing import Any | ||
|
|
||
|
|
||
| class JsonFormatter(logging.Formatter): | ||
| """Custom formatter for json logs.""" | ||
|
|
||
| def get_json_record(self, record: logging.LogRecord) -> dict[str, Any]: | ||
| formatted_message = record.getMessage() | ||
| json_record = { | ||
| "levelname": record.levelname, | ||
| "message": formatted_message, | ||
| "timestamp": self.formatTime(record, self.datefmt), | ||
| "logger_name": record.name, | ||
| "pid": record.process, | ||
| "thread_name": record.threadName, | ||
| } | ||
| if record.exc_info: | ||
| json_record["exc_info"] = self.formatException(record.exc_info) | ||
rolodato marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return json_record | ||
|
|
||
| def format(self, record: logging.LogRecord) -> str: | ||
| return json.dumps(self.get_json_record(record)) | ||
rolodato marked this conversation as resolved.
Show resolved
Hide resolved
|
||
This file contains hidden or 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,40 @@ | ||
| import logging | ||
| import os | ||
| import sys | ||
| import tempfile | ||
|
|
||
| from django.core.management import execute_from_command_line | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| def main() -> None: | ||
| """ | ||
| The main entry point to the Flagsmith application. | ||
|
|
||
| An equivalent to Django's `manage.py` script, this module is used to run management commands. | ||
|
|
||
| It's installed as the `flagsmith` command. | ||
|
|
||
| Everything that needs to be run before Django is started should be done here. | ||
|
|
||
| The end goal is to eventually replace Core API's `run-docker.sh` with this. | ||
|
|
||
| Usage: | ||
| `flagsmith <command> [options]` | ||
| """ | ||
| os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.dev") | ||
|
|
||
| # Set up Prometheus' multiprocess mode | ||
| if "PROMETHEUS_MULTIPROC_DIR" not in os.environ: | ||
rolodato marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| prometheus_multiproc_dir = tempfile.TemporaryDirectory( | ||
| prefix="prometheus_multiproc", | ||
| ) | ||
| logger.info( | ||
| "Created %s for Prometheus multi-process mode", | ||
| prometheus_multiproc_dir.name, | ||
| ) | ||
| os.environ["PROMETHEUS_MULTIPROC_DIR"] = prometheus_multiproc_dir.name | ||
|
|
||
| # Run Django | ||
| execute_from_command_line(sys.argv) | ||
Empty file.
Empty file.
This file contains hidden or 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,41 @@ | ||
| from typing import Any, Callable | ||
|
|
||
| from django.core.management import BaseCommand, CommandParser | ||
| from django.utils.module_loading import autodiscover_modules | ||
|
|
||
| from common.gunicorn.utils import add_arguments, run_server | ||
|
|
||
|
|
||
| class Command(BaseCommand): | ||
| help = "Start the Flagsmith application." | ||
|
|
||
| def create_parser(self, *args: Any, **kwargs: Any) -> CommandParser: | ||
| return super().create_parser(*args, conflict_handler="resolve", **kwargs) | ||
|
|
||
| def add_arguments(self, parser: CommandParser) -> None: | ||
| add_arguments(parser) | ||
|
|
||
| subparsers = parser.add_subparsers( | ||
| title="sub-commands", | ||
| required=True, | ||
| ) | ||
| api_parser = subparsers.add_parser( | ||
| "api", | ||
| help="Start the Core API.", | ||
| ) | ||
| api_parser.set_defaults(handle_method=self.handle_api) | ||
|
|
||
| def initialise(self) -> None: | ||
| autodiscover_modules("metrics") | ||
|
|
||
| def handle( | ||
| self, | ||
| *args: Any, | ||
| handle_method: Callable[..., None], | ||
| **options: Any, | ||
| ) -> None: | ||
| self.initialise() | ||
| handle_method(*args, **options) | ||
|
|
||
| def handle_api(self, *args: Any, **options: Any) -> None: | ||
| run_server(options) |
This file contains hidden or 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,23 @@ | ||
| import prometheus_client | ||
|
|
||
| from common.core.utils import get_version_info | ||
|
|
||
| flagsmith_build_info = prometheus_client.Gauge( | ||
| "flagsmith_build_info", | ||
| "Flagsmith version and build information", | ||
| ["ci_commit_sha", "version"], | ||
| multiprocess_mode="livemax", | ||
rolodato marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ) | ||
|
|
||
|
|
||
| def advertise() -> None: | ||
| # Advertise Flagsmith build info. | ||
| version_info = get_version_info() | ||
|
|
||
| flagsmith_build_info.labels( | ||
| ci_commit_sha=version_info["ci_commit_sha"], | ||
| version=version_info.get("package_versions", {}).get(".") or "unknown", | ||
| ).set(1) | ||
|
|
||
|
|
||
| advertise() | ||
This file contains hidden or 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
File renamed without changes.
This file contains hidden or 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,23 @@ | ||
| import logging | ||
|
|
||
| import prometheus_client | ||
| from django.http import HttpResponse, JsonResponse | ||
| from rest_framework.request import Request | ||
|
|
||
| from common.core import utils | ||
| from common.prometheus.utils import get_registry | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| def version_info(request: Request) -> JsonResponse: | ||
| return JsonResponse(utils.get_version_info()) | ||
|
|
||
|
|
||
| def metrics(request: Request) -> HttpResponse: | ||
rolodato marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| registry = get_registry() | ||
| metrics_page = prometheus_client.generate_latest(registry) | ||
| return HttpResponse( | ||
| metrics_page, | ||
| content_type=prometheus_client.CONTENT_TYPE_LATEST, | ||
| ) | ||
Empty file.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.