diff --git a/api/Makefile b/api/Makefile index d3748d0f1936..9e93284b522d 100644 --- a/api/Makefile +++ b/api/Makefile @@ -165,6 +165,7 @@ integrate-private-tests: .PHONY: generate-docs generate-docs: generate-flagsmith-sdk-openapi poetry run flagsmith docgen metrics > ../docs/docs/administration-and-security/platform-configuration/metrics.md + poetry run flagsmith docgen events > ../docs/docs/administration-and-security/platform-configuration/events.md .PHONY: add-known-sdk-version add-known-sdk-version: diff --git a/api/poetry.lock b/api/poetry.lock index caf301439f5b..4c7bf6313e7d 100644 --- a/api/poetry.lock +++ b/api/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "annotated-types" @@ -2058,48 +2058,52 @@ name = "flagsmith-common" version = "3.7.0" description = "Flagsmith's common library" optional = false -python-versions = "<4.0,>=3.11" +python-versions = ">=3.11,<4.0" groups = ["main", "dev", "licensing", "workflows"] -files = [ - {file = "flagsmith_common-3.7.0-py3-none-any.whl", hash = "sha256:96013812c926df779f9f960759d15607cb724656a1ccd921b923887774aa8f1b"}, - {file = "flagsmith_common-3.7.0.tar.gz", hash = "sha256:b0a87ba4d4039970b4aedb8baa207f970cf48514386be6b3874961803980b56d"}, -] - -[package.dependencies] -backoff = {version = ">=2.2.1,<3.0.0", optional = true, markers = "extra == \"task-processor\""} -django = {version = ">4,<6", optional = true, markers = "extra == \"common-core\" or extra == \"task-processor\""} -django-health-check = {version = "*", optional = true, markers = "extra == \"common-core\" or extra == \"task-processor\""} -djangorestframework = {version = "*", optional = true, markers = "extra == \"common-core\""} -djangorestframework-recursive = {version = "*", optional = true, markers = "extra == \"common-core\""} -drf-spectacular = {version = ">=0.28.0,<1", optional = true, markers = "extra == \"common-core\""} -drf-writable-nested = {version = "*", optional = true, markers = "extra == \"common-core\""} -environs = {version = "<15", optional = true, markers = "extra == \"common-core\""} -flagsmith-flag-engine = {version = ">6", optional = true, markers = "extra == \"flagsmith-schemas\""} -gunicorn = {version = ">=19.1", optional = true, markers = "extra == \"common-core\""} -inflection = {version = "*", optional = true, markers = "extra == \"common-core\""} -opentelemetry-api = {version = ">=1.25,<2", optional = true, markers = "extra == \"common-core\" or extra == \"task-processor\""} -opentelemetry-exporter-otlp-proto-http = {version = ">=1.25,<2", optional = true, markers = "extra == \"common-core\""} -opentelemetry-instrumentation-django = {version = ">=0.46b0,<1", optional = true, markers = "extra == \"common-core\""} -opentelemetry-instrumentation-psycopg2 = {version = ">=0.46b0,<1", optional = true, markers = "extra == \"common-core\""} -opentelemetry-instrumentation-redis = {version = ">=0.46b0,<1", optional = true, markers = "extra == \"common-core\""} -opentelemetry-sdk = {version = ">=1.25,<2", optional = true, markers = "extra == \"common-core\""} -prometheus-client = {version = ">=0.0.16", optional = true, markers = "extra == \"common-core\" or extra == \"task-processor\""} -psycopg2-binary = {version = ">=2.9,<3", optional = true, markers = "extra == \"common-core\""} -pyfakefs = {version = ">=5,<6", optional = true, markers = "extra == \"test-tools\""} -pytest-django = {version = ">=4,<5", optional = true, markers = "extra == \"test-tools\""} -redis = {version = ">=5,<6", optional = true, markers = "extra == \"common-core\""} -requests = {version = "*", optional = true, markers = "extra == \"common-core\""} -sentry-sdk = {version = ">=2.0.0,<3.0.0", optional = true, markers = "extra == \"common-core\""} -simplejson = {version = ">=3,<4", optional = true, markers = "extra == \"common-core\" and extra == \"flagsmith-schemas\""} -structlog = {version = ">=24.4,<26", optional = true, markers = "extra == \"common-core\""} -typing-extensions = {version = "*", optional = true, markers = "extra == \"common-core\" or extra == \"flagsmith-schemas\""} +files = [] +develop = false + +[package.dependencies] +backoff = {version = ">=2.2.1,<3.0.0", optional = true} +django = {version = ">4,<6", optional = true} +django-health-check = {version = "*", optional = true} +djangorestframework = {version = "*", optional = true} +djangorestframework-recursive = {version = "*", optional = true} +drf-spectacular = {version = ">=0.28.0,<1", optional = true} +drf-writable-nested = {version = "*", optional = true} +environs = {version = "<15", optional = true} +flagsmith-flag-engine = {version = ">6", optional = true} +gunicorn = {version = ">=19.1", optional = true} +inflection = {version = "*", optional = true} +opentelemetry-api = {version = ">=1.25,<2", optional = true} +opentelemetry-exporter-otlp-proto-http = {version = ">=1.25,<2", optional = true} +opentelemetry-instrumentation-django = {version = ">=0.46b0,<1", optional = true} +opentelemetry-instrumentation-psycopg2 = {version = ">=0.46b0,<1", optional = true} +opentelemetry-instrumentation-redis = {version = ">=0.46b0,<1", optional = true} +opentelemetry-sdk = {version = ">=1.25,<2", optional = true} +prometheus-client = {version = ">=0.0.16", optional = true} +psycopg2-binary = {version = ">=2.9,<3", optional = true} +pyfakefs = {version = ">=5,<6", optional = true} +pytest-django = {version = ">=4,<5", optional = true} +redis = {version = ">=5,<6", optional = true} +requests = {version = "*", optional = true} +sentry-sdk = {version = ">=2.0.0,<3.0.0", optional = true} +simplejson = {version = ">=3,<4", optional = true} +structlog = {version = ">=24.4,<26", optional = true} +typing_extensions = {version = "*", optional = true} [package.extras] -common-core = ["django (>4,<6)", "django-health-check", "djangorestframework", "djangorestframework-recursive", "drf-spectacular (>=0.28.0,<1)", "drf-writable-nested", "environs (<15)", "gunicorn (>=19.1)", "inflection", "opentelemetry-api (>=1.25,<2)", "opentelemetry-exporter-otlp-proto-http (>=1.25,<2)", "opentelemetry-instrumentation-django (>=0.46b0,<1)", "opentelemetry-instrumentation-psycopg2 (>=0.46b0,<1)", "opentelemetry-instrumentation-redis (>=0.46b0,<1)", "opentelemetry-sdk (>=1.25,<2)", "prometheus-client (>=0.0.16)", "psycopg2-binary (>=2.9,<3)", "redis (>=5,<6)", "requests", "sentry-sdk (>=2.0.0,<3.0.0)", "simplejson (>=3,<4)", "structlog (>=24.4,<26)", "typing-extensions"] -flagsmith-schemas = ["flagsmith-flag-engine (>6)", "simplejson", "typing-extensions"] +common-core = ["django (>4,<6)", "django-health-check", "djangorestframework", "djangorestframework-recursive", "drf-spectacular (>=0.28.0,<1)", "drf-writable-nested", "environs (<15)", "gunicorn (>=19.1)", "inflection", "opentelemetry-api (>=1.25,<2)", "opentelemetry-exporter-otlp-proto-http (>=1.25,<2)", "opentelemetry-instrumentation-django (>=0.46b0,<1)", "opentelemetry-instrumentation-psycopg2 (>=0.46b0,<1)", "opentelemetry-instrumentation-redis (>=0.46b0,<1)", "opentelemetry-sdk (>=1.25,<2)", "prometheus-client (>=0.0.16)", "psycopg2-binary (>=2.9,<3)", "redis (>=5,<6)", "requests", "sentry-sdk (>=2.0.0,<3.0.0)", "simplejson (>=3,<4)", "structlog (>=24.4,<26)", "typing_extensions"] +flagsmith-schemas = ["flagsmith-flag-engine (>6)", "simplejson", "typing_extensions"] task-processor = ["backoff (>=2.2.1,<3.0.0)", "django (>4,<6)", "django-health-check", "opentelemetry-api (>=1.25,<2)", "prometheus-client (>=0.0.16)"] test-tools = ["pyfakefs (>=5,<6)", "pytest-django (>=4,<5)"] +[package.source] +type = "git" +url = "https://github.com/Flagsmith/flagsmith-common.git" +reference = "feat/docgen-events-scanner" +resolved_reference = "d1669ef0f9f01cb2f515b5fe08acb3a6fc58803e" + [[package]] name = "flagsmith-flag-engine" version = "10.0.3" @@ -4587,7 +4591,6 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -6022,4 +6025,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">3.11,<3.14" -content-hash = "ae5e279244c9c4b4e8eeff86d7c62cf7559501c3c2317969eb5e28ead8a14153" +content-hash = "6cde8e5e9d176dfcee62210357b2eede3129debe4353433710b47e970f0737eb" diff --git a/api/pyproject.toml b/api/pyproject.toml index 7e3d676f2dfe..e9c9a483cc7a 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -178,7 +178,7 @@ pygithub = "~2.8" hubspot-api-client = "^12.0.0" djangorestframework-dataclasses = "^1.3.1" pyotp = "^2.9.0" -flagsmith-common = { version = ">=3.5.0,<4", extras = [ +flagsmith-common = { git = "https://github.com/Flagsmith/flagsmith-common.git", branch = "feat/docgen-events-scanner", extras = [ "common-core", "flagsmith-schemas", "task-processor", @@ -263,7 +263,7 @@ types-psycopg2 = "^2.9.21.20250121" types-python-dateutil = "^2.9.0.20241206" types-pytz = "^2025.1.0.20250204" ruff = "^0.9.7" -flagsmith-common = { version = "*", extras = ["test-tools"] } +flagsmith-common = { git = "https://github.com/Flagsmith/flagsmith-common.git", branch = "feat/docgen-events-scanner", extras = ["test-tools"] } pytest-responses = "^0.5.1" diff-cover = "^10.1.0" django-debug-toolbar = "*" diff --git a/docs/docs/administration-and-security/platform-configuration/events.md b/docs/docs/administration-and-security/platform-configuration/events.md new file mode 100644 index 000000000000..e02d795d6c10 --- /dev/null +++ b/docs/docs/administration-and-security/platform-configuration/events.md @@ -0,0 +1,243 @@ +--- +title: Events +sidebar_label: Events +sidebar_position: 30 +--- + +Flagsmith backend emits [OpenTelemetry events](https://opentelemetry.io/docs/specs/otel/logs/data-model/#events) +that can be ingested to downstream observability systems and/or a data warehouse of your choice via OTLP. +To learn how to configure this, see [OpenTelemetry](deployment-self-hosting/scaling-and-performance/opentelemetry). + +## Event catalog + +### `app_analytics.no-analytics-database-configured` + +Logged at `warning` from: + - `api/app_analytics/analytics_db_service.py:74` + - `api/app_analytics/analytics_db_service.py:210` + +Attributes: + - `details` + +### `code_references.cleanup_issues.created` + +Logged at `info` from: + - `api/integrations/github/views.py:388` + +Attributes: + - `issues_created.count` + - `organisation.id` + +### `code_references.scan.created` + +Logged at `info` from: + - `api/projects/code_references/views.py:41` + +Attributes: + - `code_references.count` + - `feature.count` + - `organisation.id` + +### `dynamodb.environment-document-compressed` + +Logged at `info` from: + - `api/environments/dynamodb/wrappers/environment_wrapper.py:92` + +Attributes: + - `environment_api_key` + - `environment_id` + +### `feature_health.feature-health-event-dismissal-not-supported` + +Logged at `warning` from: + - `api/features/feature_health/services.py:127` + +Attributes: + - `feature_health_event_external_id` + - `feature_health_event_id` + - `feature_health_event_type` + - `provider_name` + +### `feature_health.feature-health-provider-error` + +Logged at `error` from: + - `api/features/feature_health/services.py:50` + +Attributes: + - `exc_info` + - `provider_id` + - `provider_name` + +### `feature_health.invalid-feature-health-webhook-path-requested` + +Logged at `warning` from: + - `api/features/feature_health/providers/services.py:30` + +Attributes: + - `path` + +### `gitlab.api-call-failed` + +Logged at `error` from: + - `api/integrations/gitlab/views/browse_gitlab.py:58` + +Attributes: + - `exc_info` + +### `gitlab.configuration-created` + +Logged at `info` from: + - `api/integrations/gitlab/views/configuration.py:26` + +Attributes: + - `gitlab_instance_url` + - `organisation.id` + - `project.id` + +### `gitlab.configuration-updated` + +Logged at `info` from: + - `api/integrations/gitlab/views/configuration.py:34` + +Attributes: + - `gitlab_instance_url` + - `organisation.id` + - `project.id` + +### `gitlab.issues-fetched` + +Logged at `info` from: + - `api/integrations/gitlab/views/browse_gitlab.py:133` + +Attributes: + - `gitlab_project_id` + - `project.id` + +### `gitlab.merge-requests-fetched` + +Logged at `info` from: + - `api/integrations/gitlab/views/browse_gitlab.py:159` + +Attributes: + - `gitlab_project_id` + - `project.id` + +### `gitlab.projects-fetched` + +Logged at `info` from: + - `api/integrations/gitlab/views/browse_gitlab.py:108` + +Attributes: + - `project.id` + +### `launch_darkly.import-failed` + +Logged at `exception` from: + - `api/integrations/launch_darkly/tasks.py:36` + +Attributes: + - `import_request_id` + - `ld_project_key` + - `organisation_id` + - `project_id` + +### `launch_darkly.import-rate-limit-reached` + +Logged at `warning` from: + - `api/integrations/launch_darkly/tasks.py:26` + +Attributes: + - `error_message` + - `import_request_id` + - `ld_project_key` + - `organisation_id` + - `project_id` + - `retry_at` + +### `platform_hub.no-analytics-database-configured` + +Logged at `warning` from: + - `api/platform_hub/services.py:116` + - `api/platform_hub/services.py:206` + - `api/platform_hub/services.py:428` + +Attributes: + +### `segments.serializers.segment-revision-created` + +Logged at `info` from: + - `api/segments/serializers.py:141` + +Attributes: + - `revision_id` + - `segment_id` + +### `sentry_change_tracking.integration-error` + +Logged at `warning` from: + - `api/integrations/sentry/change_tracking.py:112` + +Attributes: + - `feature_name` + - `sentry_action` + - `sentry_response_body` + - `sentry_response_status` + +### `sentry_change_tracking.request-failure` + +Logged at `warning` from: + - `api/integrations/sentry/change_tracking.py:102` + +Attributes: + - `error` + - `feature_name` + - `sentry_action` + +### `sentry_change_tracking.sending` + +Logged at `debug` from: + - `api/integrations/sentry/change_tracking.py:87` + +Attributes: + - `feature_name` + - `headers` + - `payload` + - `sentry_action` + - `url` + +### `sentry_change_tracking.success` + +Logged at `info` from: + - `api/integrations/sentry/change_tracking.py:109` + +Attributes: + - `feature_name` + - `sentry_action` + +### `workflows.change_request.committed` + +Logged at `info` from: + - `api/core/workflows_services.py:39` + +Attributes: + - `environment.id` + - `feature_states.count` + - `organisation.id` + +### `workflows.missing-live-segment` + +Logged at `warning` from: + - `api/core/workflows_services.py:114` + +Attributes: + - `draft_segment` + +### `workflows.segment-revision-created` + +Logged at `info` from: + - `api/core/workflows_services.py:119` + +Attributes: + - `revision_id` + - `segment_id` +