Repository: elastic/apm-agent-python

Issue:
[Python] elastic-apm 6.8.0 : no more SQL informations in the timeline
**Describe the bug**: ...

**To Reproduce**

1. Python project with elastic-apm 6.8.0
2. Python project with databases/queries...

**Environment (please complete the following information)**
- OS: Docker / Python 3.10.2 / Debian bullseye
- Python version: 3.10.2
- Framework and version [e.g. Django 2.1]: FastAPI 0.75.0 / SQLAlchemy 1.4.32 
- APM Server version: 7.16.2
- Agent version: elastic-apm 6.8.0


**Additional context**

Add any other context about the problem here.

- Agent config options <!-- be careful not to post sensitive information -->
  <details>
    <summary>Click to expand</summary>

    ```
    Default Elastic Google Cloud config
    ```
  </details>
- `requirements.txt`:
  <details>
    <summary>Click to expand</summary>

    ```
    fastapi==0.75.0
    uvicorn==0.17.5
    pydantic[dotenv]==1.9.0
    requests==2.27.1
    alembic==1.7.6
    PyMySQL==1.0.2
    SQLAlchemy==1.4.32
    python-slugify==6.1.1
    elastic-apm==6.8.0
    cryptography==36.0.1
    pycountry==22.3.5
    python-jose==3.3.0
    ```
  </details>

With the upgrade of elastic-apm from 6.7.2 to 6.8.0 I've lost SQL informations in the timeline : 

