ddtrace 3.11.0rc1__cp312-cp312-musllinux_1_2_i686.whl → 3.11.0rc2__cp312-cp312-musllinux_1_2_i686.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ddtrace might be problematic. Click here for more details.
- ddtrace/_logger.py +5 -6
- ddtrace/_trace/product.py +1 -1
- ddtrace/_trace/trace_handlers.py +3 -1
- ddtrace/_version.py +2 -2
- ddtrace/appsec/_asm_request_context.py +3 -1
- ddtrace/appsec/_iast/_listener.py +12 -2
- ddtrace/contrib/integration_registry/registry.yaml +10 -0
- ddtrace/contrib/internal/avro/__init__.py +17 -0
- ddtrace/contrib/internal/azure_functions/patch.py +23 -12
- ddtrace/contrib/internal/azure_functions/utils.py +14 -0
- ddtrace/contrib/internal/botocore/__init__.py +153 -0
- ddtrace/contrib/{_freezegun.py → internal/freezegun/__init__.py} +1 -1
- ddtrace/contrib/internal/langchain/patch.py +11 -443
- ddtrace/contrib/internal/langchain/utils.py +0 -26
- ddtrace/contrib/internal/logbook/patch.py +1 -2
- ddtrace/contrib/internal/logging/patch.py +4 -7
- ddtrace/contrib/internal/loguru/patch.py +1 -3
- ddtrace/contrib/internal/protobuf/__init__.py +17 -0
- ddtrace/contrib/internal/pytest/__init__.py +62 -0
- ddtrace/contrib/internal/pytest/_plugin_v2.py +12 -3
- ddtrace/contrib/internal/pytest_bdd/__init__.py +23 -0
- ddtrace/contrib/internal/pytest_benchmark/__init__.py +3 -0
- ddtrace/contrib/internal/structlog/patch.py +2 -4
- ddtrace/contrib/internal/unittest/__init__.py +36 -0
- ddtrace/internal/_encoding.cpython-312-i386-linux-musl.so +0 -0
- ddtrace/internal/_encoding.pyi +1 -1
- ddtrace/internal/ci_visibility/encoder.py +18 -12
- ddtrace/internal/ci_visibility/utils.py +4 -4
- ddtrace/internal/core/__init__.py +5 -2
- ddtrace/internal/test_visibility/coverage_lines.py +4 -4
- ddtrace/internal/writer/writer.py +24 -11
- ddtrace/llmobs/_constants.py +2 -0
- ddtrace/llmobs/_experiment.py +69 -10
- ddtrace/llmobs/_integrations/bedrock.py +4 -0
- ddtrace/llmobs/_integrations/bedrock_agents.py +5 -1
- ddtrace/llmobs/_integrations/langchain.py +29 -20
- ddtrace/llmobs/_llmobs.py +78 -13
- ddtrace/llmobs/_telemetry.py +20 -5
- ddtrace/llmobs/_utils.py +6 -0
- ddtrace/settings/_config.py +1 -2
- ddtrace/settings/profiling.py +0 -9
- {ddtrace-3.11.0rc1.dist-info → ddtrace-3.11.0rc2.dist-info}/METADATA +1 -1
- {ddtrace-3.11.0rc1.dist-info → ddtrace-3.11.0rc2.dist-info}/RECORD +126 -133
- ddtrace/contrib/_avro.py +0 -17
- ddtrace/contrib/_botocore.py +0 -153
- ddtrace/contrib/_protobuf.py +0 -17
- ddtrace/contrib/_pytest.py +0 -62
- ddtrace/contrib/_pytest_bdd.py +0 -23
- ddtrace/contrib/_pytest_benchmark.py +0 -3
- ddtrace/contrib/_unittest.py +0 -36
- /ddtrace/contrib/{_aiobotocore.py → internal/aiobotocore/__init__.py} +0 -0
- /ddtrace/contrib/{_aiohttp_jinja2.py → internal/aiohttp_jinja2/__init__.py} +0 -0
- /ddtrace/contrib/{_aiomysql.py → internal/aiomysql/__init__.py} +0 -0
- /ddtrace/contrib/{_aiopg.py → internal/aiopg/__init__.py} +0 -0
- /ddtrace/contrib/{_aioredis.py → internal/aioredis/__init__.py} +0 -0
- /ddtrace/contrib/{_algoliasearch.py → internal/algoliasearch/__init__.py} +0 -0
- /ddtrace/contrib/{_anthropic.py → internal/anthropic/__init__.py} +0 -0
- /ddtrace/contrib/{_aredis.py → internal/aredis/__init__.py} +0 -0
- /ddtrace/contrib/{_asyncio.py → internal/asyncio/__init__.py} +0 -0
- /ddtrace/contrib/{_asyncpg.py → internal/asyncpg/__init__.py} +0 -0
- /ddtrace/contrib/{_aws_lambda.py → internal/aws_lambda/__init__.py} +0 -0
- /ddtrace/contrib/{_azure_functions.py → internal/azure_functions/__init__.py} +0 -0
- /ddtrace/contrib/{_azure_servicebus.py → internal/azure_servicebus/__init__.py} +0 -0
- /ddtrace/contrib/{_boto.py → internal/boto/__init__.py} +0 -0
- /ddtrace/contrib/{_cassandra.py → internal/cassandra/__init__.py} +0 -0
- /ddtrace/contrib/{_consul.py → internal/consul/__init__.py} +0 -0
- /ddtrace/contrib/{_coverage.py → internal/coverage/__init__.py} +0 -0
- /ddtrace/contrib/{_crewai.py → internal/crewai/__init__.py} +0 -0
- /ddtrace/contrib/{_django.py → internal/django/__init__.py} +0 -0
- /ddtrace/contrib/{_dogpile_cache.py → internal/dogpile_cache/__init__.py} +0 -0
- /ddtrace/contrib/{_dramatiq.py → internal/dramatiq/__init__.py} +0 -0
- /ddtrace/contrib/{_elasticsearch.py → internal/elasticsearch/__init__.py} +0 -0
- /ddtrace/contrib/{_fastapi.py → internal/fastapi/__init__.py} +0 -0
- /ddtrace/contrib/{_flask.py → internal/flask/__init__.py} +0 -0
- /ddtrace/contrib/{_futures.py → internal/futures/__init__.py} +0 -0
- /ddtrace/contrib/{_gevent.py → internal/gevent/__init__.py} +0 -0
- /ddtrace/contrib/{_google_genai.py → internal/google_genai/__init__.py} +0 -0
- /ddtrace/contrib/{_google_generativeai.py → internal/google_generativeai/__init__.py} +0 -0
- /ddtrace/contrib/{_graphql.py → internal/graphql/__init__.py} +0 -0
- /ddtrace/contrib/{_grpc.py → internal/grpc/__init__.py} +0 -0
- /ddtrace/contrib/{_gunicorn.py → internal/gunicorn/__init__.py} +0 -0
- /ddtrace/contrib/{_httplib.py → internal/httplib/__init__.py} +0 -0
- /ddtrace/contrib/{_httpx.py → internal/httpx/__init__.py} +0 -0
- /ddtrace/contrib/{_jinja2.py → internal/jinja2/__init__.py} +0 -0
- /ddtrace/contrib/{_kafka.py → internal/kafka/__init__.py} +0 -0
- /ddtrace/contrib/{_kombu.py → internal/kombu/__init__.py} +0 -0
- /ddtrace/contrib/{_langchain.py → internal/langchain/__init__.py} +0 -0
- /ddtrace/contrib/{_langgraph.py → internal/langgraph/__init__.py} +0 -0
- /ddtrace/contrib/{_litellm.py → internal/litellm/__init__.py} +0 -0
- /ddtrace/contrib/{_logbook.py → internal/logbook/__init__.py} +0 -0
- /ddtrace/contrib/{_logging.py → internal/logging/__init__.py} +0 -0
- /ddtrace/contrib/{_loguru.py → internal/loguru/__init__.py} +0 -0
- /ddtrace/contrib/{_mako.py → internal/mako/__init__.py} +0 -0
- /ddtrace/contrib/{_mariadb.py → internal/mariadb/__init__.py} +0 -0
- /ddtrace/contrib/{_mcp.py → internal/mcp/__init__.py} +0 -0
- /ddtrace/contrib/{_molten.py → internal/molten/__init__.py} +0 -0
- /ddtrace/contrib/{_mongoengine.py → internal/mongoengine/__init__.py} +0 -0
- /ddtrace/contrib/{_mysql.py → internal/mysql/__init__.py} +0 -0
- /ddtrace/contrib/{_mysqldb.py → internal/mysqldb/__init__.py} +0 -0
- /ddtrace/contrib/{_openai.py → internal/openai/__init__.py} +0 -0
- /ddtrace/contrib/{_openai_agents.py → internal/openai_agents/__init__.py} +0 -0
- /ddtrace/contrib/{_psycopg.py → internal/psycopg/__init__.py} +0 -0
- /ddtrace/contrib/{_pydantic_ai.py → internal/pydantic_ai/__init__.py} +0 -0
- /ddtrace/contrib/{_pymemcache.py → internal/pymemcache/__init__.py} +0 -0
- /ddtrace/contrib/{_pymongo.py → internal/pymongo/__init__.py} +0 -0
- /ddtrace/contrib/{_pymysql.py → internal/pymysql/__init__.py} +0 -0
- /ddtrace/contrib/{_pynamodb.py → internal/pynamodb/__init__.py} +0 -0
- /ddtrace/contrib/{_pyodbc.py → internal/pyodbc/__init__.py} +0 -0
- /ddtrace/contrib/{_redis.py → internal/redis/__init__.py} +0 -0
- /ddtrace/contrib/{_rediscluster.py → internal/rediscluster/__init__.py} +0 -0
- /ddtrace/contrib/{_rq.py → internal/rq/__init__.py} +0 -0
- /ddtrace/contrib/{_sanic.py → internal/sanic/__init__.py} +0 -0
- /ddtrace/contrib/{_selenium.py → internal/selenium/__init__.py} +0 -0
- /ddtrace/contrib/{_snowflake.py → internal/snowflake/__init__.py} +0 -0
- /ddtrace/contrib/{_sqlite3.py → internal/sqlite3/__init__.py} +0 -0
- /ddtrace/contrib/{_starlette.py → internal/starlette/__init__.py} +0 -0
- /ddtrace/contrib/{_structlog.py → internal/structlog/__init__.py} +0 -0
- /ddtrace/contrib/{_subprocess.py → internal/subprocess/__init__.py} +0 -0
- /ddtrace/contrib/{_urllib.py → internal/urllib/__init__.py} +0 -0
- /ddtrace/contrib/{_urllib3.py → internal/urllib3/__init__.py} +0 -0
- /ddtrace/contrib/{_vertexai.py → internal/vertexai/__init__.py} +0 -0
- /ddtrace/contrib/{_vertica.py → internal/vertica/__init__.py} +0 -0
- /ddtrace/contrib/{_webbrowser.py → internal/webbrowser/__init__.py} +0 -0
- /ddtrace/contrib/{_yaaredis.py → internal/yaaredis/__init__.py} +0 -0
- {ddtrace-3.11.0rc1.dist-info → ddtrace-3.11.0rc2.dist-info}/WHEEL +0 -0
- {ddtrace-3.11.0rc1.dist-info → ddtrace-3.11.0rc2.dist-info}/entry_points.txt +0 -0
- {ddtrace-3.11.0rc1.dist-info → ddtrace-3.11.0rc2.dist-info}/licenses/LICENSE +0 -0
- {ddtrace-3.11.0rc1.dist-info → ddtrace-3.11.0rc2.dist-info}/licenses/LICENSE.Apache +0 -0
- {ddtrace-3.11.0rc1.dist-info → ddtrace-3.11.0rc2.dist-info}/licenses/LICENSE.BSD3 +0 -0
- {ddtrace-3.11.0rc1.dist-info → ddtrace-3.11.0rc2.dist-info}/licenses/NOTICE +0 -0
- {ddtrace-3.11.0rc1.dist-info → ddtrace-3.11.0rc2.dist-info}/top_level.txt +0 -0
@@ -106,32 +106,6 @@ def shared_stream(
|
|
106
106
|
raise
|
107
107
|
|
108
108
|
|
109
|
-
def tag_general_message_input(span, inputs, integration, langchain_core):
|
110
|
-
if langchain_core and isinstance(inputs, langchain_core.prompt_values.PromptValue):
|
111
|
-
inputs = inputs.to_messages()
|
112
|
-
elif not isinstance(inputs, list):
|
113
|
-
inputs = [inputs]
|
114
|
-
for input_idx, inp in enumerate(inputs):
|
115
|
-
if isinstance(inp, dict):
|
116
|
-
span.set_tag_str(
|
117
|
-
"langchain.request.messages.%d.content" % (input_idx),
|
118
|
-
integration.trunc(str(inp.get("content", ""))),
|
119
|
-
)
|
120
|
-
role = inp.get("role")
|
121
|
-
if role is not None:
|
122
|
-
span.set_tag_str(
|
123
|
-
"langchain.request.messages.%d.message_type" % (input_idx),
|
124
|
-
str(inp.get("role", "")),
|
125
|
-
)
|
126
|
-
elif langchain_core and isinstance(inp, langchain_core.messages.BaseMessage):
|
127
|
-
content = inp.content
|
128
|
-
role = inp.__class__.__name__
|
129
|
-
span.set_tag_str("langchain.request.messages.%d.content" % (input_idx), integration.trunc(str(content)))
|
130
|
-
span.set_tag_str("langchain.request.messages.%d.message_type" % (input_idx), str(role))
|
131
|
-
else:
|
132
|
-
span.set_tag_str("langchain.request.messages.%d.content" % (input_idx), integration.trunc(str(inp)))
|
133
|
-
|
134
|
-
|
135
109
|
def _get_chunk_callback(interface_type, args, kwargs):
|
136
110
|
results = core.dispatch_with_results("langchain.stream.chunk.callback", (interface_type, args, kwargs))
|
137
111
|
callbacks = []
|
@@ -5,7 +5,6 @@ from wrapt import wrap_function_wrapper as _w
|
|
5
5
|
|
6
6
|
import ddtrace
|
7
7
|
from ddtrace import config
|
8
|
-
from ddtrace._logger import LogInjectionState
|
9
8
|
from ddtrace.contrib.internal.trace_utils import unwrap as _u
|
10
9
|
from ddtrace.internal.utils import get_argument_value
|
11
10
|
|
@@ -27,7 +26,7 @@ def _supported_versions() -> Dict[str, str]:
|
|
27
26
|
|
28
27
|
def _w_process_record(func, instance, args, kwargs):
|
29
28
|
# patch logger to include datadog info before logging
|
30
|
-
if config._logs_injection
|
29
|
+
if config._logs_injection:
|
31
30
|
record = get_argument_value(args, kwargs, 0, "record")
|
32
31
|
record.extra.update(ddtrace.tracer.get_log_correlation_context())
|
33
32
|
return func(*args, **kwargs)
|
@@ -5,7 +5,6 @@ from wrapt import wrap_function_wrapper as _w
|
|
5
5
|
|
6
6
|
import ddtrace
|
7
7
|
from ddtrace import config
|
8
|
-
from ddtrace._logger import LogInjectionState
|
9
8
|
from ddtrace._logger import set_log_formatting
|
10
9
|
from ddtrace.contrib.internal.trace_utils import unwrap as _u
|
11
10
|
from ddtrace.internal.constants import LOG_ATTR_ENV
|
@@ -54,15 +53,13 @@ class DDLogRecord:
|
|
54
53
|
def _w_makeRecord(func, instance, args, kwargs):
|
55
54
|
# Get the LogRecord instance for this log
|
56
55
|
record = func(*args, **kwargs)
|
57
|
-
if config._logs_injection
|
58
|
-
|
59
|
-
return record
|
60
|
-
record.__dict__.update(ddtrace.tracer.get_log_correlation_context())
|
56
|
+
if config._logs_injection:
|
57
|
+
record.__dict__.update(ddtrace.tracer.get_log_correlation_context())
|
61
58
|
return record
|
62
59
|
|
63
60
|
|
64
61
|
def _w_StrFormatStyle_format(func, instance, args, kwargs):
|
65
|
-
if config._logs_injection
|
62
|
+
if not config._logs_injection:
|
66
63
|
return func(*args, **kwargs)
|
67
64
|
# The format string "dd.service={dd.service}" expects
|
68
65
|
# the record to have a "dd" property which is an object that
|
@@ -103,7 +100,7 @@ def patch():
|
|
103
100
|
_w(logging.Logger, "makeRecord", _w_makeRecord)
|
104
101
|
_w(logging.StrFormatStyle, "_format", _w_StrFormatStyle_format)
|
105
102
|
|
106
|
-
if config._logs_injection
|
103
|
+
if config._logs_injection:
|
107
104
|
# Only set the formatter is DD_LOGS_INJECTION is set to True. We do not want to modify
|
108
105
|
# unstructured logs if a user has not enabled logs injection.
|
109
106
|
# Also, the Datadog log format must be set after the logging module has been patched,
|
@@ -5,7 +5,6 @@ from wrapt import wrap_function_wrapper as _w
|
|
5
5
|
|
6
6
|
import ddtrace
|
7
7
|
from ddtrace import config
|
8
|
-
from ddtrace._logger import LogInjectionState
|
9
8
|
from ddtrace.contrib.internal.trace_utils import unwrap as _u
|
10
9
|
|
11
10
|
|
@@ -25,12 +24,11 @@ def _supported_versions() -> Dict[str, str]:
|
|
25
24
|
|
26
25
|
|
27
26
|
def _tracer_injection(event_dict):
|
28
|
-
if config._logs_injection
|
27
|
+
if not config._logs_injection:
|
29
28
|
# log injection is opt-out for structured logging
|
30
29
|
return event_dict
|
31
30
|
event_dd_attributes = ddtrace.tracer.get_log_correlation_context()
|
32
31
|
event_dict.update(event_dd_attributes)
|
33
|
-
|
34
32
|
return event_dd_attributes
|
35
33
|
|
36
34
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
"""
|
2
|
+
The Protobuf integration will trace all Protobuf read / write calls made with the ``google.protobuf``
|
3
|
+
library. This integration is enabled by default.
|
4
|
+
|
5
|
+
Enabling
|
6
|
+
~~~~~~~~
|
7
|
+
|
8
|
+
The protobuf integration is enabled by default. Use
|
9
|
+
:func:`patch()<ddtrace.patch>` to enable the integration::
|
10
|
+
|
11
|
+
from ddtrace import patch
|
12
|
+
patch(protobuf=True)
|
13
|
+
|
14
|
+
Configuration
|
15
|
+
~~~~~~~~~~~~~
|
16
|
+
|
17
|
+
"""
|
@@ -0,0 +1,62 @@
|
|
1
|
+
"""
|
2
|
+
The pytest integration traces test executions.
|
3
|
+
|
4
|
+
Enabling
|
5
|
+
~~~~~~~~
|
6
|
+
|
7
|
+
Enable traced execution of tests using ``pytest`` runner by
|
8
|
+
running ``pytest --ddtrace`` or by modifying any configuration
|
9
|
+
file read by pytest (``pytest.ini``, ``setup.cfg``, ...)::
|
10
|
+
|
11
|
+
[pytest]
|
12
|
+
ddtrace = 1
|
13
|
+
|
14
|
+
|
15
|
+
If you need to disable it, the option ``--no-ddtrace`` will take
|
16
|
+
precedence over ``--ddtrace`` and (``pytest.ini``, ``setup.cfg``, ...)
|
17
|
+
|
18
|
+
You can enable all integrations by using the ``--ddtrace-patch-all`` option
|
19
|
+
alongside ``--ddtrace`` or by adding this to your configuration::
|
20
|
+
|
21
|
+
[pytest]
|
22
|
+
ddtrace = 1
|
23
|
+
ddtrace-patch-all = 1
|
24
|
+
|
25
|
+
|
26
|
+
.. note::
|
27
|
+
The ddtrace plugin for pytest has the side effect of importing the ddtrace
|
28
|
+
package and starting a global tracer.
|
29
|
+
|
30
|
+
If this is causing issues for your pytest runs where traced execution of
|
31
|
+
tests is not enabled, you can deactivate the plugin::
|
32
|
+
|
33
|
+
[pytest]
|
34
|
+
addopts = -p no:ddtrace
|
35
|
+
|
36
|
+
See the `pytest documentation
|
37
|
+
<https://docs.pytest.org/en/7.1.x/how-to/plugins.html#deactivating-unregistering-a-plugin-by-name>`_
|
38
|
+
for more details.
|
39
|
+
|
40
|
+
|
41
|
+
Global Configuration
|
42
|
+
~~~~~~~~~~~~~~~~~~~~
|
43
|
+
|
44
|
+
.. py:data:: ddtrace.config.pytest["service"]
|
45
|
+
|
46
|
+
The service name reported by default for pytest traces.
|
47
|
+
|
48
|
+
This option can also be set with the integration specific ``DD_PYTEST_SERVICE`` environment
|
49
|
+
variable, or more generally with the `DD_SERVICE` environment variable.
|
50
|
+
|
51
|
+
Default: Name of the repository being tested, otherwise ``"pytest"`` if the repository name cannot be found.
|
52
|
+
|
53
|
+
|
54
|
+
.. py:data:: ddtrace.config.pytest["operation_name"]
|
55
|
+
|
56
|
+
The operation name reported by default for pytest traces.
|
57
|
+
|
58
|
+
This option can also be set with the ``DD_PYTEST_OPERATION_NAME`` environment
|
59
|
+
variable.
|
60
|
+
|
61
|
+
Default: ``"pytest.test"``
|
62
|
+
"""
|
@@ -237,11 +237,12 @@ def _pytest_load_initial_conftests_pre_yield(early_config, parser, args):
|
|
237
237
|
ModuleCodeCollector has a tangible impact on the time it takes to load modules, so it should only be installed if
|
238
238
|
coverage collection is requested by the backend.
|
239
239
|
"""
|
240
|
+
take_over_logger_stream_handler()
|
241
|
+
|
240
242
|
if not _is_enabled_early(early_config, args):
|
241
243
|
return
|
242
244
|
|
243
245
|
try:
|
244
|
-
take_over_logger_stream_handler()
|
245
246
|
dd_config.test_visibility.itr_skipping_level = ITR_SKIPPING_LEVEL.SUITE
|
246
247
|
enable_test_visibility(config=dd_config.pytest)
|
247
248
|
if InternalTestSession.should_collect_coverage():
|
@@ -824,8 +825,16 @@ def _pytest_sessionfinish(session: pytest.Session, exitstatus: int) -> None:
|
|
824
825
|
run_coverage_report()
|
825
826
|
|
826
827
|
lines_pct_value = _coverage_data.get(PCT_COVERED_KEY, None)
|
827
|
-
if
|
828
|
-
log.
|
828
|
+
if lines_pct_value is None:
|
829
|
+
log.debug("Unable to retrieve coverage data for the session span")
|
830
|
+
elif not isinstance(lines_pct_value, (float, int)):
|
831
|
+
t = type(lines_pct_value)
|
832
|
+
log.warning(
|
833
|
+
"Unexpected format for total covered percentage: type=%s.%s, value=%r",
|
834
|
+
t.__module__,
|
835
|
+
t.__name__,
|
836
|
+
lines_pct_value,
|
837
|
+
)
|
829
838
|
else:
|
830
839
|
InternalTestSession.set_covered_lines_pct(lines_pct_value)
|
831
840
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
"""
|
2
|
+
The pytest-bdd integration traces executions of scenarios and steps.
|
3
|
+
|
4
|
+
Enabling
|
5
|
+
~~~~~~~~
|
6
|
+
|
7
|
+
Please follow the instructions for enabling `pytest` integration.
|
8
|
+
|
9
|
+
.. note::
|
10
|
+
The ddtrace.pytest_bdd plugin for pytest-bdd has the side effect of importing
|
11
|
+
the ddtrace package and starting a global tracer.
|
12
|
+
|
13
|
+
If this is causing issues for your pytest-bdd runs where traced execution of
|
14
|
+
tests is not enabled, you can deactivate the plugin::
|
15
|
+
|
16
|
+
[pytest]
|
17
|
+
addopts = -p no:ddtrace.pytest_bdd
|
18
|
+
|
19
|
+
See the `pytest documentation
|
20
|
+
<https://docs.pytest.org/en/7.1.x/how-to/plugins.html#deactivating-unregistering-a-plugin-by-name>`_
|
21
|
+
for more details.
|
22
|
+
|
23
|
+
"""
|
@@ -4,7 +4,6 @@ import structlog
|
|
4
4
|
|
5
5
|
import ddtrace
|
6
6
|
from ddtrace import config
|
7
|
-
from ddtrace._logger import LogInjectionState
|
8
7
|
from ddtrace.contrib.internal.trace_utils import unwrap as _u
|
9
8
|
from ddtrace.contrib.internal.trace_utils import wrap as _w
|
10
9
|
from ddtrace.internal.utils import get_argument_value
|
@@ -27,9 +26,8 @@ def _supported_versions() -> Dict[str, str]:
|
|
27
26
|
|
28
27
|
|
29
28
|
def _tracer_injection(_, __, event_dict):
|
30
|
-
if config._logs_injection
|
31
|
-
|
32
|
-
event_dict.update(ddtrace.tracer.get_log_correlation_context())
|
29
|
+
if config._logs_injection:
|
30
|
+
event_dict.update(ddtrace.tracer.get_log_correlation_context())
|
33
31
|
return event_dict
|
34
32
|
|
35
33
|
|
@@ -0,0 +1,36 @@
|
|
1
|
+
"""
|
2
|
+
The unittest integration traces test executions.
|
3
|
+
|
4
|
+
|
5
|
+
Enabling
|
6
|
+
~~~~~~~~
|
7
|
+
|
8
|
+
The unittest integration is enabled automatically when using
|
9
|
+
:ref:`ddtrace-run<ddtracerun>` or :ref:`import ddtrace.auto<ddtraceauto>`.
|
10
|
+
|
11
|
+
Alternately, use :func:`patch()<ddtrace.patch>` to manually enable the integration::
|
12
|
+
|
13
|
+
from ddtrace import patch
|
14
|
+
patch(unittest=True)
|
15
|
+
|
16
|
+
Global Configuration
|
17
|
+
~~~~~~~~~~~~~~~~~~~~
|
18
|
+
|
19
|
+
.. py:data:: ddtrace.config.unittest["operation_name"]
|
20
|
+
|
21
|
+
The operation name reported by default for unittest traces.
|
22
|
+
|
23
|
+
This option can also be set with the ``DD_UNITTEST_OPERATION_NAME`` environment
|
24
|
+
variable.
|
25
|
+
|
26
|
+
Default: ``"unittest.test"``
|
27
|
+
|
28
|
+
.. py:data:: ddtrace.config.unittest["strict_naming"]
|
29
|
+
|
30
|
+
Requires all ``unittest`` tests to start with ``test`` as stated in the Python documentation
|
31
|
+
|
32
|
+
This option can also be set with the ``DD_CIVISIBILITY_UNITTEST_STRICT_NAMING`` environment
|
33
|
+
variable.
|
34
|
+
|
35
|
+
Default: ``True``
|
36
|
+
"""
|
Binary file
|
ddtrace/internal/_encoding.pyi
CHANGED
@@ -23,7 +23,7 @@ class BufferedEncoder(object):
|
|
23
23
|
def __init__(self, max_size: int, max_item_size: int) -> None: ...
|
24
24
|
def __len__(self) -> int: ...
|
25
25
|
def put(self, item: Any) -> None: ...
|
26
|
-
def encode(self) -> Tuple[Optional[bytes], int]: ...
|
26
|
+
def encode(self) -> List[Tuple[Optional[bytes], int]]: ...
|
27
27
|
@property
|
28
28
|
def size(self) -> int: ...
|
29
29
|
|
@@ -32,6 +32,7 @@ if TYPE_CHECKING: # pragma: no cover
|
|
32
32
|
from typing import Dict # noqa:F401
|
33
33
|
from typing import List # noqa:F401
|
34
34
|
from typing import Optional # noqa:F401
|
35
|
+
from typing import Tuple # noqa:F401
|
35
36
|
|
36
37
|
from ddtrace._trace.span import Span # noqa:F401
|
37
38
|
|
@@ -73,11 +74,10 @@ class CIVisibilityEncoderV01(BufferedEncoder):
|
|
73
74
|
def encode(self):
|
74
75
|
with self._lock:
|
75
76
|
with StopWatch() as sw:
|
76
|
-
|
77
|
+
result_payloads = self._build_payload(self.buffer)
|
77
78
|
record_endpoint_payload_events_serialization_time(endpoint=self.ENDPOINT_TYPE, seconds=sw.elapsed())
|
78
|
-
buffer_size = len(self.buffer)
|
79
79
|
self._init_buffer()
|
80
|
-
return
|
80
|
+
return result_payloads
|
81
81
|
|
82
82
|
def _get_parent_session(self, traces):
|
83
83
|
for trace in traces:
|
@@ -87,6 +87,7 @@ class CIVisibilityEncoderV01(BufferedEncoder):
|
|
87
87
|
return 0
|
88
88
|
|
89
89
|
def _build_payload(self, traces):
|
90
|
+
# type: (List[List[Span]]) -> List[Tuple[Optional[bytes], int]]
|
90
91
|
new_parent_session_span_id = self._get_parent_session(traces)
|
91
92
|
is_not_xdist_worker = os.getenv("PYTEST_XDIST_WORKER") is None
|
92
93
|
normalized_spans = [
|
@@ -96,20 +97,25 @@ class CIVisibilityEncoderV01(BufferedEncoder):
|
|
96
97
|
if (is_not_xdist_worker or span.get_tag(EVENT_TYPE) != SESSION_TYPE)
|
97
98
|
]
|
98
99
|
if not normalized_spans:
|
99
|
-
return
|
100
|
+
return []
|
100
101
|
record_endpoint_payload_events_count(endpoint=ENDPOINT.TEST_CYCLE, count=len(normalized_spans))
|
101
102
|
|
102
103
|
# TODO: Split the events in several payloads as needed to avoid hitting the intake's maximum payload size.
|
103
|
-
return
|
104
|
-
|
105
|
-
|
104
|
+
return [
|
105
|
+
(
|
106
|
+
CIVisibilityEncoderV01._pack_payload(
|
107
|
+
{"version": self.PAYLOAD_FORMAT_VERSION, "metadata": self._metadata, "events": normalized_spans}
|
108
|
+
),
|
109
|
+
len(traces),
|
110
|
+
)
|
111
|
+
]
|
106
112
|
|
107
113
|
@staticmethod
|
108
114
|
def _pack_payload(payload):
|
109
115
|
return msgpack_packb(payload)
|
110
116
|
|
111
117
|
def _convert_span(self, span, dd_origin, new_parent_session_span_id=0):
|
112
|
-
# type: (Span, str, Optional[int]) -> Dict[str, Any]
|
118
|
+
# type: (Span, Optional[str], Optional[int]) -> Dict[str, Any]
|
113
119
|
sp = JSONEncoderV2._span_to_dict(span)
|
114
120
|
sp = JSONEncoderV2._normalize_span(sp)
|
115
121
|
sp["type"] = span.get_tag(EVENT_TYPE) or span.span_type
|
@@ -230,14 +236,14 @@ class CIVisibilityCoverageEncoderV02(CIVisibilityEncoderV01):
|
|
230
236
|
return msgpack_packb({"version": self.PAYLOAD_FORMAT_VERSION, "coverages": normalized_covs})
|
231
237
|
|
232
238
|
def _build_payload(self, traces):
|
233
|
-
# type: (List[List[Span]]) -> Optional[bytes]
|
239
|
+
# type: (List[List[Span]]) -> List[Tuple[Optional[bytes], int]]
|
234
240
|
data = self._build_data(traces)
|
235
241
|
if not data:
|
236
|
-
return
|
237
|
-
return b"\r\n".join(self._build_body(data))
|
242
|
+
return []
|
243
|
+
return [(b"\r\n".join(self._build_body(data)), len(traces))]
|
238
244
|
|
239
245
|
def _convert_span(self, span, dd_origin, new_parent_session_span_id=0):
|
240
|
-
# type: (Span, str, Optional[int]) -> Dict[str, Any]
|
246
|
+
# type: (Span, Optional[str], Optional[int]) -> Dict[str, Any]
|
241
247
|
# DEV: new_parent_session_span_id is unused here, but it is used in super class
|
242
248
|
files: Dict[str, Any] = {}
|
243
249
|
|
@@ -112,11 +112,10 @@ def take_over_logger_stream_handler(remove_ddtrace_stream_handlers=True):
|
|
112
112
|
log.debug("CIVisibility not taking over ddtrace logger because level is set to: %s", level)
|
113
113
|
return
|
114
114
|
|
115
|
-
|
115
|
+
ddtrace_logger = logging.getLogger("ddtrace")
|
116
116
|
|
117
117
|
if remove_ddtrace_stream_handlers:
|
118
118
|
log.debug("CIVisibility removing DDTrace logger handler")
|
119
|
-
ddtrace_logger = logging.getLogger("ddtrace")
|
120
119
|
for handler in list(ddtrace_logger.handlers):
|
121
120
|
ddtrace_logger.removeHandler(handler)
|
122
121
|
else:
|
@@ -136,8 +135,9 @@ def take_over_logger_stream_handler(remove_ddtrace_stream_handlers=True):
|
|
136
135
|
log.warning("Invalid log level: %s", level)
|
137
136
|
return
|
138
137
|
|
139
|
-
|
140
|
-
|
138
|
+
ddtrace_logger.addHandler(ci_visibility_handler)
|
139
|
+
ddtrace_logger.setLevel(min(ddtrace_logger.level, ci_visibility_handler.level))
|
140
|
+
ddtrace_logger.propagate = False
|
141
141
|
|
142
142
|
log.debug("logger setup complete")
|
143
143
|
|
@@ -197,9 +197,12 @@ class ExecutionContext(object):
|
|
197
197
|
self._parent = value
|
198
198
|
|
199
199
|
def __exit__(
|
200
|
-
self,
|
200
|
+
self,
|
201
|
+
exc_type: Optional[type],
|
202
|
+
exc_value: Optional[BaseException],
|
203
|
+
traceback: Optional[types.TracebackType],
|
201
204
|
) -> bool:
|
202
|
-
dispatch("context.ended.%s" % self.identifier, (self,))
|
205
|
+
dispatch("context.ended.%s" % self.identifier, (self, (exc_type, exc_value, traceback)))
|
203
206
|
if self._span is None:
|
204
207
|
try:
|
205
208
|
if self._token is not None:
|
@@ -41,8 +41,8 @@ class CoverageLines:
|
|
41
41
|
def add(self, line_number: int):
|
42
42
|
lines_byte = line_number // 8
|
43
43
|
|
44
|
-
if lines_byte >=
|
45
|
-
self._lines.extend(bytearray(lines_byte -
|
44
|
+
if lines_byte >= self._lines.__len__():
|
45
|
+
self._lines.extend(bytearray(lines_byte - self._lines.__len__() + 1))
|
46
46
|
|
47
47
|
# DEV this fun bit allows us to trick ourselves into little-endianness, which is what the backend wants to see
|
48
48
|
# in bytes
|
@@ -62,8 +62,8 @@ class CoverageLines:
|
|
62
62
|
|
63
63
|
def update(self, other: "CoverageLines"):
|
64
64
|
# Extend our lines if the other coverage has more lines
|
65
|
-
if
|
66
|
-
self._lines.extend(bytearray(
|
65
|
+
if other._lines.__len__() > self._lines.__len__():
|
66
|
+
self._lines.extend(bytearray(other._lines.__len__() - self._lines.__len__()))
|
67
67
|
|
68
68
|
for _byte_idx, _byte in enumerate(other._lines):
|
69
69
|
self._lines[_byte_idx] |= _byte
|
@@ -385,13 +385,28 @@ class HTTPWriter(periodic.PeriodicService, TraceWriter):
|
|
385
385
|
def _flush_queue_with_client(self, client: WriterClientBase, raise_exc: bool = False) -> None:
|
386
386
|
n_traces = len(client.encoder)
|
387
387
|
try:
|
388
|
-
|
389
|
-
|
390
|
-
if encoded is None:
|
388
|
+
if not (encoded_traces := client.encoder.encode()):
|
391
389
|
return
|
392
390
|
|
393
|
-
|
394
|
-
if
|
391
|
+
except Exception:
|
392
|
+
# FIXME(munir): if client.encoder raises an Exception n_traces may not be accurate due to race conditions
|
393
|
+
log.error("failed to encode trace with encoder %r", client.encoder, exc_info=True)
|
394
|
+
self._metrics_dist("encoder.dropped.traces", n_traces)
|
395
|
+
return
|
396
|
+
|
397
|
+
for payload in encoded_traces:
|
398
|
+
encoded_data, n_traces = payload
|
399
|
+
self._flush_single_payload(encoded_data, n_traces, client=client, raise_exc=raise_exc)
|
400
|
+
|
401
|
+
def _flush_single_payload(
|
402
|
+
self, encoded: Optional[bytes], n_traces: int, client: WriterClientBase, raise_exc: bool = False
|
403
|
+
) -> None:
|
404
|
+
if encoded is None:
|
405
|
+
return
|
406
|
+
|
407
|
+
# Should gzip the payload if intake accepts it
|
408
|
+
if self._intake_accepts_gzip:
|
409
|
+
try:
|
395
410
|
original_size = len(encoded)
|
396
411
|
# Replace the value to send with the gzipped the value
|
397
412
|
encoded = gzip.compress(encoded, compresslevel=6)
|
@@ -399,12 +414,10 @@ class HTTPWriter(periodic.PeriodicService, TraceWriter):
|
|
399
414
|
|
400
415
|
# And add the header
|
401
416
|
self._headers["Content-Encoding"] = "gzip"
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
self._metrics_dist("encoder.dropped.traces", n_traces)
|
407
|
-
return
|
417
|
+
except Exception:
|
418
|
+
log.error("failed to compress traces with encoder %r", client.encoder, exc_info=True)
|
419
|
+
self._metrics_dist("encoder.dropped.traces", n_traces)
|
420
|
+
return
|
408
421
|
|
409
422
|
try:
|
410
423
|
self._send_payload_with_backoff(encoded, n_traces, client)
|
ddtrace/llmobs/_constants.py
CHANGED
@@ -56,6 +56,7 @@ AGENTLESS_EXP_BASE_URL = "https://{}".format(EXP_SUBDOMAIN_NAME)
|
|
56
56
|
EVP_PAYLOAD_SIZE_LIMIT = 5 << 20 # 5MB (actual limit is 5.1MB)
|
57
57
|
EVP_EVENT_SIZE_LIMIT = (1 << 20) - 1024 # 999KB (actual limit is 1MB)
|
58
58
|
|
59
|
+
EXPERIMENT_CSV_FIELD_MAX_SIZE = 10 * 1024 * 1024
|
59
60
|
|
60
61
|
DROPPED_IO_COLLECTION_ERROR = "dropped_io"
|
61
62
|
DROPPED_VALUE_TEXT = "[This value has been dropped because this span's size exceeds the 1MB size limit.]"
|
@@ -97,3 +98,4 @@ PROXY_REQUEST = "llmobs.proxy_request"
|
|
97
98
|
|
98
99
|
EXPERIMENT_ID_KEY = "_ml_obs.experiment_id"
|
99
100
|
EXPERIMENT_EXPECTED_OUTPUT = "_ml_obs.meta.input.expected_output"
|
101
|
+
DEFAULT_PROJECT_NAME = "default-project"
|