Repository: DataDog/dd-trace-py

Issue:
KeyError: 'split_by_domain' 
```
  File "/Users/viperfx/.pyenv/versions/scrumgenius-env-3.6/lib/python3.6/site-packages/requests/sessions.py", line 581, in post
    return self.request('POST', url, data=data, json=json, **kwargs)
  File "/Users/viperfx/.pyenv/versions/scrumgenius-env-3.6/lib/python3.6/site-packages/requests/sessions.py", line 533, in request
    resp = self.send(prep, **send_kwargs)
  File "/Users/viperfx/.pyenv/versions/scrumgenius-env-3.6/lib/python3.6/site-packages/ddtrace/contrib/requests/connection.py", line 73, in _wrap_send
    span.service = _extract_service_name(instance, span, hostname=hostname)
  File "/Users/viperfx/.pyenv/versions/scrumgenius-env-3.6/lib/python3.6/site-packages/ddtrace/contrib/requests/connection.py", line 30, in _extract_service_name
    if cfg['split_by_domain'] and hostname:
KeyError: 'split_by_domain'
```

Happens on python 3.6 and 3.7


Discussion:
@viperfx why was this closed? were you able to resolve this issue? what was the issue?
@viperfx @brettlangdon I'm having this same issue too -- any ideas?
@benhoyt Can you provide more context, such as the `requests` version?
@jd Thanks, and sure. Here's a standalone script that reproduces this issue, including version numbers:

```python

# Python 3.7.4 on macOS
# ddtrace==0.29.0
# requests==2.22.0

import ddtrace
import ddtrace.contrib.requests
import requests

session = ddtrace.contrib.requests.TracedSession()

# NOTE: These lines don't actually prevent the KeyError
config = ddtrace.config.get_from(session)
config["service_name"] = "requests"
config["split_by_domain"] = False

# Raises KeyError: 'split_by_domain'
session.get("https://example.com/")
```

When you run it, here's what you get:

```
$ python3 ddbug.py 
Traceback (most recent call last):
  File "ddbug.py", line 18, in <module>
    session.get("https://example.com/")
  File "/Users/ben.hoyt/development/html-sitemap-generator/venv/lib/python3.7/site-packages/requests/sessions.py", line 546, in get
    return self.request('GET', url, **kwargs)
  File "/Users/ben.hoyt/development/html-sitemap-generator/venv/lib/python3.7/site-packages/requests/sessions.py", line 533, in request
    resp = self.send(prep, **send_kwargs)
  File "/Users/ben.hoyt/development/html-sitemap-generator/venv/lib/python3.7/site-packages/ddtrace/contrib/requests/connection.py", line 73, in _wrap_send
    span.service = _extract_service_name(instance, span, hostname=hostname)
  File "/Users/ben.hoyt/development/html-sitemap-generator/venv/lib/python3.7/site-packages/ddtrace/contrib/requests/connection.py", line 30, in _extract_service_name
    if cfg['split_by_domain'] and hostname:
KeyError: 'split_by_domain'
Failed to send traces to Datadog Agent at localhost:8126: [Errno 61] Connection refused
```

So I don't know whether a patch call is required, or if I'm doing something else wrong. The [docs](http://pypi.datadoghq.com/trace/docs/other_integrations.html#requests) imply that you can use `TracedSession` instead of the patch call to get finer-grained control (which is what I want to do).
I think you are getting this error because the the TracedSession doesn't get it's PIN (patch info) unless `patch` is called. `ddtrace.config.get_from(session)` fails to find the config (because the PIN is missing), but returns a fresh dictionary instead of failing. 

Then when the session again uses `ddtrace.config.get_from` to get the config, it again returns a fresh, empty dictionary, causing the error. 

@jd Is it expected that `patch` needs to be called for `TracedSession` to work? Is it expected for `config.get_from` to return a dict that writes without throwing errors but also doesn't set the config?

Resolving patch:
diff --git a/ddtrace/contrib/requests/session.py b/ddtrace/contrib/requests/session.py
--- a/ddtrace/contrib/requests/session.py
+++ b/ddtrace/contrib/requests/session.py
@@ -2,6 +2,8 @@
 
 from ddtrace.vendor.wrapt import wrap_function_wrapper as _w
 
+from ddtrace import config, Pin
+
 from .connection import _wrap_send
 
 
@@ -16,3 +18,4 @@ class TracedSession(requests.Session):
 
 # always patch our `TracedSession` when imported
 _w(TracedSession, "send", _wrap_send)
+Pin(_config=config.requests).onto(TracedSession)


Test patch:
diff --git a/tests/contrib/requests/test_requests.py b/tests/contrib/requests/test_requests.py
--- a/tests/contrib/requests/test_requests.py
+++ b/tests/contrib/requests/test_requests.py
@@ -1,7 +1,11 @@
+import subprocess
+import sys
+
 import pytest
 import requests
 from requests import Session
 from requests.exceptions import MissingSchema
+from ddtrace.vendor import six
 
 from ddtrace import config
 from ddtrace import Pin
@@ -495,3 +499,30 @@ def test_analytics_integration_on_using_pin_default(self):
         self.assertEqual(len(spans), 1)
         s = spans[0]
         self.assertEqual(s.get_metric(ANALYTICS_SAMPLE_RATE_KEY), 1.0)
+
+
+def test_traced_session_no_patch_all(tmpdir):
+    f = tmpdir.join("test.py")
+    f.write(
+        """
+import mock
+import ddtrace
+from ddtrace.contrib.requests import TracedSession
+
+# disable tracer writing to agent
+ddtrace.tracer.writer.flush_queue = mock.Mock(return_value=None)
+
+session = TracedSession()
+session.get("http://httpbin.org/status/200")
+""".lstrip()
+    )
+    p = subprocess.Popen(
+        [sys.executable, "test.py"],
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        cwd=str(tmpdir),
+    )
+    p.wait()
+    assert p.stderr.read() == six.b("")
+    assert p.stdout.read() == six.b("")
+    assert p.returncode == 0