From 434d565fda10823c0404ed8153867515d808a2ec Mon Sep 17 00:00:00 2001 From: Munir Abdinur Date: Wed, 15 Jan 2025 11:19:43 -0500 Subject: [PATCH] chore(telemetry): improves typing for telemetry metric namespaces (#11564) Follow up to: #11565 Prevents telemetry metrics from being queued with an invalid namespace ## Checklist - [ ] PR author has checked that all the criteria below are met - The PR description includes an overview of the change - The PR description articulates the motivation for the change - The change includes tests OR the PR description describes a testing strategy - The PR description notes risks associated with the change, if any - Newly-added code is easy to change - The change follows the [library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) - The change includes or references documentation updates if necessary - Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [ ] Reviewer has checked that all the criteria below are met - Title is accurate - All changes are related to the pull request's stated goal - Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes - Testing strategy adequately addresses listed risks - Newly-added code is easy to change - Release note makes sense to a user of the library - If necessary, author has acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment - Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting) --- ddtrace/_monkey.py | 6 +- ddtrace/_trace/processor/__init__.py | 4 +- ddtrace/_trace/telemetry.py | 5 +- ddtrace/appsec/_iast/_metrics.py | 16 ++--- ddtrace/appsec/_metrics.py | 10 +-- .../ci_visibility/telemetry/api_request.py | 12 ++-- .../ci_visibility/telemetry/constants.py | 3 - .../ci_visibility/telemetry/coverage.py | 12 ++-- .../telemetry/early_flake_detection.py | 6 +- .../ci_visibility/telemetry/events.py | 20 ++++-- .../internal/ci_visibility/telemetry/git.py | 38 ++++++---- .../internal/ci_visibility/telemetry/itr.py | 16 +++-- .../ci_visibility/telemetry/payload.py | 22 ++++-- ddtrace/internal/telemetry/constants.py | 10 ++- .../internal/telemetry/metrics_namespaces.py | 21 ++++-- ddtrace/internal/telemetry/writer.py | 19 ++--- ddtrace/llmobs/_evaluators/ragas/base.py | 6 +- ddtrace/llmobs/_evaluators/runner.py | 4 +- ddtrace/llmobs/_evaluators/sampler.py | 6 +- ddtrace/settings/_otel_remapper.py | 8 +-- tests/appsec/appsec/test_telemetry.py | 12 ++-- tests/appsec/contrib_appsec/utils.py | 2 +- tests/appsec/iast/test_telemetry.py | 10 +-- tests/telemetry/app.py | 4 +- tests/telemetry/test_telemetry_metrics.py | 69 +++++++++---------- tests/tracer/test_processors.py | 31 ++++++--- 26 files changed, 216 insertions(+), 156 deletions(-) diff --git a/ddtrace/_monkey.py b/ddtrace/_monkey.py index ff7747ce395..d2306ace9ce 100644 --- a/ddtrace/_monkey.py +++ b/ddtrace/_monkey.py @@ -6,6 +6,7 @@ from wrapt.importer import when_imported from ddtrace.appsec import load_common_appsec_modules +from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE from .appsec._iast._utils import _is_iast_enabled from .internal import telemetry @@ -186,7 +187,10 @@ def on_import(hook): ) telemetry.telemetry_writer.add_integration(module, False, PATCH_MODULES.get(module) is True, str(e)) telemetry.telemetry_writer.add_count_metric( - "tracers", "integration_errors", 1, (("integration_name", module), ("error_type", type(e).__name__)) + TELEMETRY_NAMESPACE.TRACERS, + "integration_errors", + 1, + (("integration_name", module), ("error_type", type(e).__name__)), ) else: if hasattr(imported_module, "get_versions"): diff --git a/ddtrace/_trace/processor/__init__.py b/ddtrace/_trace/processor/__init__.py index 03d815b86d2..fc59a64828b 100644 --- a/ddtrace/_trace/processor/__init__.py +++ b/ddtrace/_trace/processor/__init__.py @@ -26,7 +26,7 @@ from ddtrace.internal.sampling import is_single_span_sampled from ddtrace.internal.service import ServiceStatusError from ddtrace.internal.telemetry.constants import TELEMETRY_LOG_LEVEL -from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE_TAG_TRACER +from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE from ddtrace.internal.writer import TraceWriter @@ -392,6 +392,6 @@ def _queue_span_count_metrics(self, metric_name: str, tag_name: str, min_count: if config._telemetry_enabled and sum(self._span_metrics[metric_name].values()) >= min_count: for tag_value, count in self._span_metrics[metric_name].items(): telemetry.telemetry_writer.add_count_metric( - TELEMETRY_NAMESPACE_TAG_TRACER, metric_name, count, tags=((tag_name, tag_value),) + TELEMETRY_NAMESPACE.TRACERS, metric_name, count, tags=((tag_name, tag_value),) ) self._span_metrics[metric_name] = defaultdict(int) diff --git a/ddtrace/_trace/telemetry.py b/ddtrace/_trace/telemetry.py index f9cd9ef79b9..929acd101ec 100644 --- a/ddtrace/_trace/telemetry.py +++ b/ddtrace/_trace/telemetry.py @@ -2,11 +2,12 @@ from typing import Tuple from ddtrace.internal.telemetry import telemetry_writer +from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE def record_span_pointer_calculation(context: str, span_pointer_count: int) -> None: telemetry_writer.add_count_metric( - namespace="tracers", + namespace=TELEMETRY_NAMESPACE.TRACERS, name="span_pointer_calculation", value=1, tags=(("context", context), ("count", _span_pointer_count_to_tag(span_pointer_count))), @@ -45,7 +46,7 @@ def record_span_pointer_calculation_issue( tags += additional_tags telemetry_writer.add_count_metric( - namespace="tracers", + namespace=TELEMETRY_NAMESPACE.TRACERS, name="span_pointer_calculation.issue", value=1, tags=tags, diff --git a/ddtrace/appsec/_iast/_metrics.py b/ddtrace/appsec/_iast/_metrics.py index e9e0f604e69..35e2729565e 100644 --- a/ddtrace/appsec/_iast/_metrics.py +++ b/ddtrace/appsec/_iast/_metrics.py @@ -12,7 +12,7 @@ from ddtrace.internal import telemetry from ddtrace.internal.logger import get_logger from ddtrace.internal.telemetry.constants import TELEMETRY_LOG_LEVEL -from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE_TAG_IAST +from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE from ddtrace.settings.asm import config as asm_config @@ -73,19 +73,19 @@ def _set_metric_iast_instrumented_source(source_type): from ._taint_tracking import origin_to_str telemetry.telemetry_writer.add_count_metric( - TELEMETRY_NAMESPACE_TAG_IAST, "instrumented.source", 1, (("source_type", origin_to_str(source_type)),) + TELEMETRY_NAMESPACE.IAST, "instrumented.source", 1, (("source_type", origin_to_str(source_type)),) ) @metric_verbosity(TELEMETRY_MANDATORY_VERBOSITY) def _set_metric_iast_instrumented_propagation(): - telemetry.telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE_TAG_IAST, "instrumented.propagation", 1) + telemetry.telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.IAST, "instrumented.propagation", 1) @metric_verbosity(TELEMETRY_MANDATORY_VERBOSITY) def _set_metric_iast_instrumented_sink(vulnerability_type, counter=1): telemetry.telemetry_writer.add_count_metric( - TELEMETRY_NAMESPACE_TAG_IAST, "instrumented.sink", counter, (("vulnerability_type", vulnerability_type),) + TELEMETRY_NAMESPACE.IAST, "instrumented.sink", counter, (("vulnerability_type", vulnerability_type),) ) @@ -94,14 +94,14 @@ def _set_metric_iast_executed_source(source_type): from ._taint_tracking import origin_to_str telemetry.telemetry_writer.add_count_metric( - TELEMETRY_NAMESPACE_TAG_IAST, "executed.source", 1, (("source_type", origin_to_str(source_type)),) + TELEMETRY_NAMESPACE.IAST, "executed.source", 1, (("source_type", origin_to_str(source_type)),) ) @metric_verbosity(TELEMETRY_INFORMATION_VERBOSITY) def _set_metric_iast_executed_sink(vulnerability_type): telemetry.telemetry_writer.add_count_metric( - TELEMETRY_NAMESPACE_TAG_IAST, "executed.sink", 1, (("vulnerability_type", vulnerability_type),) + TELEMETRY_NAMESPACE.IAST, "executed.sink", 1, (("vulnerability_type", vulnerability_type),) ) @@ -115,9 +115,7 @@ def _request_tainted(): def _set_metric_iast_request_tainted(): total_objects_tainted = _request_tainted() if total_objects_tainted > 0: - telemetry.telemetry_writer.add_count_metric( - TELEMETRY_NAMESPACE_TAG_IAST, "request.tainted", total_objects_tainted - ) + telemetry.telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.IAST, "request.tainted", total_objects_tainted) def _set_span_tag_iast_request_tainted(span): diff --git a/ddtrace/appsec/_metrics.py b/ddtrace/appsec/_metrics.py index cbe8490d717..3d5c7e3e59f 100644 --- a/ddtrace/appsec/_metrics.py +++ b/ddtrace/appsec/_metrics.py @@ -5,7 +5,7 @@ from ddtrace.internal import telemetry from ddtrace.internal.logger import get_logger from ddtrace.internal.telemetry.constants import TELEMETRY_LOG_LEVEL -from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE_TAG_APPSEC +from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE log = get_logger(__name__) @@ -36,7 +36,7 @@ def _set_waf_updates_metric(info): tags = (("waf_version", DDWAF_VERSION),) telemetry.telemetry_writer.add_count_metric( - TELEMETRY_NAMESPACE_TAG_APPSEC, + TELEMETRY_NAMESPACE.APPSEC, "waf.updates", 1.0, tags=tags, @@ -56,7 +56,7 @@ def _set_waf_init_metric(info): tags = (("waf_version", DDWAF_VERSION),) telemetry.telemetry_writer.add_count_metric( - TELEMETRY_NAMESPACE_TAG_APPSEC, + TELEMETRY_NAMESPACE.APPSEC, "waf.init", 1.0, tags=tags, @@ -90,7 +90,7 @@ def _set_waf_request_metrics(*args): ) telemetry.telemetry_writer.add_count_metric( - TELEMETRY_NAMESPACE_TAG_APPSEC, + TELEMETRY_NAMESPACE.APPSEC, "waf.requests", 1.0, tags=tags_request, @@ -101,7 +101,7 @@ def _set_waf_request_metrics(*args): for rule_type, value in rasp[t].items(): if value: telemetry.telemetry_writer.add_count_metric( - TELEMETRY_NAMESPACE_TAG_APPSEC, + TELEMETRY_NAMESPACE.APPSEC, n, float(value), tags=_TYPES_AND_TAGS.get(rule_type, ()) + (("waf_version", DDWAF_VERSION),), diff --git a/ddtrace/internal/ci_visibility/telemetry/api_request.py b/ddtrace/internal/ci_visibility/telemetry/api_request.py index 076cc0cca77..77f3ea5f626 100644 --- a/ddtrace/internal/ci_visibility/telemetry/api_request.py +++ b/ddtrace/internal/ci_visibility/telemetry/api_request.py @@ -1,10 +1,10 @@ import dataclasses from typing import Optional -from ddtrace.internal.ci_visibility.telemetry.constants import CIVISIBILITY_TELEMETRY_NAMESPACE as _NAMESPACE from ddtrace.internal.ci_visibility.telemetry.constants import ERROR_TYPES from ddtrace.internal.logger import get_logger from ddtrace.internal.telemetry import telemetry_writer +from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE log = get_logger(__name__) @@ -32,13 +32,15 @@ def record_api_request( error, ) - telemetry_writer.add_count_metric(_NAMESPACE, f"{metric_names.count}", 1) - telemetry_writer.add_distribution_metric(_NAMESPACE, f"{metric_names.duration}", duration) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.CIVISIBILITY, f"{metric_names.count}", 1) + telemetry_writer.add_distribution_metric(TELEMETRY_NAMESPACE.CIVISIBILITY, f"{metric_names.duration}", duration) if response_bytes is not None: if metric_names.response_bytes is not None: # We don't always want to record response bytes (for settings requests), so assume that no metric name # means we don't want to record it. - telemetry_writer.add_distribution_metric(_NAMESPACE, f"{metric_names.response_bytes}", response_bytes) + telemetry_writer.add_distribution_metric( + TELEMETRY_NAMESPACE.CIVISIBILITY, f"{metric_names.response_bytes}", response_bytes + ) if error is not None: record_api_request_error(metric_names.error, error) @@ -46,4 +48,4 @@ def record_api_request( def record_api_request_error(error_metric_name: str, error: ERROR_TYPES): log.debug("Recording early flake detection request error telemetry: %s", error) - telemetry_writer.add_count_metric(_NAMESPACE, error_metric_name, 1, (("error_type", error),)) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.CIVISIBILITY, error_metric_name, 1, (("error_type", error),)) diff --git a/ddtrace/internal/ci_visibility/telemetry/constants.py b/ddtrace/internal/ci_visibility/telemetry/constants.py index dad54511c04..191338e86e9 100644 --- a/ddtrace/internal/ci_visibility/telemetry/constants.py +++ b/ddtrace/internal/ci_visibility/telemetry/constants.py @@ -1,9 +1,6 @@ from enum import Enum -CIVISIBILITY_TELEMETRY_NAMESPACE = "civisibility" - - class ERROR_TYPES(str, Enum): TIMEOUT = "timeout" NETWORK = "network" diff --git a/ddtrace/internal/ci_visibility/telemetry/coverage.py b/ddtrace/internal/ci_visibility/telemetry/coverage.py index e3370fbee6e..392196f7236 100644 --- a/ddtrace/internal/ci_visibility/telemetry/coverage.py +++ b/ddtrace/internal/ci_visibility/telemetry/coverage.py @@ -3,10 +3,10 @@ from typing import Optional from typing import Tuple -from ddtrace.internal.ci_visibility.telemetry.constants import CIVISIBILITY_TELEMETRY_NAMESPACE as _NAMESPACE from ddtrace.internal.ci_visibility.telemetry.constants import TEST_FRAMEWORKS from ddtrace.internal.logger import get_logger from ddtrace.internal.telemetry import telemetry_writer +from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE log = get_logger(__name__) @@ -30,7 +30,7 @@ def record_code_coverage_started(coverage_library: COVERAGE_LIBRARY, test_framew _tags: List[Tuple[str, str]] = [("library", coverage_library)] if test_framework is not None: _tags.append(("test_framework", test_framework)) - telemetry_writer.add_count_metric(_NAMESPACE, COVERAGE_TELEMETRY.STARTED, 1, tuple(_tags)) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.CIVISIBILITY, COVERAGE_TELEMETRY.STARTED, 1, tuple(_tags)) def record_code_coverage_finished(coverage_library: COVERAGE_LIBRARY, test_framework: Optional[TEST_FRAMEWORKS] = None): @@ -38,19 +38,19 @@ def record_code_coverage_finished(coverage_library: COVERAGE_LIBRARY, test_frame _tags: List[Tuple[str, str]] = [("library", coverage_library)] if test_framework is not None: _tags.append(("test_framework", test_framework)) - telemetry_writer.add_count_metric(_NAMESPACE, COVERAGE_TELEMETRY.FINISHED, 1, tuple(_tags)) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.CIVISIBILITY, COVERAGE_TELEMETRY.FINISHED, 1, tuple(_tags)) def record_code_coverage_empty(): log.debug("Recording code coverage empty telemetry") - telemetry_writer.add_count_metric(_NAMESPACE, COVERAGE_TELEMETRY.IS_EMPTY, 1) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.CIVISIBILITY, COVERAGE_TELEMETRY.IS_EMPTY, 1) def record_code_coverage_files(count_files: int): log.debug("Recording code coverage files telemetry: %s", count_files) - telemetry_writer.add_distribution_metric(_NAMESPACE, COVERAGE_TELEMETRY.FILES, count_files) + telemetry_writer.add_distribution_metric(TELEMETRY_NAMESPACE.CIVISIBILITY, COVERAGE_TELEMETRY.FILES, count_files) def record_code_coverage_error(): log.debug("Recording code coverage error telemetry") - telemetry_writer.add_count_metric(_NAMESPACE, COVERAGE_TELEMETRY.ERRORS, 1) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.CIVISIBILITY, COVERAGE_TELEMETRY.ERRORS, 1) diff --git a/ddtrace/internal/ci_visibility/telemetry/early_flake_detection.py b/ddtrace/internal/ci_visibility/telemetry/early_flake_detection.py index f8a512e7048..b9e9e48d021 100644 --- a/ddtrace/internal/ci_visibility/telemetry/early_flake_detection.py +++ b/ddtrace/internal/ci_visibility/telemetry/early_flake_detection.py @@ -1,8 +1,8 @@ from enum import Enum -from ddtrace.internal.ci_visibility.telemetry.constants import CIVISIBILITY_TELEMETRY_NAMESPACE as _NAMESPACE from ddtrace.internal.logger import get_logger from ddtrace.internal.telemetry import telemetry_writer +from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE log = get_logger(__name__) @@ -19,5 +19,7 @@ class EARLY_FLAKE_DETECTION_TELEMETRY(str, Enum): def record_early_flake_detection_tests_count(early_flake_detection_count: int): log.debug("Recording early flake detection tests count telemetry: %s", early_flake_detection_count) telemetry_writer.add_distribution_metric( - _NAMESPACE, EARLY_FLAKE_DETECTION_TELEMETRY.RESPONSE_TESTS.value, early_flake_detection_count + TELEMETRY_NAMESPACE.CIVISIBILITY, + EARLY_FLAKE_DETECTION_TELEMETRY.RESPONSE_TESTS.value, + early_flake_detection_count, ) diff --git a/ddtrace/internal/ci_visibility/telemetry/events.py b/ddtrace/internal/ci_visibility/telemetry/events.py index 34c603c3b03..b630ee96413 100644 --- a/ddtrace/internal/ci_visibility/telemetry/events.py +++ b/ddtrace/internal/ci_visibility/telemetry/events.py @@ -3,11 +3,11 @@ from typing import Optional from typing import Tuple -from ddtrace.internal.ci_visibility.telemetry.constants import CIVISIBILITY_TELEMETRY_NAMESPACE as _NAMESPACE from ddtrace.internal.ci_visibility.telemetry.constants import EVENT_TYPES from ddtrace.internal.ci_visibility.telemetry.constants import TEST_FRAMEWORKS from ddtrace.internal.logger import get_logger from ddtrace.internal.telemetry import telemetry_writer +from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE log = get_logger(__name__) @@ -67,7 +67,7 @@ def _record_event( if early_flake_detection_abort_reason and event == EVENTS_TELEMETRY.FINISHED and event_type == EVENT_TYPES.SESSION: _tags.append(("early_flake_detection_abort_reason", early_flake_detection_abort_reason)) - telemetry_writer.add_count_metric(_NAMESPACE, event.value, 1, tuple(_tags)) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.CIVISIBILITY, event.value, 1, tuple(_tags)) def record_event_created( @@ -117,11 +117,19 @@ def record_event_finished( def record_manual_api_event_created(event_type: EVENT_TYPES): # Note: _created suffix is added in cases we were to change the metric name in the future. # The current metric applies to event creation even though it does not specify it - telemetry_writer.add_count_metric(_NAMESPACE, EVENTS_TELEMETRY.MANUAL_API_EVENT, 1, (("event_type", event_type),)) + telemetry_writer.add_count_metric( + TELEMETRY_NAMESPACE.CIVISIBILITY, + EVENTS_TELEMETRY.MANUAL_API_EVENT, + 1, + (("event_type", event_type),) + ) def record_events_enqueued_for_serialization(events_count: int): - telemetry_writer.add_count_metric(_NAMESPACE, EVENTS_TELEMETRY.ENQUEUED_FOR_SERIALIZATION, events_count) + telemetry_writer.add_count_metric( + TELEMETRY_NAMESPACE.CIVISIBILITY, + EVENTS_TELEMETRY.ENQUEUED_FOR_SERIALIZATION, + events_count) def record_event_created_test( @@ -139,7 +147,7 @@ def record_event_created_test( if is_benchmark: tags.append(("is_benchmark", "true")) - telemetry_writer.add_count_metric(_NAMESPACE, EVENTS_TELEMETRY.FINISHED, 1, tuple(tags)) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.CIVISIBILITY, EVENTS_TELEMETRY.FINISHED, 1, tuple(tags)) def record_event_finished_test( @@ -190,4 +198,4 @@ def record_event_finished_test( if is_quarantined: tags.append(("is_quarantined", "true")) - telemetry_writer.add_count_metric(_NAMESPACE, EVENTS_TELEMETRY.FINISHED, 1, tuple(tags)) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.CIVISIBILITY, EVENTS_TELEMETRY.FINISHED, 1, tuple(tags)) diff --git a/ddtrace/internal/ci_visibility/telemetry/git.py b/ddtrace/internal/ci_visibility/telemetry/git.py index faf01621cde..41bca64a8fd 100644 --- a/ddtrace/internal/ci_visibility/telemetry/git.py +++ b/ddtrace/internal/ci_visibility/telemetry/git.py @@ -1,11 +1,11 @@ from typing import Optional -from ddtrace.internal.ci_visibility.telemetry.constants import CIVISIBILITY_TELEMETRY_NAMESPACE as _NAMESPACE from ddtrace.internal.ci_visibility.telemetry.constants import ERROR_TYPES from ddtrace.internal.ci_visibility.telemetry.constants import GIT_TELEMETRY from ddtrace.internal.ci_visibility.telemetry.constants import GIT_TELEMETRY_COMMANDS from ddtrace.internal.logger import get_logger from ddtrace.internal.telemetry import telemetry_writer +from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE log = get_logger(__name__) @@ -14,35 +14,45 @@ def record_git_command(command: GIT_TELEMETRY_COMMANDS, duration: float, exit_code: Optional[int]) -> None: log.debug("Recording git command telemetry: %s, %s, %s", command, duration, exit_code) tags = (("command", command),) - telemetry_writer.add_count_metric(_NAMESPACE, GIT_TELEMETRY.COMMAND_COUNT, 1, tags) - telemetry_writer.add_distribution_metric(_NAMESPACE, GIT_TELEMETRY.COMMAND_MS, duration, tags) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.CIVISIBILITY, GIT_TELEMETRY.COMMAND_COUNT, 1, tags) + telemetry_writer.add_distribution_metric(TELEMETRY_NAMESPACE.CIVISIBILITY, GIT_TELEMETRY.COMMAND_MS, duration, tags) if exit_code is not None and exit_code != 0: error_tags = (("command", command), ("exit_code", str(exit_code))) - telemetry_writer.add_count_metric(_NAMESPACE, GIT_TELEMETRY.COMMAND_ERRORS, 1, error_tags) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.CIVISIBILITY, GIT_TELEMETRY.COMMAND_ERRORS, 1, error_tags) def record_search_commits(duration: float, error: Optional[ERROR_TYPES] = None) -> None: log.debug("Recording search commits telemetry: %s, %s", duration, error) - telemetry_writer.add_count_metric(_NAMESPACE, GIT_TELEMETRY.SEARCH_COMMITS_COUNT, 1) - telemetry_writer.add_distribution_metric(_NAMESPACE, GIT_TELEMETRY.SEARCH_COMMITS_MS, duration) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.CIVISIBILITY, GIT_TELEMETRY.SEARCH_COMMITS_COUNT, 1) + telemetry_writer.add_distribution_metric( + TELEMETRY_NAMESPACE.CIVISIBILITY, GIT_TELEMETRY.SEARCH_COMMITS_MS, duration + ) if error is not None: error_tags = (("error_type", str(error)),) - telemetry_writer.add_count_metric(_NAMESPACE, GIT_TELEMETRY.SEARCH_COMMITS_ERRORS, 1, error_tags) + telemetry_writer.add_count_metric( + TELEMETRY_NAMESPACE.CIVISIBILITY, GIT_TELEMETRY.SEARCH_COMMITS_ERRORS, 1, error_tags + ) def record_objects_pack_request(duration: float, error: Optional[ERROR_TYPES] = None) -> None: log.debug("Recording objects pack request telmetry: %s, %s", duration, error) - telemetry_writer.add_count_metric(_NAMESPACE, GIT_TELEMETRY.OBJECTS_PACK_COUNT, 1) - telemetry_writer.add_distribution_metric(_NAMESPACE, GIT_TELEMETRY.OBJECTS_PACK_MS, duration) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.CIVISIBILITY, GIT_TELEMETRY.OBJECTS_PACK_COUNT, 1) + telemetry_writer.add_distribution_metric(TELEMETRY_NAMESPACE.CIVISIBILITY, GIT_TELEMETRY.OBJECTS_PACK_MS, duration) if error is not None: error_tags = (("error", error),) - telemetry_writer.add_count_metric(_NAMESPACE, GIT_TELEMETRY.OBJECTS_PACK_ERRORS, 1, error_tags) + telemetry_writer.add_count_metric( + TELEMETRY_NAMESPACE.CIVISIBILITY, GIT_TELEMETRY.OBJECTS_PACK_ERRORS, 1, error_tags + ) def record_objects_pack_data(num_files: int, num_bytes: int) -> None: log.debug("Recording objects pack data telemetry: %s, %s", num_files, num_bytes) - telemetry_writer.add_distribution_metric(_NAMESPACE, GIT_TELEMETRY.OBJECTS_PACK_BYTES, num_bytes) - telemetry_writer.add_distribution_metric(_NAMESPACE, GIT_TELEMETRY.OBJECTS_PACK_FILES, num_files) + telemetry_writer.add_distribution_metric( + TELEMETRY_NAMESPACE.CIVISIBILITY, GIT_TELEMETRY.OBJECTS_PACK_BYTES, num_bytes + ) + telemetry_writer.add_distribution_metric( + TELEMETRY_NAMESPACE.CIVISIBILITY, GIT_TELEMETRY.OBJECTS_PACK_FILES, num_files + ) def record_settings_response( @@ -87,4 +97,6 @@ def record_settings_response( response_tags.append(("quarantine_enabled", "true")) if response_tags: - telemetry_writer.add_count_metric(_NAMESPACE, GIT_TELEMETRY.SETTINGS_RESPONSE, 1, tuple(response_tags)) + telemetry_writer.add_count_metric( + TELEMETRY_NAMESPACE.CIVISIBILITY, GIT_TELEMETRY.SETTINGS_RESPONSE, 1, tuple(response_tags) + ) diff --git a/ddtrace/internal/ci_visibility/telemetry/itr.py b/ddtrace/internal/ci_visibility/telemetry/itr.py index 210a4103734..b8bf6889471 100644 --- a/ddtrace/internal/ci_visibility/telemetry/itr.py +++ b/ddtrace/internal/ci_visibility/telemetry/itr.py @@ -2,10 +2,10 @@ import functools from ddtrace.internal.ci_visibility.constants import SUITE -from ddtrace.internal.ci_visibility.telemetry.constants import CIVISIBILITY_TELEMETRY_NAMESPACE as _NAMESPACE from ddtrace.internal.ci_visibility.telemetry.constants import EVENT_TYPES from ddtrace.internal.logger import get_logger from ddtrace.internal.telemetry import telemetry_writer +from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE log = get_logger(__name__) @@ -40,18 +40,24 @@ def wrapper(event_type: str): @_enforce_event_is_test_or_suite def record_itr_skipped(event_type: EVENT_TYPES): log.debug("Recording itr skipped telemetry for %s", event_type) - telemetry_writer.add_count_metric(_NAMESPACE, ITR_TELEMETRY.SKIPPED, 1, (("event_type", event_type.value),)) + telemetry_writer.add_count_metric( + TELEMETRY_NAMESPACE.CIVISIBILITY, ITR_TELEMETRY.SKIPPED, 1, (("event_type", event_type.value),) + ) @_enforce_event_is_test_or_suite def record_itr_unskippable(event_type: EVENT_TYPES): log.debug("Recording itr unskippable telemetry for %s", event_type) - telemetry_writer.add_count_metric(_NAMESPACE, ITR_TELEMETRY.UNSKIPPABLE, 1, (("event_type", event_type.value),)) + telemetry_writer.add_count_metric( + TELEMETRY_NAMESPACE.CIVISIBILITY, ITR_TELEMETRY.UNSKIPPABLE, 1, (("event_type", event_type.value),) + ) def record_itr_forced_run(event_type: EVENT_TYPES): log.debug("Recording itr forced run telemetry for %s", event_type) - telemetry_writer.add_count_metric(_NAMESPACE, ITR_TELEMETRY.FORCED_RUN, 1, (("event_type", event_type.value),)) + telemetry_writer.add_count_metric( + TELEMETRY_NAMESPACE.CIVISIBILITY, ITR_TELEMETRY.FORCED_RUN, 1, (("event_type", event_type.value),) + ) def record_skippable_count(skippable_count: int, skipping_level: str): @@ -60,4 +66,4 @@ def record_skippable_count(skippable_count: int, skipping_level: str): if skipping_level == SUITE else SKIPPABLE_TESTS_TELEMETRY.RESPONSE_TESTS ) - telemetry_writer.add_count_metric(_NAMESPACE, skippable_count_metric, skippable_count) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.CIVISIBILITY, skippable_count_metric, skippable_count) diff --git a/ddtrace/internal/ci_visibility/telemetry/payload.py b/ddtrace/internal/ci_visibility/telemetry/payload.py index 1cf41d306ff..f5dd7a9ca00 100644 --- a/ddtrace/internal/ci_visibility/telemetry/payload.py +++ b/ddtrace/internal/ci_visibility/telemetry/payload.py @@ -1,8 +1,8 @@ from enum import Enum -from ddtrace.internal.ci_visibility.telemetry.constants import CIVISIBILITY_TELEMETRY_NAMESPACE as _NAMESPACE from ddtrace.internal.logger import get_logger from ddtrace.internal.telemetry import telemetry_writer +from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE log = get_logger(__name__) @@ -31,38 +31,46 @@ class REQUEST_ERROR_TYPE(str, Enum): def record_endpoint_payload_bytes(endpoint: ENDPOINT, nbytes: int) -> None: log.debug("Recording endpoint payload bytes: %s, %s", endpoint, nbytes) tags = (("endpoint", endpoint.value),) - telemetry_writer.add_distribution_metric(_NAMESPACE, ENDPOINT_PAYLOAD_TELEMETRY.BYTES.value, nbytes, tags) + telemetry_writer.add_distribution_metric( + TELEMETRY_NAMESPACE.CIVISIBILITY, ENDPOINT_PAYLOAD_TELEMETRY.BYTES.value, nbytes, tags + ) def record_endpoint_payload_request(endpoint: ENDPOINT) -> None: log.debug("Recording endpoint payload request: %s", endpoint) tags = (("endpoint", endpoint.value),) - telemetry_writer.add_count_metric(_NAMESPACE, ENDPOINT_PAYLOAD_TELEMETRY.REQUESTS_COUNT.value, 1, tags) + telemetry_writer.add_count_metric( + TELEMETRY_NAMESPACE.CIVISIBILITY, ENDPOINT_PAYLOAD_TELEMETRY.REQUESTS_COUNT.value, 1, tags + ) def record_endpoint_payload_request_time(endpoint: ENDPOINT, seconds: float) -> None: log.debug("Recording endpoint payload request time: %s, %s seconds", endpoint, seconds) tags = (("endpoint", endpoint.value),) telemetry_writer.add_distribution_metric( - _NAMESPACE, ENDPOINT_PAYLOAD_TELEMETRY.REQUESTS_MS.value, seconds * 1000, tags + TELEMETRY_NAMESPACE.CIVISIBILITY, ENDPOINT_PAYLOAD_TELEMETRY.REQUESTS_MS.value, seconds * 1000, tags ) def record_endpoint_payload_request_error(endpoint: ENDPOINT, error_type: REQUEST_ERROR_TYPE) -> None: log.debug("Recording endpoint payload request error: %s, %s", endpoint, error_type) tags = (("endpoint", endpoint.value), ("error_type", error_type)) - telemetry_writer.add_count_metric(_NAMESPACE, ENDPOINT_PAYLOAD_TELEMETRY.REQUESTS_ERRORS.value, 1, tags) + telemetry_writer.add_count_metric( + TELEMETRY_NAMESPACE.CIVISIBILITY, ENDPOINT_PAYLOAD_TELEMETRY.REQUESTS_ERRORS.value, 1, tags + ) def record_endpoint_payload_events_count(endpoint: ENDPOINT, count: int) -> None: log.debug("Recording endpoint payload events count: %s, %s", endpoint, count) tags = (("endpoint", endpoint.value),) - telemetry_writer.add_distribution_metric(_NAMESPACE, ENDPOINT_PAYLOAD_TELEMETRY.EVENTS_COUNT.value, count, tags) + telemetry_writer.add_distribution_metric( + TELEMETRY_NAMESPACE.CIVISIBILITY, ENDPOINT_PAYLOAD_TELEMETRY.EVENTS_COUNT.value, count, tags + ) def record_endpoint_payload_events_serialization_time(endpoint: ENDPOINT, seconds: float) -> None: log.debug("Recording endpoint payload serialization time: %s, %s seconds", endpoint, seconds) tags = (("endpoint", endpoint.value),) telemetry_writer.add_distribution_metric( - _NAMESPACE, ENDPOINT_PAYLOAD_TELEMETRY.EVENTS_SERIALIZATION_MS.value, seconds * 1000, tags + TELEMETRY_NAMESPACE.CIVISIBILITY, ENDPOINT_PAYLOAD_TELEMETRY.EVENTS_SERIALIZATION_MS.value, seconds * 1000, tags ) diff --git a/ddtrace/internal/telemetry/constants.py b/ddtrace/internal/telemetry/constants.py index 3298fdd7616..a809b5f2f4f 100644 --- a/ddtrace/internal/telemetry/constants.py +++ b/ddtrace/internal/telemetry/constants.py @@ -1,9 +1,13 @@ from enum import Enum -TELEMETRY_NAMESPACE_TAG_TRACER = "tracers" -TELEMETRY_NAMESPACE_TAG_APPSEC = "appsec" -TELEMETRY_NAMESPACE_TAG_IAST = "iast" +class TELEMETRY_NAMESPACE(Enum): + TRACERS = "tracers" + APPSEC = "appsec" + IAST = "iast" + CIVISIBILITY = "civisibility" + MLOBS = "mlobs" + TELEMETRY_TYPE_GENERATE_METRICS = "generate-metrics" TELEMETRY_TYPE_DISTRIBUTION = "distributions" diff --git a/ddtrace/internal/telemetry/metrics_namespaces.py b/ddtrace/internal/telemetry/metrics_namespaces.py index 927f6de775d..4b432ba330c 100644 --- a/ddtrace/internal/telemetry/metrics_namespaces.py +++ b/ddtrace/internal/telemetry/metrics_namespaces.py @@ -5,6 +5,7 @@ from typing import Type # noqa:F401 from ddtrace.internal import forksafe +from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE from ddtrace.internal.telemetry.constants import TELEMETRY_TYPE_DISTRIBUTION from ddtrace.internal.telemetry.constants import TELEMETRY_TYPE_GENERATE_METRICS from ddtrace.internal.telemetry.metrics import DistributionMetric @@ -34,23 +35,31 @@ def flush(self): } return namespace_metrics - def add_metric(self, metric_class, namespace, name, value=1.0, tags=None, interval=None): - # type: (Type[Metric], str, str, float, MetricTagType, Optional[float]) -> None + def add_metric( + self, + metric_class: Type[Metric], + namespace: TELEMETRY_NAMESPACE, + name: str, + value: float = 1.0, + tags: MetricTagType = None, + interval: Optional[float] = None, + ) -> None: """ Telemetry Metrics are stored in DD dashboards, check the metrics in datadoghq.com/metric/explorer. The metric will store in dashboard as "dd.instrumentation_telemetry_data." + namespace + "." + name """ - metric_id = Metric.get_id(name, namespace, tags, metric_class.metric_type) + namespace_str = namespace.value + metric_id = Metric.get_id(name, namespace_str, tags, metric_class.metric_type) if metric_class is DistributionMetric: metrics_type_payload = TELEMETRY_TYPE_DISTRIBUTION else: metrics_type_payload = TELEMETRY_TYPE_GENERATE_METRICS with self._lock: - existing_metric = self._metrics_data[metrics_type_payload][namespace].get(metric_id) + existing_metric = self._metrics_data[metrics_type_payload][namespace_str].get(metric_id) if existing_metric: existing_metric.add_point(value) else: - new_metric = metric_class(namespace, name, tags=tags, common=True, interval=interval) + new_metric = metric_class(namespace_str, name, tags=tags, common=True, interval=interval) new_metric.add_point(value) - self._metrics_data[metrics_type_payload][namespace][metric_id] = new_metric + self._metrics_data[metrics_type_payload][namespace_str][metric_id] = new_metric diff --git a/ddtrace/internal/telemetry/writer.py b/ddtrace/internal/telemetry/writer.py index 2be240c06fd..35a73d5e235 100644 --- a/ddtrace/internal/telemetry/writer.py +++ b/ddtrace/internal/telemetry/writer.py @@ -31,6 +31,7 @@ from . import modules from .constants import TELEMETRY_APM_PRODUCT from .constants import TELEMETRY_LOG_LEVEL # noqa:F401 +from .constants import TELEMETRY_NAMESPACE from .constants import TELEMETRY_TYPE_DISTRIBUTION from .constants import TELEMETRY_TYPE_GENERATE_METRICS from .constants import TELEMETRY_TYPE_LOGS @@ -337,7 +338,7 @@ def _app_started(self, register_app_shutdown=True): } # SOABI should help us identify which wheels people are getting from PyPI - self.add_configurations(get_python_config_vars()) # type: ignore + self.add_configurations(get_python_config_vars()) payload = { "configuration": self._flush_configuration_queue(), @@ -474,7 +475,6 @@ def add_configuration(self, configuration_name, configuration_value, origin="unk } def add_configurations(self, configuration_list): - # type: (List[Tuple[str, Union[bool, float, str], str]]) -> None """Creates and queues a list of configurations""" with self._service_lock: for name, value, _origin in configuration_list: @@ -485,7 +485,6 @@ def add_configurations(self, configuration_list): } def add_log(self, level, message, stack_trace="", tags=None): - # type: (TELEMETRY_LOG_LEVEL, str, str, Optional[Dict]) -> None """ Queues log. This event is meant to send library logs to Datadog’s backend through the Telemetry intake. This will make support cycles easier and ensure we know about potentially silent issues in libraries. @@ -507,8 +506,7 @@ def add_log(self, level, message, stack_trace="", tags=None): data["stack_trace"] = stack_trace self._logs.add(data) - def add_gauge_metric(self, namespace, name, value, tags=None): - # type: (str,str, float, MetricTagType) -> None + def add_gauge_metric(self, namespace: TELEMETRY_NAMESPACE, name: str, value: float, tags: MetricTagType = None): """ Queues gauge metric """ @@ -522,8 +520,7 @@ def add_gauge_metric(self, namespace, name, value, tags=None): self.interval, ) - def add_rate_metric(self, namespace, name, value=1.0, tags=None): - # type: (str,str, float, MetricTagType) -> None + def add_rate_metric(self, namespace: TELEMETRY_NAMESPACE, name: str, value: float, tags: MetricTagType = None): """ Queues rate metric """ @@ -537,8 +534,7 @@ def add_rate_metric(self, namespace, name, value=1.0, tags=None): self.interval, ) - def add_count_metric(self, namespace, name, value=1.0, tags=None): - # type: (str,str, float, MetricTagType) -> None + def add_count_metric(self, namespace: TELEMETRY_NAMESPACE, name: str, value: int = 1, tags: MetricTagType = None): """ Queues count metric """ @@ -551,8 +547,7 @@ def add_count_metric(self, namespace, name, value=1.0, tags=None): tags, ) - def add_distribution_metric(self, namespace, name, value=1.0, tags=None): - # type: (str,str, float, MetricTagType) -> None + def add_distribution_metric(self, namespace: TELEMETRY_NAMESPACE, name: str, value, tags: MetricTagType = None): """ Queues distributions metric """ @@ -708,7 +703,7 @@ def _telemetry_excepthook(self, tp, value, root_traceback): internal_index = dir_parts.index("internal") integration_name = dir_parts[internal_index + 1] self.add_count_metric( - "tracers", + TELEMETRY_NAMESPACE.TRACERS, "integration_errors", 1, (("integration_name", integration_name), ("error_type", tp.__name__)), diff --git a/ddtrace/llmobs/_evaluators/ragas/base.py b/ddtrace/llmobs/_evaluators/ragas/base.py index 23aa4cd3caa..10e89165a01 100644 --- a/ddtrace/llmobs/_evaluators/ragas/base.py +++ b/ddtrace/llmobs/_evaluators/ragas/base.py @@ -6,8 +6,8 @@ from ddtrace.internal.logger import get_logger from ddtrace.internal.telemetry import telemetry_writer -from ddtrace.internal.telemetry.constants import TELEMETRY_APM_PRODUCT from ddtrace.internal.telemetry.constants import TELEMETRY_LOG_LEVEL +from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE from ddtrace.internal.utils.version import parse_version from ddtrace.llmobs._constants import INTERNAL_CONTEXT_VARIABLE_KEYS from ddtrace.llmobs._constants import INTERNAL_QUERY_VARIABLE_KEYS @@ -121,7 +121,7 @@ def __init__(self, llmobs_service): raise NotImplementedError("Failed to load dependencies for `{}` evaluator".format(self.LABEL)) from e finally: telemetry_writer.add_count_metric( - namespace=TELEMETRY_APM_PRODUCT.LLMOBS, + namespace=TELEMETRY_NAMESPACE.MLOBS, name="evaluators.init", value=1, tags=( @@ -143,7 +143,7 @@ def run_and_submit_evaluation(self, span_event: dict): return score_result_or_failure, metric_metadata = self.evaluate(span_event) telemetry_writer.add_count_metric( - TELEMETRY_APM_PRODUCT.LLMOBS, + TELEMETRY_NAMESPACE.MLOBS, "evaluators.run", 1, tags=( diff --git a/ddtrace/llmobs/_evaluators/runner.py b/ddtrace/llmobs/_evaluators/runner.py index 3d26998f1b4..ffbe4a58d64 100644 --- a/ddtrace/llmobs/_evaluators/runner.py +++ b/ddtrace/llmobs/_evaluators/runner.py @@ -7,7 +7,7 @@ from ddtrace.internal.logger import get_logger from ddtrace.internal.periodic import PeriodicService from ddtrace.internal.telemetry import telemetry_writer -from ddtrace.internal.telemetry.constants import TELEMETRY_APM_PRODUCT +from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE from ddtrace.llmobs._evaluators.ragas.faithfulness import RagasFaithfulnessEvaluator from ddtrace.llmobs._evaluators.sampler import EvaluatorRunnerSampler @@ -56,7 +56,7 @@ def __init__(self, interval: float, llmobs_service=None, evaluators=None): raise e finally: telemetry_writer.add_count_metric( - namespace=TELEMETRY_APM_PRODUCT.LLMOBS, + namespace=TELEMETRY_NAMESPACE.MLOBS, name="evaluators.init", value=1, tags=( diff --git a/ddtrace/llmobs/_evaluators/sampler.py b/ddtrace/llmobs/_evaluators/sampler.py index 9dcb0759724..3598e90f7f3 100644 --- a/ddtrace/llmobs/_evaluators/sampler.py +++ b/ddtrace/llmobs/_evaluators/sampler.py @@ -9,8 +9,8 @@ from ddtrace._trace.sampling_rule import SamplingRule from ddtrace.internal.logger import get_logger from ddtrace.internal.telemetry import telemetry_writer -from ddtrace.internal.telemetry.constants import TELEMETRY_APM_PRODUCT from ddtrace.internal.telemetry.constants import TELEMETRY_LOG_LEVEL +from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE logger = get_logger(__name__) @@ -67,7 +67,7 @@ def parsing_failed_because(msg, maybe_throw_this): TELEMETRY_LOG_LEVEL.ERROR, message="Evaluator sampling parsing failure because: {}".format(msg) ) telemetry_writer.add_count_metric( - namespace=TELEMETRY_APM_PRODUCT.LLMOBS, + namespace=TELEMETRY_NAMESPACE.MLOBS, name="evaluators.error", value=1, tags=(("reason", "sampling_rule_parsing_failure"),), @@ -104,7 +104,7 @@ def parsing_failed_because(msg, maybe_throw_this): span_name = rule.get(EvaluatorRunnerSamplingRule.SPAN_NAME_KEY, SamplingRule.NO_RULE) evaluator_label = rule.get(EvaluatorRunnerSamplingRule.EVALUATOR_LABEL_KEY, SamplingRule.NO_RULE) telemetry_writer.add_distribution_metric( - TELEMETRY_APM_PRODUCT.LLMOBS, + TELEMETRY_NAMESPACE.MLOBS, "evaluators.rule_sample_rate", sample_rate, tags=(("evaluator_label", evaluator_label), ("span_name", span_name)), diff --git a/ddtrace/settings/_otel_remapper.py b/ddtrace/settings/_otel_remapper.py index 8bdb313fdef..d3501c2e3fa 100644 --- a/ddtrace/settings/_otel_remapper.py +++ b/ddtrace/settings/_otel_remapper.py @@ -28,7 +28,7 @@ def __class_getitem__(self, item): from ..constants import VERSION_KEY from ..internal.logger import get_logger from ..internal.telemetry import telemetry_writer -from ..internal.telemetry.constants import TELEMETRY_NAMESPACE_TAG_TRACER +from ..internal.telemetry.constants import TELEMETRY_NAMESPACE log = get_logger(__name__) @@ -169,7 +169,7 @@ def otel_remapping(): if otel_env.startswith("OTEL_") and otel_env != "OTEL_PYTHON_CONTEXT": log.warning("OpenTelemetry configuration %s is not supported by Datadog.", otel_env) telemetry_writer.add_count_metric( - TELEMETRY_NAMESPACE_TAG_TRACER, + TELEMETRY_NAMESPACE.TRACERS, "otel.env.unsupported", 1, (("config_opentelemetry", otel_env.lower()),), @@ -185,7 +185,7 @@ def otel_remapping(): otel_value, ) telemetry_writer.add_count_metric( - TELEMETRY_NAMESPACE_TAG_TRACER, + TELEMETRY_NAMESPACE.TRACERS, "otel.env.hiding", 1, (("config_opentelemetry", otel_env.lower()), ("config_datadog", dd_env.lower())), @@ -205,7 +205,7 @@ def otel_remapping(): otel_value, ) telemetry_writer.add_count_metric( - TELEMETRY_NAMESPACE_TAG_TRACER, + TELEMETRY_NAMESPACE.TRACERS, "otel.env.invalid", 1, (("config_opentelemetry", otel_env.lower()), ("config_datadog", dd_env.lower())), diff --git a/tests/appsec/appsec/test_telemetry.py b/tests/appsec/appsec/test_telemetry.py index 8678820e8d6..6932a7bdba0 100644 --- a/tests/appsec/appsec/test_telemetry.py +++ b/tests/appsec/appsec/test_telemetry.py @@ -12,7 +12,7 @@ from ddtrace.appsec._processor import AppSecSpanProcessor from ddtrace.contrib.trace_utils import set_http_meta from ddtrace.ext import SpanTypes -from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE_TAG_APPSEC +from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE from ddtrace.internal.telemetry.constants import TELEMETRY_TYPE_DISTRIBUTION from ddtrace.internal.telemetry.constants import TELEMETRY_TYPE_GENERATE_METRICS import tests.appsec.rules as rules @@ -27,7 +27,7 @@ def _assert_generate_metrics(metrics_result, is_rule_triggered=False, is_blocked_request=False): - generate_metrics = metrics_result[TELEMETRY_TYPE_GENERATE_METRICS][TELEMETRY_NAMESPACE_TAG_APPSEC] + generate_metrics = metrics_result[TELEMETRY_TYPE_GENERATE_METRICS][TELEMETRY_NAMESPACE.APPSEC.value] assert len(generate_metrics) == 2, "Expected 2 generate_metrics" for _metric_id, metric in generate_metrics.items(): if metric.name == "waf.requests": @@ -44,7 +44,7 @@ def _assert_generate_metrics(metrics_result, is_rule_triggered=False, is_blocked def _assert_distributions_metrics(metrics_result, is_rule_triggered=False, is_blocked_request=False): - distributions_metrics = metrics_result[TELEMETRY_TYPE_DISTRIBUTION][TELEMETRY_NAMESPACE_TAG_APPSEC] + distributions_metrics = metrics_result[TELEMETRY_TYPE_DISTRIBUTION][TELEMETRY_NAMESPACE.APPSEC.value] assert len(distributions_metrics) == 2, "Expected 2 distributions_metrics" for _metric_id, metric in distributions_metrics.items(): @@ -69,8 +69,8 @@ def test_metrics_when_appsec_doesnt_runs(telemetry_writer, tracer): rules.Config(), ) metrics_data = telemetry_writer._namespace._metrics_data - assert len(metrics_data[TELEMETRY_TYPE_GENERATE_METRICS][TELEMETRY_NAMESPACE_TAG_APPSEC]) == 0 - assert len(metrics_data[TELEMETRY_TYPE_DISTRIBUTION][TELEMETRY_NAMESPACE_TAG_APPSEC]) == 0 + assert len(metrics_data[TELEMETRY_TYPE_GENERATE_METRICS][TELEMETRY_NAMESPACE.APPSEC.value]) == 0 + assert len(metrics_data[TELEMETRY_TYPE_DISTRIBUTION][TELEMETRY_NAMESPACE.APPSEC.value]) == 0 def test_metrics_when_appsec_runs(telemetry_writer, tracer): @@ -136,7 +136,7 @@ def test_log_metric_error_ddwaf_timeout(telemetry_writer, tracer): assert len(list_metrics_logs) == 0 generate_metrics = telemetry_writer._namespace._metrics_data[TELEMETRY_TYPE_GENERATE_METRICS][ - TELEMETRY_NAMESPACE_TAG_APPSEC + TELEMETRY_NAMESPACE.APPSEC.value ] timeout_found = False diff --git a/tests/appsec/contrib_appsec/utils.py b/tests/appsec/contrib_appsec/utils.py index d3691e2bea3..d7aa077052f 100644 --- a/tests/appsec/contrib_appsec/utils.py +++ b/tests/appsec/contrib_appsec/utils.py @@ -1379,7 +1379,7 @@ def validate_top_function(trace): assert get_tag(http.STATUS_CODE) == str(code), (get_tag(http.STATUS_CODE), code) if code == 200: assert self.body(response).startswith(f"{endpoint} endpoint") - telemetry_calls = {(c.__name__, f"{ns}.{nm}", t): v for (c, ns, nm, v, t), _ in mocked.call_args_list} + telemetry_calls = {(c.__name__, f"{ns.value}.{nm}", t): v for (c, ns, nm, v, t), _ in mocked.call_args_list} if asm_enabled and ep_enabled and action_level > 0: self.check_rules_triggered([rule] * (1 if action_level == 2 else 2), root_span) assert self.check_for_stack_trace(root_span) diff --git a/tests/appsec/iast/test_telemetry.py b/tests/appsec/iast/test_telemetry.py index 106d9408815..139dab79918 100644 --- a/tests/appsec/iast/test_telemetry.py +++ b/tests/appsec/iast/test_telemetry.py @@ -28,7 +28,7 @@ from ddtrace.contrib.internal.sqlalchemy.patch import patch as sqli_sqlalchemy_patch from ddtrace.contrib.internal.sqlite3.patch import patch as sqli_sqlite3_patch from ddtrace.ext import SpanTypes -from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE_TAG_IAST +from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE from ddtrace.internal.telemetry.constants import TELEMETRY_TYPE_GENERATE_METRICS from tests.appsec.iast.aspects.conftest import _iast_patched_module from tests.appsec.utils import asm_context @@ -38,7 +38,7 @@ def _assert_instrumented_sink(telemetry_writer, vuln_type): metrics_result = telemetry_writer._namespace._metrics_data - generate_metrics = metrics_result[TELEMETRY_TYPE_GENERATE_METRICS][TELEMETRY_NAMESPACE_TAG_IAST] + generate_metrics = metrics_result[TELEMETRY_TYPE_GENERATE_METRICS][TELEMETRY_NAMESPACE.IAST.value] assert len(generate_metrics) == 1, "Expected 1 generate_metrics" assert [metric.name for metric in generate_metrics.values()] == ["instrumented.sink"] assert [metric._tags for metric in generate_metrics.values()] == [(("vulnerability_type", vuln_type),)] @@ -87,7 +87,7 @@ def test_metric_executed_sink(no_request_sampling, telemetry_writer, caplog): metrics_result = telemetry_writer._namespace._metrics_data - generate_metrics = metrics_result[TELEMETRY_TYPE_GENERATE_METRICS][TELEMETRY_NAMESPACE_TAG_IAST].values() + generate_metrics = metrics_result[TELEMETRY_TYPE_GENERATE_METRICS][TELEMETRY_NAMESPACE.IAST.value].values() assert len(generate_metrics) == 1 # Remove potential sinks from internal usage of the lib (like http.client, used to communicate with # the agent) @@ -151,7 +151,7 @@ def test_metric_instrumented_propagation(no_request_sampling, telemetry_writer): _iast_patched_module("benchmarks.bm.iast_fixtures.str_methods") metrics_result = telemetry_writer._namespace._metrics_data - generate_metrics = metrics_result[TELEMETRY_TYPE_GENERATE_METRICS][TELEMETRY_NAMESPACE_TAG_IAST] + generate_metrics = metrics_result[TELEMETRY_TYPE_GENERATE_METRICS][TELEMETRY_NAMESPACE.IAST.value] # Remove potential sinks from internal usage of the lib (like http.client, used to communicate with # the agent) filtered_metrics = [metric.name for metric in generate_metrics.values() if metric.name != "executed.sink"] @@ -175,7 +175,7 @@ def test_metric_request_tainted(no_request_sampling, telemetry_writer): metrics_result = telemetry_writer._namespace._metrics_data - generate_metrics = metrics_result[TELEMETRY_TYPE_GENERATE_METRICS][TELEMETRY_NAMESPACE_TAG_IAST] + generate_metrics = metrics_result[TELEMETRY_TYPE_GENERATE_METRICS][TELEMETRY_NAMESPACE.IAST.value] # Remove potential sinks from internal usage of the lib (like http.client, used to communicate with # the agent) filtered_metrics = [metric.name for metric in generate_metrics.values() if metric.name != "executed.sink"] diff --git a/tests/telemetry/app.py b/tests/telemetry/app.py index 7390d9b5da6..ae2b9932c9f 100644 --- a/tests/telemetry/app.py +++ b/tests/telemetry/app.py @@ -1,7 +1,7 @@ from flask import Flask from ddtrace.internal.telemetry import telemetry_writer -from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE_TAG_TRACER +from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE app = Flask(__name__) @@ -23,7 +23,7 @@ def starting_app_view(): @app.route("/count_metric") def metrics_view(): telemetry_writer.add_count_metric( - TELEMETRY_NAMESPACE_TAG_TRACER, + TELEMETRY_NAMESPACE.TRACERS, "test_metric", 1.0, ) diff --git a/tests/telemetry/test_telemetry_metrics.py b/tests/telemetry/test_telemetry_metrics.py index a3ea6051b8b..d1061d57770 100644 --- a/tests/telemetry/test_telemetry_metrics.py +++ b/tests/telemetry/test_telemetry_metrics.py @@ -3,8 +3,7 @@ from mock.mock import ANY from ddtrace.internal.telemetry.constants import TELEMETRY_LOG_LEVEL -from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE_TAG_APPSEC -from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE_TAG_TRACER +from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE from ddtrace.internal.telemetry.constants import TELEMETRY_TYPE_DISTRIBUTION from ddtrace.internal.telemetry.constants import TELEMETRY_TYPE_GENERATE_METRICS from tests.utils import override_global_config @@ -13,7 +12,7 @@ def _assert_metric( test_agent, expected_metrics, - namespace=TELEMETRY_NAMESPACE_TAG_TRACER, + namespace=TELEMETRY_NAMESPACE.TRACERS, type_paypload=TELEMETRY_TYPE_GENERATE_METRICS, ): assert len(expected_metrics) > 0, "expected_metrics should not be empty" @@ -23,7 +22,7 @@ def _assert_metric( metrics = [] for event in metrics_events: - if event["payload"]["namespace"] == namespace: + if event["payload"]["namespace"] == namespace.value: for metric in event["payload"]["series"]: metric["tags"].sort() metrics.append(metric) @@ -49,7 +48,7 @@ def _assert_logs(test_agent, expected_logs): def test_send_metric_flush_and_generate_metrics_series_is_restarted(telemetry_writer, test_agent_session, mock_time): """Check the queue of metrics is empty after run periodic method of PeriodicService""" - telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE_TAG_TRACER, "test-metric2", 1, (("a", "b"),)) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.TRACERS, "test-metric2", 1, (("a", "b"),)) expected_series = [ { "common": True, @@ -62,7 +61,7 @@ def test_send_metric_flush_and_generate_metrics_series_is_restarted(telemetry_wr _assert_metric(test_agent_session, expected_series) - telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE_TAG_TRACER, "test-metric2", 1, (("a", "b"),)) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.TRACERS, "test-metric2", 1, (("a", "b"),)) _assert_metric(test_agent_session, expected_series) @@ -75,8 +74,8 @@ def test_send_metric_datapoint_equal_type_and_tags_yields_single_series( But in Datadog, a datapoint also includes tags, which declare all the various scopes the datapoint belongs to https://www.datadoghq.com/blog/the-power-of-tagged-metrics/#whats-a-metric-tag """ - telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE_TAG_TRACER, "test-metric", 2, (("a", "b"),)) - telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE_TAG_TRACER, "test-metric", 3, (("a", "b"),)) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.TRACERS, "test-metric", 2, (("a", "b"),)) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.TRACERS, "test-metric", 3, (("a", "b"),)) expected_series = [ { @@ -99,9 +98,9 @@ def test_send_metric_datapoint_equal_type_different_tags_yields_multiple_series( But in Datadog, a datapoint also includes tags, which declare all the various scopes the datapoint belongs to https://www.datadoghq.com/blog/the-power-of-tagged-metrics/#whats-a-metric-tag """ - telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE_TAG_TRACER, "test-metric", 4, (("a", "b"),)) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.TRACERS, "test-metric", 4, (("a", "b"),)) telemetry_writer.add_count_metric( - TELEMETRY_NAMESPACE_TAG_TRACER, + TELEMETRY_NAMESPACE.TRACERS, "test-metric", 5, ( @@ -109,7 +108,7 @@ def test_send_metric_datapoint_equal_type_different_tags_yields_multiple_series( ("c", "True"), ), ) - telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE_TAG_TRACER, "test-metric", 6, tuple()) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.TRACERS, "test-metric", 6, tuple()) expected_series = [ { @@ -144,8 +143,8 @@ def test_send_metric_datapoint_with_different_types(telemetry_writer, test_agent But in Datadog, a datapoint also includes tags, which declare all the various scopes the datapoint belongs to https://www.datadoghq.com/blog/the-power-of-tagged-metrics/#whats-a-metric-tag """ - telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE_TAG_TRACER, "test-metric", 1, (("a", "b"),)) - telemetry_writer.add_gauge_metric(TELEMETRY_NAMESPACE_TAG_TRACER, "test-metric", 1, (("a", "b"),)) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.TRACERS, "test-metric", 1, (("a", "b"),)) + telemetry_writer.add_gauge_metric(TELEMETRY_NAMESPACE.TRACERS, "test-metric", 1, (("a", "b"),)) expected_series = [ {"common": True, "metric": "test-metric", "points": [[1642544540, 1.0]], "tags": ["a:b"], "type": "count"}, @@ -162,11 +161,11 @@ def test_send_metric_datapoint_with_different_types(telemetry_writer, test_agent def test_send_tracers_count_metric(telemetry_writer, test_agent_session, mock_time): - telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE_TAG_TRACER, "test-metric", 1, (("a", "b"),)) - telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE_TAG_TRACER, "test-metric", 1, (("a", "b"),)) - telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE_TAG_TRACER, "test-metric", 1, tuple()) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.TRACERS, "test-metric", 1, (("a", "b"),)) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.TRACERS, "test-metric", 1, (("a", "b"),)) + telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.TRACERS, "test-metric", 1, tuple()) telemetry_writer.add_count_metric( - TELEMETRY_NAMESPACE_TAG_TRACER, + TELEMETRY_NAMESPACE.TRACERS, "test-metric", 1, ( @@ -203,13 +202,13 @@ def test_send_tracers_count_metric(telemetry_writer, test_agent_session, mock_ti def test_send_appsec_rate_metric(telemetry_writer, test_agent_session, mock_time): telemetry_writer.add_rate_metric( - TELEMETRY_NAMESPACE_TAG_APPSEC, + TELEMETRY_NAMESPACE.APPSEC, "test-metric", 6, (("hi", "HELLO"), ("NAME", "CANDY")), ) - telemetry_writer.add_rate_metric(TELEMETRY_NAMESPACE_TAG_APPSEC, "test-metric", 6, tuple()) - telemetry_writer.add_rate_metric(TELEMETRY_NAMESPACE_TAG_APPSEC, "test-metric", 6, tuple()) + telemetry_writer.add_rate_metric(TELEMETRY_NAMESPACE.APPSEC, "test-metric", 6, tuple()) + telemetry_writer.add_rate_metric(TELEMETRY_NAMESPACE.APPSEC, "test-metric", 6, tuple()) expected_series = [ { @@ -230,12 +229,12 @@ def test_send_appsec_rate_metric(telemetry_writer, test_agent_session, mock_time }, ] - _assert_metric(test_agent_session, expected_series, namespace=TELEMETRY_NAMESPACE_TAG_APPSEC) + _assert_metric(test_agent_session, expected_series, namespace=TELEMETRY_NAMESPACE.APPSEC) def test_send_appsec_gauge_metric(telemetry_writer, test_agent_session, mock_time): telemetry_writer.add_gauge_metric( - TELEMETRY_NAMESPACE_TAG_APPSEC, + TELEMETRY_NAMESPACE.APPSEC, "test-metric", 5, ( @@ -243,8 +242,8 @@ def test_send_appsec_gauge_metric(telemetry_writer, test_agent_session, mock_tim ("NAME", "CANDY"), ), ) - telemetry_writer.add_gauge_metric(TELEMETRY_NAMESPACE_TAG_APPSEC, "test-metric", 5, (("a", "b"),)) - telemetry_writer.add_gauge_metric(TELEMETRY_NAMESPACE_TAG_APPSEC, "test-metric", 6, tuple()) + telemetry_writer.add_gauge_metric(TELEMETRY_NAMESPACE.APPSEC, "test-metric", 5, (("a", "b"),)) + telemetry_writer.add_gauge_metric(TELEMETRY_NAMESPACE.APPSEC, "test-metric", 6, tuple()) expected_series = [ { @@ -272,13 +271,13 @@ def test_send_appsec_gauge_metric(telemetry_writer, test_agent_session, mock_tim "type": "gauge", }, ] - _assert_metric(test_agent_session, expected_series, namespace=TELEMETRY_NAMESPACE_TAG_APPSEC) + _assert_metric(test_agent_session, expected_series, namespace=TELEMETRY_NAMESPACE.APPSEC) def test_send_appsec_distributions_metric(telemetry_writer, test_agent_session, mock_time): - telemetry_writer.add_distribution_metric(TELEMETRY_NAMESPACE_TAG_APPSEC, "test-metric", 4, tuple()) - telemetry_writer.add_distribution_metric(TELEMETRY_NAMESPACE_TAG_APPSEC, "test-metric", 5, tuple()) - telemetry_writer.add_distribution_metric(TELEMETRY_NAMESPACE_TAG_APPSEC, "test-metric", 6, tuple()) + telemetry_writer.add_distribution_metric(TELEMETRY_NAMESPACE.APPSEC, "test-metric", 4, tuple()) + telemetry_writer.add_distribution_metric(TELEMETRY_NAMESPACE.APPSEC, "test-metric", 5, tuple()) + telemetry_writer.add_distribution_metric(TELEMETRY_NAMESPACE.APPSEC, "test-metric", 6, tuple()) expected_series = [ { @@ -290,16 +289,16 @@ def test_send_appsec_distributions_metric(telemetry_writer, test_agent_session, _assert_metric( test_agent_session, expected_series, - namespace=TELEMETRY_NAMESPACE_TAG_APPSEC, + namespace=TELEMETRY_NAMESPACE.APPSEC, type_paypload=TELEMETRY_TYPE_DISTRIBUTION, ) def test_send_metric_flush_and_distributions_series_is_restarted(telemetry_writer, test_agent_session, mock_time): """Check the queue of metrics is empty after run periodic method of PeriodicService""" - telemetry_writer.add_distribution_metric(TELEMETRY_NAMESPACE_TAG_APPSEC, "test-metric", 4, tuple()) - telemetry_writer.add_distribution_metric(TELEMETRY_NAMESPACE_TAG_APPSEC, "test-metric", 5, tuple()) - telemetry_writer.add_distribution_metric(TELEMETRY_NAMESPACE_TAG_APPSEC, "test-metric", 6, tuple()) + telemetry_writer.add_distribution_metric(TELEMETRY_NAMESPACE.APPSEC, "test-metric", 4, tuple()) + telemetry_writer.add_distribution_metric(TELEMETRY_NAMESPACE.APPSEC, "test-metric", 5, tuple()) + telemetry_writer.add_distribution_metric(TELEMETRY_NAMESPACE.APPSEC, "test-metric", 6, tuple()) expected_series = [ { "metric": "test-metric", @@ -311,7 +310,7 @@ def test_send_metric_flush_and_distributions_series_is_restarted(telemetry_write _assert_metric( test_agent_session, expected_series, - namespace=TELEMETRY_NAMESPACE_TAG_APPSEC, + namespace=TELEMETRY_NAMESPACE.APPSEC, type_paypload=TELEMETRY_TYPE_DISTRIBUTION, ) @@ -323,12 +322,12 @@ def test_send_metric_flush_and_distributions_series_is_restarted(telemetry_write } ] - telemetry_writer.add_distribution_metric(TELEMETRY_NAMESPACE_TAG_APPSEC, "test-metric", 1, tuple()) + telemetry_writer.add_distribution_metric(TELEMETRY_NAMESPACE.APPSEC, "test-metric", 1, tuple()) _assert_metric( test_agent_session, expected_series, - namespace=TELEMETRY_NAMESPACE_TAG_APPSEC, + namespace=TELEMETRY_NAMESPACE.APPSEC, type_paypload=TELEMETRY_TYPE_DISTRIBUTION, ) diff --git a/tests/tracer/test_processors.py b/tests/tracer/test_processors.py index d06716e1825..a752275f3ab 100644 --- a/tests/tracer/test_processors.py +++ b/tests/tracer/test_processors.py @@ -26,6 +26,7 @@ from ddtrace.internal.processor.endpoint_call_counter import EndpointCallCounterProcessor from ddtrace.internal.sampling import SamplingMechanism from ddtrace.internal.sampling import SpanSamplingRule +from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE from tests.utils import DummyTracer from tests.utils import DummyWriter from tests.utils import override_global_config @@ -353,20 +354,34 @@ def test_span_creation_metrics(): mock_tm.assert_has_calls( [ - mock.call("tracers", "spans_created", 100, tags=(("integration_name", "datadog"),)), - mock.call("tracers", "spans_finished", 100, tags=(("integration_name", "datadog"),)), - mock.call("tracers", "spans_created", 100, tags=(("integration_name", "datadog"),)), - mock.call("tracers", "spans_finished", 100, tags=(("integration_name", "datadog"),)), - mock.call("tracers", "spans_created", 100, tags=(("integration_name", "datadog"),)), - mock.call("tracers", "spans_finished", 100, tags=(("integration_name", "datadog"),)), + mock.call( + TELEMETRY_NAMESPACE.TRACERS, "spans_created", 100, tags=(("integration_name", "datadog"),) + ), + mock.call( + TELEMETRY_NAMESPACE.TRACERS, "spans_finished", 100, tags=(("integration_name", "datadog"),) + ), + mock.call( + TELEMETRY_NAMESPACE.TRACERS, "spans_created", 100, tags=(("integration_name", "datadog"),) + ), + mock.call( + TELEMETRY_NAMESPACE.TRACERS, "spans_finished", 100, tags=(("integration_name", "datadog"),) + ), + mock.call( + TELEMETRY_NAMESPACE.TRACERS, "spans_created", 100, tags=(("integration_name", "datadog"),) + ), + mock.call( + TELEMETRY_NAMESPACE.TRACERS, "spans_finished", 100, tags=(("integration_name", "datadog"),) + ), ] ) mock_tm.reset_mock() aggr.shutdown(None) mock_tm.assert_has_calls( [ - mock.call("tracers", "spans_created", 1, tags=(("integration_name", "datadog"),)), - mock.call("tracers", "spans_finished", 1, tags=(("integration_name", "datadog"),)), + mock.call(TELEMETRY_NAMESPACE.TRACERS, "spans_created", 1, tags=(("integration_name", "datadog"),)), + mock.call( + TELEMETRY_NAMESPACE.TRACERS, "spans_finished", 1, tags=(("integration_name", "datadog"),) + ), ] )