![imagen](https://user-images.githubusercontent.com/25102234/157474218-152b3c35-43d0-48fc-bc7a-8f62c571327b.png)

When I downgrade elastic-apm to 6.7.2 everything is working well : 

![imagen](https://user-images.githubusercontent.com/25102234/157474425-21ab501b-04c9-4481-9d8f-5bf9a4460cf3.png)

Could you please confirm this bug ?


Discussion:
apmmachine: 
## :grey_exclamation: Build Aborted
  
  > There is a new build on-going so the previous on-going builds have been aborted.
  


<!-- BUILD BADGES-->
> _the below badges are clickable and redirect to their specific view in the CI or DOCS_
[![Pipeline View](https://img.shields.io/badge/pipeline-pipeline%20-green)](https://apm-ci.elastic.co/blue/organizations/jenkins/apm-agent-python%2Fapm-agent-python-mbp%2FPR-1483/detail/PR-1483/1//pipeline) [![Test View](https://img.shields.io/badge/test-test-green)](https://apm-ci.elastic.co/blue/organizations/jenkins/apm-agent-python%2Fapm-agent-python-mbp%2FPR-1483/detail/PR-1483/1//tests) [![Changes](https://img.shields.io/badge/changes-changes-green)](https://apm-ci.elastic.co/blue/organizations/jenkins/apm-agent-python%2Fapm-agent-python-mbp%2FPR-1483/detail/PR-1483/1//changes) [![Artifacts](https://img.shields.io/badge/artifacts-artifacts-yellow)](https://apm-ci.elastic.co/blue/organizations/jenkins/apm-agent-python%2Fapm-agent-python-mbp%2FPR-1483/detail/PR-1483/1//artifacts)   [![preview](https://img.shields.io/badge/elastic-observability-blue)](https://ci-stats.elastic.co/app/apm/services/apm-ci/transactions/view?rangeFrom=2022-03-09T18:44:40.273Z&rangeTo=2022-03-09T19:04:40.273Z&transactionName=apm-agent-python/apm-agent-python-mbp/PR-{number}&transactionType=job&latencyAggregationType=avg&traceId=65d37d9f1817e39e11f1ef6f8c0cd564&transactionId=0d49ce8974dc4b4d)

<!-- BUILD SUMMARY-->
<details><summary>Expand to view the summary</summary>
<p>

#### Build stats




* Start Time: 2022-03-09T18:54:40.273+0000


* Duration: 6 min 8 sec





</p>
</details>

<!-- TEST RESULTS IF ANY-->


<!-- STEPS ERRORS IF ANY -->












### Steps errors [![81](https://img.shields.io/badge/81%20-red)](https://apm-ci.elastic.co/blue/organizations/jenkins/apm-agent-python%2Fapm-agent-python-mbp%2FPR-1483/detail/PR-1483/1//pipeline)
  <details><summary>Expand to view the steps failures</summary>
  <p>


> Show only the first 10 steps failures

  
  
  
  
##### `Restore files previously stashed`
<ul>
<li>Took 0 min 0 sec . View more details <a href="https://apm-ci.elastic.co//blue/rest/organizations/jenkins/pipelines/apm-agent-python/pipelines/apm-agent-python-mbp/pipelines/PR-1483/runs/1/steps/566/log/?start=0">here</a></li>
<li>Description: <code>coverage-python-3.10-tornado-newest</code></l1>
</ul>
  
  
  
  
##### `Restore files previously stashed`
<ul>
<li>Took 0 min 0 sec . View more details <a href="https://apm-ci.elastic.co//blue/rest/organizations/jenkins/pipelines/apm-agent-python/pipelines/apm-agent-python-mbp/pipelines/PR-1483/runs/1/steps/571/log/?start=0">here</a></li>
<li>Description: <code>coverage-python-3.10-pymemcache-newest</code></l1>
</ul>
  
  
  
  
##### `Restore files previously stashed`
<ul>
<li>Took 0 min 0 sec . View more details <a href="https://apm-ci.elastic.co//blue/rest/organizations/jenkins/pipelines/apm-agent-python/pipelines/apm-agent-python-mbp/pipelines/PR-1483/runs/1/steps/576/log/?start=0">here</a></li>
<li>Description: <code>coverage-python-3.10-httpx-newest</code></l1>
</ul>
  
  
  
  
##### `Restore files previously stashed`
<ul>
<li>Took 0 min 0 sec . View more details <a href="https://apm-ci.elastic.co//blue/rest/organizations/jenkins/pipelines/apm-agent-python/pipelines/apm-agent-python-mbp/pipelines/PR-1483/runs/1/steps/581/log/?start=0">here</a></li>
<li>Description: <code>coverage-python-3.10-httplib2-newest</code></l1>
</ul>
  
  
  
  
##### `Restore files previously stashed`
<ul>
<li>Took 0 min 0 sec . View more details <a href="https://apm-ci.elastic.co//blue/rest/organizations/jenkins/pipelines/apm-agent-python/pipelines/apm-agent-python-mbp/pipelines/PR-1483/runs/1/steps/586/log/?start=0">here</a></li>
<li>Description: <code>coverage-python-3.10-prometheus_client-newest</code></l1>
</ul>
  
  
  
  
##### `Restore files previously stashed`
<ul>
<li>Took 0 min 0 sec . View more details <a href="https://apm-ci.elastic.co//blue/rest/organizations/jenkins/pipelines/apm-agent-python/pipelines/apm-agent-python-mbp/pipelines/PR-1483/runs/1/steps/591/log/?start=0">here</a></li>
<li>Description: <code>coverage-python-3.10-sanic-newest</code></l1>
</ul>
  
  
  
  
##### `Restore files previously stashed`
<ul>
<li>Took 0 min 0 sec . View more details <a href="https://apm-ci.elastic.co//blue/rest/organizations/jenkins/pipelines/apm-agent-python/pipelines/apm-agent-python-mbp/pipelines/PR-1483/runs/1/steps/597/log/?start=0">here</a></li>
<li>Description: <code>coverage-3.7-none</code></l1>
</ul>
  
  
  
  
##### `Restore files previously stashed`
<ul>
<li>Took 0 min 0 sec . View more details <a href="https://apm-ci.elastic.co//blue/rest/organizations/jenkins/pipelines/apm-agent-python/pipelines/apm-agent-python-mbp/pipelines/PR-1483/runs/1/steps/602/log/?start=0">here</a></li>
<li>Description: <code>coverage-3.8-none</code></l1>
</ul>
  
  
  
  
##### `Restore files previously stashed`
<ul>
<li>Took 0 min 0 sec . View more details <a href="https://apm-ci.elastic.co//blue/rest/organizations/jenkins/pipelines/apm-agent-python/pipelines/apm-agent-python-mbp/pipelines/PR-1483/runs/1/steps/607/log/?start=0">here</a></li>
<li>Description: <code>coverage-3.9-none</code></l1>
</ul>
  
  
  
  
##### `Shell Script`
<ul>
<li>Took 0 min 0 sec . View more details <a href="https://apm-ci.elastic.co//blue/rest/organizations/jenkins/pipelines/apm-agent-python/pipelines/apm-agent-python-mbp/pipelines/PR-1483/runs/1/steps/610/log/?start=0">here</a></li>
<li>Description: <code>python3 -m coverage combine && python3 -m coverage xml</code></l1>
</ul>
  
  </p>
  </details>



## :robot: GitHub comments

To re-run your PR in the CI, just comment with:



- `/test` : Re-trigger the build.


- `/test linters` : Run the Python linters only.


- `/test full` : Run the full matrix of tests.


- `/test benchmark` : Run the APM Agent Python benchmarks tests.


- `run` `elasticsearch-ci/docs` : Re-trigger the docs validation. (use unformatted text in the comment!)




<!--COMMENT_GENERATED_WITH_ID_comment.id-->

Patch:
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 96fdab78..59f69e72 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -38,8 +38,10 @@ endif::[]
 
 * Add OpenTelemetry API bridge {pull}1411[#1411]
 
-//[float]
-//===== Bug fixes
+[float]
+===== Bug fixes
+
+* Fix `exit_span_min_duration` and disable by default {pull}1483[#1483]
 
 
 [[release-notes-6.x]]
diff --git a/docs/configuration.asciidoc b/docs/configuration.asciidoc
index e7c25580..7aa9c87f 100644
--- a/docs/configuration.asciidoc
+++ b/docs/configuration.asciidoc
@@ -761,12 +761,14 @@ Two spans are considered to be of the same kind if the following attributes are
 [options="header"]
 |============
 | Environment                            | Django/Flask               | Default
-| `ELASTIC_APM_EXIT_SPAN_MIN_DURATION` | `EXIT_SPAN_MIN_DURATION` | `"1ms"`
+| `ELASTIC_APM_EXIT_SPAN_MIN_DURATION` | `EXIT_SPAN_MIN_DURATION` | `"0ms"`
 |============
 
 Exit spans are spans that represent a call to an external service, like a database.
 If such calls are very short, they are usually not relevant and can be ignored.
 
+This feature is disabled by default.
+
 NOTE: if a span propagates distributed tracing IDs, it will not be ignored, even if it is shorter than the configured threshold.
 This is to ensure that no broken traces are recorded.
 
diff --git a/elasticapm/conf/__init__.py b/elasticapm/conf/__init__.py
index 37eb9f0c..e9b71ab8 100644
--- a/elasticapm/conf/__init__.py
+++ b/elasticapm/conf/__init__.py
@@ -598,7 +598,7 @@ class Config(_ConfigBase):
     )
     exit_span_min_duration = _ConfigValue(
         "EXIT_SPAN_MIN_DURATION",
-        default=1,
+        default=0,
         validators=[duration_validator],
         type=float,
     )
diff --git a/elasticapm/traces.py b/elasticapm/traces.py
index d23bb97a..136f780e 100644
--- a/elasticapm/traces.py
+++ b/elasticapm/traces.py
@@ -657,7 +657,7 @@ class Span(BaseSpan):
         p.child_ended(self)
 
     def report(self) -> None:
-        if self.discardable and self.duration < self.transaction.config_exit_span_min_duration:
+        if self.discardable and (self.duration * 1000) < self.transaction.config_exit_span_min_duration:
             self.transaction.track_dropped_span(self)
             self.transaction.dropped_spans += 1
         else:


Test patch:
diff --git a/tests/client/dropped_spans_tests.py b/tests/client/dropped_spans_tests.py
index bc20665a..5b14ecec 100644
--- a/tests/client/dropped_spans_tests.py
+++ b/tests/client/dropped_spans_tests.py
@@ -124,11 +124,11 @@ def test_transaction_max_span_dropped_statistics_not_collected_for_incompatible_
 @pytest.mark.parametrize("elasticapm_client", [{"exit_span_min_duration": "1ms"}], indirect=True)
 def test_transaction_fast_exit_span(elasticapm_client):
     elasticapm_client.begin_transaction("test_type")
-    with elasticapm.capture_span(span_type="x", name="x", leaf=True, duration=2):  # not dropped, too long
+    with elasticapm.capture_span(span_type="x", name="x", leaf=True, duration=0.002):  # not dropped, too long
         pass
-    with elasticapm.capture_span(span_type="y", name="y", leaf=True, duration=0.1):  # dropped
+    with elasticapm.capture_span(span_type="y", name="y", leaf=True, duration=0.0001):  # dropped
         pass
-    with elasticapm.capture_span(span_type="z", name="z", leaf=False, duration=0.1):  # not dropped, not exit
+    with elasticapm.capture_span(span_type="z", name="z", leaf=False, duration=0.0001):  # not dropped, not exit
         pass
     elasticapm_client.end_transaction("foo", duration=2.2)
     transaction = elasticapm_client.events[constants.TRANSACTION][0]
@@ -139,6 +139,6 @@ def test_transaction_fast_exit_span(elasticapm_client):
     assert transaction["span_count"]["started"] == 3
     assert transaction["span_count"]["dropped"] == 1
     assert metrics[0]["span"]["type"] == "x"
-    assert metrics[0]["samples"]["span.self_time.sum.us"]["value"] == 2000000
+    assert metrics[0]["samples"]["span.self_time.sum.us"]["value"] == 2000
     assert metrics[1]["span"]["type"] == "y"
-    assert metrics[1]["samples"]["span.self_time.sum.us"]["value"] == 100000
+    assert metrics[1]["samples"]["span.self_time.sum.us"]["value"] == 100
diff --git a/tests/config/config_snapshotting_tests.py b/tests/config/config_snapshotting_tests.py
index d45979f0..8e1ce642 100644
--- a/tests/config/config_snapshotting_tests.py
+++ b/tests/config/config_snapshotting_tests.py
@@ -115,7 +115,7 @@ def test_config_snapshotting_span_compression_drop_exit_span(elasticapm_client):
         span_subtype="b",
         span_action="c",
         extra={"destination": {"service": {"resource": "x"}}},
-        duration=5,
+        duration=0.005,
     ):
         pass
     elasticapm_client.end_transaction()