Repository: elastic/apm-agent-python

Issue:
capture_exception raises exception in UUID __repr__
**Describe the bug**: When capturing exceptions, if the exception is because of a raise in UUID `__init__`, apm agent itself raises with exception:

```
self = <[AttributeError('int') raised in repr()] UUID object at 0x12508cd00>

    def __str__(self):
>       hex = '%032x' % self.int
E       AttributeError: int
```

**To Reproduce**

1. Add this test to `exception_tests.py`:
```python
def test_fail_on_uuid_raise(elasticapm_client):
    def generate_uuid():
        from uuid import UUID
        return UUID("INVALID")

    try:
        generate_uuid()
    except Exception:
        elasticapm_client.capture_exception()
```

**Environment (please complete the following information)**
- OS: MacOS / Linux
- Python version: 3.8.11
- Framework and version [e.g. Django 2.1]: Custom / Starlette
- APM Server version: N/A
- Agent version: 6.3.3


**Additional context**

Add any other context about the problem here.

`stacktrace`:
```
../../elasticapm/base.py:272: in capture_exception
    return self.capture("Exception", exc_info=exc_info, handled=handled, **kwargs)
../../elasticapm/base.py:241: in capture
    data = self._build_msg_for_logging(
../../elasticapm/base.py:469: in _build_msg_for_logging
    result = handler.capture(self, **kwargs)
../../elasticapm/events.py:96: in capture
    frames = get_stack_info(
../../elasticapm/utils/stacks.py:325: in get_stack_info
    result = get_frame_info(
../../elasticapm/utils/stacks.py:294: in get_frame_info
    f_locals = {varname: locals_processor_func(var) for varname, var in compat.iteritems(f_locals)}
../../elasticapm/utils/stacks.py:294: in <dictcomp>
    f_locals = {varname: locals_processor_func(var) for varname, var in compat.iteritems(f_locals)}
../../elasticapm/events.py:103: in <lambda>
    locals_processor_func=lambda local_var: varmap(
../../elasticapm/utils/__init__.py:71: in varmap
    ret = func(name, var, **kwargs)
../../elasticapm/events.py:104: in <lambda>
    lambda k, val: shorten(
../../elasticapm/utils/encoding.py:205: in shorten
    var = transform(var)
../../elasticapm/utils/encoding.py:132: in transform
    ret = repr(value)
/usr/local/Cellar/python@3.8/3.8.11/Frameworks/Python.framework/Versions/3.8/lib/python3.8/uuid.py:268: in __repr__
    return '%s(%r)' % (self.__class__.__name__, str(self))
```



Discussion:
Thanks for the report, and for the reproducing test case! I'll take a look at this today.

Resolving patch:
diff --git a/elasticapm/utils/encoding.py b/elasticapm/utils/encoding.py
--- a/elasticapm/utils/encoding.py
+++ b/elasticapm/utils/encoding.py
@@ -129,7 +129,10 @@ class value_type(list):
 
             ret = value_type(transform_rec(o) for o in value)
     elif isinstance(value, uuid.UUID):
-        ret = repr(value)
+        try:
+            ret = repr(value)
+        except AttributeError:
+            ret = None
     elif isinstance(value, dict):
         ret = dict((to_unicode(k), transform_rec(v)) for k, v in compat.iteritems(value))
     elif isinstance(value, compat.text_type):


Test patch:
diff --git a/tests/client/exception_tests.py b/tests/client/exception_tests.py
--- a/tests/client/exception_tests.py
+++ b/tests/client/exception_tests.py
@@ -385,3 +385,15 @@ def func():
     exception = elasticapm_client.events[ERROR][-1]
     frames = exception["exception"]["stacktrace"]
     assert len(frames) == 0
+
+
+def test_fail_on_uuid_raise(elasticapm_client):
+    def generate_uuid():
+        from uuid import UUID
+
+        return UUID("INVALID")
+
+    try:
+        generate_uuid()
+    except Exception:
+        elasticapm_client.capture_exception()