flagsmith-common
Flagsmith's common library
Local development
The project assumes the following tools installed:
To list available Makefile targets, run make help.
To set up local development environment, run make install.
To run linters, run make lint.
To run tests, run make test.
Usage
Installation
-
Install all runtime packages:
uv add flagsmith-common[common-core,task-processor] -
To enable the Pytest fixtures, run
uv add --G dev flagsmith-common[test-tools]. Skipping this step will make Pytest collection fail due to missing dependencies. -
Make sure
"common.core"is in theINSTALLED_APPSof your settings module. This enables themanage.py flagsmithcommands. -
Add
"common.gunicorn.middleware.RouteLoggerMiddleware"toMIDDLEWAREin your settings module. This enables theroutelabel for Prometheus HTTP metrics. -
To enable the
/metricsendpoint, set thePROMETHEUS_ENABLEDsetting toTrue.
Startup commands
common.core registers composite startup verbs on the flagsmith command, so a container entrypoint can sequence startup itself rather than via a shell script:
flagsmith serve— wait for the database, then start the API.flagsmith migrate— migrate each configured database, then create the cache table. A bare invocation runs the full sequence;flagsmith migrate <app> <name>defers to Django for targeted migrations.flagsmith run-task-processor— migrate, wait for migrations to be applied, then start the Task Processor.flagsmith migrate-and-serve— migrate, run any configured startup commands, then start the API.
The verbs are configured through these settings:
| Setting | Default | Purpose |
|---|---|---|
FLAGSMITH_MIGRATE_DATABASES |
["default"] |
Database aliases the migrate step applies, in order. |
FLAGSMITH_WAIT_FOR_MIGRATIONS_DATABASES |
["default"] |
Database aliases run-task-processor waits on before starting. |
FLAGSMITH_STARTUP_COMMANDS |
[] |
Management commands run, in order, between migrate and serve in migrate-and-serve. |
…and these environment variables:
| Environment variable | Default | Purpose |
|---|---|---|
SKIP_WAIT_FOR_DB |
unset | When set, skip waiting for the database. |
TASK_PROCESSOR_NUM_THREADS |
5 |
Number of worker threads. |
TASK_PROCESSOR_SLEEP_INTERVAL_MS |
500 |
Millis each worker waits before checking for new tasks (falls back to TASK_PROCESSOR_SLEEP_INTERVAL). |
TASK_PROCESSOR_GRACE_PERIOD_MS |
20000 |
Millis before a running task is considered stuck. |
TASK_PROCESSOR_QUEUE_POP_SIZE |
10 |
Tasks each worker pops from the queue per cycle. |
Pre-commit hooks
This repo provides a flagsmith-lint-tests hook that enforces test conventions:
- FT001: No module-level
Test*classes — use function-based tests - FT002: No
import unittest/from unittest import TestCase— use pytest (unittest.mockis fine) - FT003: Test names must follow
test_{subject}__{condition}__{expected} - FT004: Test bodies must contain
# Given,# When, and# Thencomments
To use in your repo, add to .pre-commit-config.yaml:
- repo: https://github.com/Flagsmith/flagsmith-common
rev: main
hooks:
- id: flagsmith-lint-tests
Use # noqa: FT003 (or any code) inline to suppress individual violations.
Test tools
Fixtures
assert_metric
To test your metrics using the assert_metric fixture:
from common.test_tools import AssertMetricFixture
def test_my_code__expected_metrics(assert_metric: AssertMetricFixture) -> None:
# When
my_code()
# Then
assert_metric(
name="flagsmith_distance_from_earth_au_sum",
labels={"engine_type": "solar_sail"},
value=1.0,
)
saas_mode
The saas_mode fixture makes all common.core.utils.is_saas calls return True.
enterprise_mode
The enterprise_mode fixture makes all common.core.utils.is_enterprise calls return True.
Markers
pytest.mark.saas_mode
Use this mark to auto-use the saas_mode fixture.
pytest.mark.enterprise_mode
Use this mark to auto-use the enterprise_mode fixture.
OpenTelemetry
Flagsmith supports exporting traces and structured logs over OTLP.
Configuration
OTel instrumentation is opt-in, controlled by environment variables:
| Variable | Description | Default |
|---|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT |
Base OTLP endpoint (e.g. http://collector:4318 for HTTP, http://collector:4317 for gRPC). If unset, no OTel setup occurs. |
(disabled) |
OTEL_EXPORTER_OTLP_PROTOCOL |
OTLP transport: grpc or http/protobuf (default). |
http/protobuf |
OTEL_SERVICE_NAME |
The service.name resource attribute. Defaults to flagsmith-task-processor when running the task processor. |
flagsmith-api |
OTEL_TRACING_EXCLUDED_URL_PATHS |
Comma-separated URL paths to exclude from tracing (e.g. health/liveness,health/readiness). |
(none) |
Standard OTEL_* env vars (e.g. OTEL_RESOURCE_ATTRIBUTES, OTEL_EXPORTER_OTLP_HEADERS) are also respected by the OTel SDK.
What gets configured
When OTEL_EXPORTER_OTLP_ENDPOINT is set, ensure_cli_env() sets up:
- Tracing:
TracerProviderwith OTLP span export (HTTP or gRPC, perOTEL_EXPORTER_OTLP_PROTOCOL), W3CTraceContext+Baggagepropagation, and auto-instrumentation for:- Django (
DjangoInstrumentor): creates a root span per HTTP request with span names formatted as{METHOD} {route_template}(e.g.GET /api/v1/projects/{pk}/). - psycopg2 (
Psycopg2Instrumentor): creates child spans for each SQL query withdb.system,db.statement, anddb.nameattributes. SQL commenter is enabled, adding trace context as SQL comments for database-side correlation. - Redis (
RedisInstrumentor): creates child spans for each Redis command withdb.systemanddb.statementattributes.
- Django (
- Structured log export: A structlog processor that emits each log event as both an OTLP log record and a span event (when an active span exists).
- Task processor trace propagation: When a task is enqueued via
TaskHandler.delay(), the current W3C trace context (including baggage) is serialized into the task'strace_contextfield. When the task processor executes the task, the context is extracted and a child span is created, linking the task execution back to the originating request trace. Span attributes (task_identifier,task_type,result) match the Prometheus metric labels for cross-signal correlation.
Emitting OTel log events via structlog
Use structlog as usual. The OTel processor captures events and maps them to OTLP log records:
import structlog
log = structlog.get_logger("code_references")
log.info("scan-created", code_references__count=3, feature__count=2)
This produces:
-
An OTLP log record with:
Body: scan-createdEventName: code_references.scan_created(logger name +inflection.underscoreof the event)Severity: INFOAttributes: code_references.count=3, feature.count=2(double underscores are converted to dots)- W3C Baggage entries from the current OTel context are copied into log attributes (e.g.
amplitude.device_id,amplitude.session_id).
-
A span event on the active span (if one exists) with the same name and attributes. This makes structlog events visible in trace backends (e.g. SigNoz's "Events" tab) without requiring separate log correlation. When no span is active (e.g. during startup or management commands), only the OTLP log record is emitted.
Metrics
Flagsmith uses Prometheus to track performance metrics.
The following default metrics are exposed:
Common metrics
flagsmith_build_info: Has the labelsversionandci_commit_sha.flagsmith_http_server_request_duration_seconds: Histogram labeled withmethod,route, andresponse_status.flagsmith_http_server_requests_total: Counter labeled withmethod,route, andresponse_status.flagsmith_http_server_response_size_bytes: Histogram labeled withmethod,route, andresponse_status.flagsmith_task_processor_enqueued_tasks_total: Counter labeled withtask_identifier.
Task Processor metrics
flagsmith_task_processor_finished_tasks_total: Counter labeled withtask_identifier,task_type("recurring","standard") andresult("success","failure").flagsmith_task_processor_task_duration_seconds: Histogram labeled withtask_identifier,task_type("recurring","standard") andresult("success","failure").
Guidelines
Try to come up with meaningful metrics to cover your feature with when developing it. Refer to Prometheus best practices when naming your metric and labels.
As a reasonable default, Flagsmith metrics are expected to be namespaced with the "flagsmith_" prefix.
Define your metrics in a metrics.py module of your Django application — see example. Contrary to Prometheus Python client examples and documentation, please name a metric variable exactly as your metric name.
It's generally a good idea to allow users to define histogram buckets of their own. Flagsmith accepts a PROMETHEUS_HISTOGRAM_BUCKETS setting so users can customise their buckets. To honour the setting, use the common.prometheus.Histogram class when defining your histograms. When using prometheus_client.Histogram directly, please expose a dedicated setting like so:
import prometheus_client
from django.conf import settings
flagsmith_distance_from_earth_au = prometheus_client.Histogram(
"flagsmith_distance_from_earth_au",
"Distance from Earth in astronomical units",
labels=["engine_type"],
buckets=settings.DISTANCE_FROM_EARTH_AU_HISTOGRAM_BUCKETS,
)
For testing your metrics, refer to assert_metric documentation.
Metadata
Release files for flagsmith-common 3.15.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| flagsmith_common-3.15.0.tar.gz | 62.6 kB | With provenance | Details for flagsmith_common-3.15.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| flagsmith_common-3.15.0-py3-none-any.whl | Python 3 | none | any | Details for flagsmith_common-3.15.0-py3-none-any.whl |
Total release size: 163.4 kB
Release files / flagsmith_common-3.15.0.tar.gz
| Download URL | flagsmith_common-3.15.0.tar.gz |
|---|---|
| Size | 62.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
970290aa8b5aa0399c6e9f64b843de95e5a458fcbe0623425a6d15305c7e3559
|
|
BLAKE2b-256 checksum How to use checksums |
59a4c8605bb5fbf853a8d06838241d89d0b70ea93b5c5b68c234f51c660b74b0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.
Transparency logRelease files / flagsmith_common-3.15.0-py3-none-any.whl
| Download URL | flagsmith_common-3.15.0-py3-none-any.whl |
|---|---|
| Size | 100.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bd7b54970fb0ebe563fe007ef25cbc45bd1a4c586648d8c2bf6df27e82469565
|
|
BLAKE2b-256 checksum How to use checksums |
975708475196dc972e2d980b267d0bea25353c402983a8ea13f9fdfed6745f38
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.
Transparency logSecurity for flagsmith-common 3.15.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for flagsmith_common-3.15.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.