We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Redis: Support publish and subscribe methods
While running the examples from the blog post [How to instrument a polyglot microservices application with Elastic APM](https://www.elastic.co/blog/how-to-instrument-a-polyglot-microservices-application-with-elastic-apm), I noticed Redis doesn't show up on the service map as being connected to the Python service in this example.

It looks like that's because [according to our documentation we don't have the `publish` and `subscribe` methods instrumented](https://www.elastic.co/guide/en/apm/agent/python/5.x/supported-technologies.html#automatic-instrumentation-db-redis).

If these methods were instrumented we would be able to see Redis on service maps for applications that are using it for pub/sub.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `elasticapm/instrumentation/packages/asyncio/aioredis.py`
Content:
```
1 #  BSD 3-Clause License
2 #
3 #  Copyright (c) 2019, Elasticsearch BV
4 #  All rights reserved.
5 #
6 #  Redistribution and use in source and binary forms, with or without
7 #  modification, are permitted provided that the following conditions are met:
8 #
9 #  * Redistributions of source code must retain the above copyright notice, this
10 #    list of conditions and the following disclaimer.
11 #
12 #  * Redistributions in binary form must reproduce the above copyright notice,
13 #    this list of conditions and the following disclaimer in the documentation
14 #    and/or other materials provided with the distribution.
15 #
16 #  * Neither the name of the copyright holder nor the names of its
17 #    contributors may be used to endorse or promote products derived from
18 #    this software without specific prior written permission.
19 #
20 #  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 #  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 #  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 #  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 #  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 #  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 #  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 #  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 #  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 #  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 from __future__ import absolute_import
32 
33 from elasticapm.contrib.asyncio.traces import async_capture_span
34 from elasticapm.instrumentation.packages.base import AbstractInstrumentedModule
35 from elasticapm.traces import execution_context
36 
37 
38 class RedisConnectionPoolInstrumentation(AbstractInstrumentedModule):
39     name = "aioredis"
40 
41     instrument_list = [("aioredis.pool", "ConnectionsPool.execute")]
42 
43     def call(self, module, method, wrapped, instance, args, kwargs):
44         if len(args) > 0:
45             wrapped_name = args[0].decode()
46         else:
47             wrapped_name = self.get_wrapped_name(wrapped, instance, method)
48 
49         with async_capture_span(
50             wrapped_name, span_type="db", span_subtype="redis", span_action="query", leaf=True
51         ) as span:
52             span.context["destination"] = _get_destination_info(instance)
53 
54             return wrapped(*args, **kwargs)
55 
56 
57 class RedisPipelineInstrumentation(AbstractInstrumentedModule):
58     name = "aioredis"
59 
60     instrument_list = [("aioredis.commands.transaction", "Pipeline.execute")]
61 
62     def call(self, module, method, wrapped, instance, args, kwargs):
63         wrapped_name = self.get_wrapped_name(wrapped, instance, method)
64 
65         with async_capture_span(
66             wrapped_name, span_type="db", span_subtype="redis", span_action="query", leaf=True
67         ) as span:
68             span.context["destination"] = _get_destination_info(instance)
69 
70             return wrapped(*args, **kwargs)
71 
72 
73 class RedisConnectionInstrumentation(AbstractInstrumentedModule):
74     name = "aioredis"
75 
76     instrument_list = (("aioredis.connection", "RedisConnection.execute"),)
77 
78     def call(self, module, method, wrapped, instance, args, kwargs):
79         span = execution_context.get_span()
80         if span and span.subtype == "aioredis":
81             span.context["destination"] = _get_destination_info(instance)
82         return wrapped(*args, **kwargs)
83 
84 
85 def _get_destination_info(connection):
86     destination_info = {"service": {"name": "aioredis", "resource": "redis", "type": "db"}}
87 
88     if hasattr(connection, "_pool_or_conn"):
89         destination_info["port"] = connection._pool_or_conn.address[1]
90         destination_info["address"] = connection._pool_or_conn.address[0]
91     else:
92         destination_info["port"] = connection.address[1]
93         destination_info["address"] = connection.address[0]
94 
95     return destination_info
96
```

Path: `elasticapm/instrumentation/packages/redis.py`
Content:
```
1 #  BSD 3-Clause License
2 #
3 #  Copyright (c) 2019, Elasticsearch BV
4 #  All rights reserved.
5 #
6 #  Redistribution and use in source and binary forms, with or without
7 #  modification, are permitted provided that the following conditions are met:
8 #
9 #  * Redistributions of source code must retain the above copyright notice, this
10 #    list of conditions and the following disclaimer.
11 #
12 #  * Redistributions in binary form must reproduce the above copyright notice,
13 #    this list of conditions and the following disclaimer in the documentation
14 #    and/or other materials provided with the distribution.
15 #
16 #  * Neither the name of the copyright holder nor the names of its
17 #    contributors may be used to endorse or promote products derived from
18 #    this software without specific prior written permission.
19 #
20 #  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 #  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 #  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 #  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 #  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 #  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 #  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 #  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 #  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 #  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 from __future__ import absolute_import
32 
33 from elasticapm.instrumentation.packages.base import AbstractInstrumentedModule
34 from elasticapm.traces import capture_span, execution_context
35 
36 
37 class Redis3CheckMixin(object):
38     instrument_list_3 = []
39     instrument_list = []
40 
41     def get_instrument_list(self):
42         try:
43             from redis import VERSION
44 
45             if VERSION[0] >= 3:
46                 return self.instrument_list_3
47             return self.instrument_list
48         except ImportError:
49             return self.instrument_list
50 
51 
52 class RedisInstrumentation(Redis3CheckMixin, AbstractInstrumentedModule):
53     name = "redis"
54 
55     # no need to instrument StrictRedis in redis-py >= 3.0
56     instrument_list_3 = [("redis.client", "Redis.execute_command")]
57     instrument_list = [("redis.client", "Redis.execute_command"), ("redis.client", "StrictRedis.execute_command")]
58 
59     def call(self, module, method, wrapped, instance, args, kwargs):
60         if len(args) > 0:
61             wrapped_name = str(args[0])
62         else:
63             wrapped_name = self.get_wrapped_name(wrapped, instance, method)
64 
65         with capture_span(wrapped_name, span_type="db", span_subtype="redis", span_action="query", leaf=True):
66             return wrapped(*args, **kwargs)
67 
68 
69 class RedisPipelineInstrumentation(Redis3CheckMixin, AbstractInstrumentedModule):
70     name = "redis"
71 
72     # BasePipeline has been renamed to Pipeline in redis-py 3
73     instrument_list_3 = [("redis.client", "Pipeline.execute")]
74     instrument_list = [("redis.client", "BasePipeline.execute")]
75 
76     def call(self, module, method, wrapped, instance, args, kwargs):
77         wrapped_name = self.get_wrapped_name(wrapped, instance, method)
78         with capture_span(wrapped_name, span_type="db", span_subtype="redis", span_action="query", leaf=True):
79             return wrapped(*args, **kwargs)
80 
81 
82 class RedisConnectionInstrumentation(AbstractInstrumentedModule):
83     name = "redis"
84 
85     instrument_list = (("redis.connection", "Connection.send_packed_command"),)
86 
87     def call(self, module, method, wrapped, instance, args, kwargs):
88         span = execution_context.get_span()
89         if span and span.subtype == "redis":
90             span.context["destination"] = get_destination_info(instance)
91         return wrapped(*args, **kwargs)
92 
93 
94 def get_destination_info(connection):
95     destination_info = {"service": {"name": "redis", "resource": "redis", "type": "db"}}
96     if hasattr(connection, "port"):
97         destination_info["port"] = connection.port
98         destination_info["address"] = connection.host
99     elif hasattr(connection, "path"):
100         destination_info["port"] = None
101         destination_info["address"] = "unix://" + connection.path
102     return destination_info
103
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/elasticapm/instrumentation/packages/asyncio/aioredis.py b/elasticapm/instrumentation/packages/asyncio/aioredis.py
--- a/elasticapm/instrumentation/packages/asyncio/aioredis.py
+++ b/elasticapm/instrumentation/packages/asyncio/aioredis.py
@@ -38,7 +38,8 @@
 class RedisConnectionPoolInstrumentation(AbstractInstrumentedModule):
     name = "aioredis"
 
-    instrument_list = [("aioredis.pool", "ConnectionsPool.execute")]
+    instrument_list = [("aioredis.pool", "ConnectionsPool.execute"),
+                       ("aioredis.pool", "ConnectionsPool.execute_pubsub")]
 
     def call(self, module, method, wrapped, instance, args, kwargs):
         if len(args) > 0:
@@ -73,7 +74,8 @@
 class RedisConnectionInstrumentation(AbstractInstrumentedModule):
     name = "aioredis"
 
-    instrument_list = (("aioredis.connection", "RedisConnection.execute"),)
+    instrument_list = (("aioredis.connection", "RedisConnection.execute"),
+                       ("aioredis.pool", "ConnectionsPool.execute_pubsub"))
 
     def call(self, module, method, wrapped, instance, args, kwargs):
         span = execution_context.get_span()
diff --git a/elasticapm/instrumentation/packages/redis.py b/elasticapm/instrumentation/packages/redis.py
--- a/elasticapm/instrumentation/packages/redis.py
+++ b/elasticapm/instrumentation/packages/redis.py
@@ -53,7 +53,7 @@
     name = "redis"
 
     # no need to instrument StrictRedis in redis-py >= 3.0
-    instrument_list_3 = [("redis.client", "Redis.execute_command")]
+    instrument_list_3 = [("redis.client", "Redis.execute_command"), ("redis.client", "PubSub.execute_command")]
     instrument_list = [("redis.client", "Redis.execute_command"), ("redis.client", "StrictRedis.execute_command")]
 
     def call(self, module, method, wrapped, instance, args, kwargs):

{"golden_diff": "diff --git a/elasticapm/instrumentation/packages/asyncio/aioredis.py b/elasticapm/instrumentation/packages/asyncio/aioredis.py\n--- a/elasticapm/instrumentation/packages/asyncio/aioredis.py\n+++ b/elasticapm/instrumentation/packages/asyncio/aioredis.py\n@@ -38,7 +38,8 @@\n class RedisConnectionPoolInstrumentation(AbstractInstrumentedModule):\n     name = \"aioredis\"\n \n-    instrument_list = [(\"aioredis.pool\", \"ConnectionsPool.execute\")]\n+    instrument_list = [(\"aioredis.pool\", \"ConnectionsPool.execute\"),\n+                       (\"aioredis.pool\", \"ConnectionsPool.execute_pubsub\")]\n \n     def call(self, module, method, wrapped, instance, args, kwargs):\n         if len(args) > 0:\n@@ -73,7 +74,8 @@\n class RedisConnectionInstrumentation(AbstractInstrumentedModule):\n     name = \"aioredis\"\n \n-    instrument_list = ((\"aioredis.connection\", \"RedisConnection.execute\"),)\n+    instrument_list = ((\"aioredis.connection\", \"RedisConnection.execute\"),\n+                       (\"aioredis.pool\", \"ConnectionsPool.execute_pubsub\"))\n \n     def call(self, module, method, wrapped, instance, args, kwargs):\n         span = execution_context.get_span()\ndiff --git a/elasticapm/instrumentation/packages/redis.py b/elasticapm/instrumentation/packages/redis.py\n--- a/elasticapm/instrumentation/packages/redis.py\n+++ b/elasticapm/instrumentation/packages/redis.py\n@@ -53,7 +53,7 @@\n     name = \"redis\"\n \n     # no need to instrument StrictRedis in redis-py >= 3.0\n-    instrument_list_3 = [(\"redis.client\", \"Redis.execute_command\")]\n+    instrument_list_3 = [(\"redis.client\", \"Redis.execute_command\"), (\"redis.client\", \"PubSub.execute_command\")]\n     instrument_list = [(\"redis.client\", \"Redis.execute_command\"), (\"redis.client\", \"StrictRedis.execute_command\")]\n \n     def call(self, module, method, wrapped, instance, args, kwargs):\n", "issue": "Redis: Support publish and subscribe methods\nWhile running the examples from the blog post [How to instrument a polyglot microservices application with Elastic APM](https://www.elastic.co/blog/how-to-instrument-a-polyglot-microservices-application-with-elastic-apm), I noticed Redis doesn't show up on the service map as being connected to the Python service in this example.\r\n\r\nIt looks like that's because [according to our documentation we don't have the `publish` and `subscribe` methods instrumented](https://www.elastic.co/guide/en/apm/agent/python/5.x/supported-technologies.html#automatic-instrumentation-db-redis).\r\n\r\nIf these methods were instrumented we would be able to see Redis on service maps for applications that are using it for pub/sub.\n", "before_files": [{"content": "#  BSD 3-Clause License\n#\n#  Copyright (c) 2019, Elasticsearch BV\n#  All rights reserved.\n#\n#  Redistribution and use in source and binary forms, with or without\n#  modification, are permitted provided that the following conditions are met:\n#\n#  * Redistributions of source code must retain the above copyright notice, this\n#    list of conditions and the following disclaimer.\n#\n#  * Redistributions in binary form must reproduce the above copyright notice,\n#    this list of conditions and the following disclaimer in the documentation\n#    and/or other materials provided with the distribution.\n#\n#  * Neither the name of the copyright holder nor the names of its\n#    contributors may be used to endorse or promote products derived from\n#    this software without specific prior written permission.\n#\n#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n#  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n#  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n#  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n#  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nfrom __future__ import absolute_import\n\nfrom elasticapm.contrib.asyncio.traces import async_capture_span\nfrom elasticapm.instrumentation.packages.base import AbstractInstrumentedModule\nfrom elasticapm.traces import execution_context\n\n\nclass RedisConnectionPoolInstrumentation(AbstractInstrumentedModule):\n    name = \"aioredis\"\n\n    instrument_list = [(\"aioredis.pool\", \"ConnectionsPool.execute\")]\n\n    def call(self, module, method, wrapped, instance, args, kwargs):\n        if len(args) > 0:\n            wrapped_name = args[0].decode()\n        else:\n            wrapped_name = self.get_wrapped_name(wrapped, instance, method)\n\n        with async_capture_span(\n            wrapped_name, span_type=\"db\", span_subtype=\"redis\", span_action=\"query\", leaf=True\n        ) as span:\n            span.context[\"destination\"] = _get_destination_info(instance)\n\n            return wrapped(*args, **kwargs)\n\n\nclass RedisPipelineInstrumentation(AbstractInstrumentedModule):\n    name = \"aioredis\"\n\n    instrument_list = [(\"aioredis.commands.transaction\", \"Pipeline.execute\")]\n\n    def call(self, module, method, wrapped, instance, args, kwargs):\n        wrapped_name = self.get_wrapped_name(wrapped, instance, method)\n\n        with async_capture_span(\n            wrapped_name, span_type=\"db\", span_subtype=\"redis\", span_action=\"query\", leaf=True\n        ) as span:\n            span.context[\"destination\"] = _get_destination_info(instance)\n\n            return wrapped(*args, **kwargs)\n\n\nclass RedisConnectionInstrumentation(AbstractInstrumentedModule):\n    name = \"aioredis\"\n\n    instrument_list = ((\"aioredis.connection\", \"RedisConnection.execute\"),)\n\n    def call(self, module, method, wrapped, instance, args, kwargs):\n        span = execution_context.get_span()\n        if span and span.subtype == \"aioredis\":\n            span.context[\"destination\"] = _get_destination_info(instance)\n        return wrapped(*args, **kwargs)\n\n\ndef _get_destination_info(connection):\n    destination_info = {\"service\": {\"name\": \"aioredis\", \"resource\": \"redis\", \"type\": \"db\"}}\n\n    if hasattr(connection, \"_pool_or_conn\"):\n        destination_info[\"port\"] = connection._pool_or_conn.address[1]\n        destination_info[\"address\"] = connection._pool_or_conn.address[0]\n    else:\n        destination_info[\"port\"] = connection.address[1]\n        destination_info[\"address\"] = connection.address[0]\n\n    return destination_info\n", "path": "elasticapm/instrumentation/packages/asyncio/aioredis.py"}, {"content": "#  BSD 3-Clause License\n#\n#  Copyright (c) 2019, Elasticsearch BV\n#  All rights reserved.\n#\n#  Redistribution and use in source and binary forms, with or without\n#  modification, are permitted provided that the following conditions are met:\n#\n#  * Redistributions of source code must retain the above copyright notice, this\n#    list of conditions and the following disclaimer.\n#\n#  * Redistributions in binary form must reproduce the above copyright notice,\n#    this list of conditions and the following disclaimer in the documentation\n#    and/or other materials provided with the distribution.\n#\n#  * Neither the name of the copyright holder nor the names of its\n#    contributors may be used to endorse or promote products derived from\n#    this software without specific prior written permission.\n#\n#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n#  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n#  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n#  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n#  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nfrom __future__ import absolute_import\n\nfrom elasticapm.instrumentation.packages.base import AbstractInstrumentedModule\nfrom elasticapm.traces import capture_span, execution_context\n\n\nclass Redis3CheckMixin(object):\n    instrument_list_3 = []\n    instrument_list = []\n\n    def get_instrument_list(self):\n        try:\n            from redis import VERSION\n\n            if VERSION[0] >= 3:\n                return self.instrument_list_3\n            return self.instrument_list\n        except ImportError:\n            return self.instrument_list\n\n\nclass RedisInstrumentation(Redis3CheckMixin, AbstractInstrumentedModule):\n    name = \"redis\"\n\n    # no need to instrument StrictRedis in redis-py >= 3.0\n    instrument_list_3 = [(\"redis.client\", \"Redis.execute_command\")]\n    instrument_list = [(\"redis.client\", \"Redis.execute_command\"), (\"redis.client\", \"StrictRedis.execute_command\")]\n\n    def call(self, module, method, wrapped, instance, args, kwargs):\n        if len(args) > 0:\n            wrapped_name = str(args[0])\n        else:\n            wrapped_name = self.get_wrapped_name(wrapped, instance, method)\n\n        with capture_span(wrapped_name, span_type=\"db\", span_subtype=\"redis\", span_action=\"query\", leaf=True):\n            return wrapped(*args, **kwargs)\n\n\nclass RedisPipelineInstrumentation(Redis3CheckMixin, AbstractInstrumentedModule):\n    name = \"redis\"\n\n    # BasePipeline has been renamed to Pipeline in redis-py 3\n    instrument_list_3 = [(\"redis.client\", \"Pipeline.execute\")]\n    instrument_list = [(\"redis.client\", \"BasePipeline.execute\")]\n\n    def call(self, module, method, wrapped, instance, args, kwargs):\n        wrapped_name = self.get_wrapped_name(wrapped, instance, method)\n        with capture_span(wrapped_name, span_type=\"db\", span_subtype=\"redis\", span_action=\"query\", leaf=True):\n            return wrapped(*args, **kwargs)\n\n\nclass RedisConnectionInstrumentation(AbstractInstrumentedModule):\n    name = \"redis\"\n\n    instrument_list = ((\"redis.connection\", \"Connection.send_packed_command\"),)\n\n    def call(self, module, method, wrapped, instance, args, kwargs):\n        span = execution_context.get_span()\n        if span and span.subtype == \"redis\":\n            span.context[\"destination\"] = get_destination_info(instance)\n        return wrapped(*args, **kwargs)\n\n\ndef get_destination_info(connection):\n    destination_info = {\"service\": {\"name\": \"redis\", \"resource\": \"redis\", \"type\": \"db\"}}\n    if hasattr(connection, \"port\"):\n        destination_info[\"port\"] = connection.port\n        destination_info[\"address\"] = connection.host\n    elif hasattr(connection, \"path\"):\n        destination_info[\"port\"] = None\n        destination_info[\"address\"] = \"unix://\" + connection.path\n    return destination_info\n", "path": "elasticapm/instrumentation/packages/redis.py"}], "after_files": [{"content": "#  BSD 3-Clause License\n#\n#  Copyright (c) 2019, Elasticsearch BV\n#  All rights reserved.\n#\n#  Redistribution and use in source and binary forms, with or without\n#  modification, are permitted provided that the following conditions are met:\n#\n#  * Redistributions of source code must retain the above copyright notice, this\n#    list of conditions and the following disclaimer.\n#\n#  * Redistributions in binary form must reproduce the above copyright notice,\n#    this list of conditions and the following disclaimer in the documentation\n#    and/or other materials provided with the distribution.\n#\n#  * Neither the name of the copyright holder nor the names of its\n#    contributors may be used to endorse or promote products derived from\n#    this software without specific prior written permission.\n#\n#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n#  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n#  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n#  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n#  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nfrom __future__ import absolute_import\n\nfrom elasticapm.contrib.asyncio.traces import async_capture_span\nfrom elasticapm.instrumentation.packages.base import AbstractInstrumentedModule\nfrom elasticapm.traces import execution_context\n\n\nclass RedisConnectionPoolInstrumentation(AbstractInstrumentedModule):\n    name = \"aioredis\"\n\n    instrument_list = [(\"aioredis.pool\", \"ConnectionsPool.execute\"),\n                       (\"aioredis.pool\", \"ConnectionsPool.execute_pubsub\")]\n\n    def call(self, module, method, wrapped, instance, args, kwargs):\n        if len(args) > 0:\n            wrapped_name = args[0].decode()\n        else:\n            wrapped_name = self.get_wrapped_name(wrapped, instance, method)\n\n        with async_capture_span(\n            wrapped_name, span_type=\"db\", span_subtype=\"redis\", span_action=\"query\", leaf=True\n        ) as span:\n            span.context[\"destination\"] = _get_destination_info(instance)\n\n            return wrapped(*args, **kwargs)\n\n\nclass RedisPipelineInstrumentation(AbstractInstrumentedModule):\n    name = \"aioredis\"\n\n    instrument_list = [(\"aioredis.commands.transaction\", \"Pipeline.execute\")]\n\n    def call(self, module, method, wrapped, instance, args, kwargs):\n        wrapped_name = self.get_wrapped_name(wrapped, instance, method)\n\n        with async_capture_span(\n            wrapped_name, span_type=\"db\", span_subtype=\"redis\", span_action=\"query\", leaf=True\n        ) as span:\n            span.context[\"destination\"] = _get_destination_info(instance)\n\n            return wrapped(*args, **kwargs)\n\n\nclass RedisConnectionInstrumentation(AbstractInstrumentedModule):\n    name = \"aioredis\"\n\n    instrument_list = ((\"aioredis.connection\", \"RedisConnection.execute\"),\n                       (\"aioredis.pool\", \"ConnectionsPool.execute_pubsub\"))\n\n    def call(self, module, method, wrapped, instance, args, kwargs):\n        span = execution_context.get_span()\n        if span and span.subtype == \"aioredis\":\n            span.context[\"destination\"] = _get_destination_info(instance)\n        return wrapped(*args, **kwargs)\n\n\ndef _get_destination_info(connection):\n    destination_info = {\"service\": {\"name\": \"aioredis\", \"resource\": \"redis\", \"type\": \"db\"}}\n\n    if hasattr(connection, \"_pool_or_conn\"):\n        destination_info[\"port\"] = connection._pool_or_conn.address[1]\n        destination_info[\"address\"] = connection._pool_or_conn.address[0]\n    else:\n        destination_info[\"port\"] = connection.address[1]\n        destination_info[\"address\"] = connection.address[0]\n\n    return destination_info\n", "path": "elasticapm/instrumentation/packages/asyncio/aioredis.py"}, {"content": "#  BSD 3-Clause License\n#\n#  Copyright (c) 2019, Elasticsearch BV\n#  All rights reserved.\n#\n#  Redistribution and use in source and binary forms, with or without\n#  modification, are permitted provided that the following conditions are met:\n#\n#  * Redistributions of source code must retain the above copyright notice, this\n#    list of conditions and the following disclaimer.\n#\n#  * Redistributions in binary form must reproduce the above copyright notice,\n#    this list of conditions and the following disclaimer in the documentation\n#    and/or other materials provided with the distribution.\n#\n#  * Neither the name of the copyright holder nor the names of its\n#    contributors may be used to endorse or promote products derived from\n#    this software without specific prior written permission.\n#\n#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n#  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n#  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n#  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n#  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nfrom __future__ import absolute_import\n\nfrom elasticapm.instrumentation.packages.base import AbstractInstrumentedModule\nfrom elasticapm.traces import capture_span, execution_context\n\n\nclass Redis3CheckMixin(object):\n    instrument_list_3 = []\n    instrument_list = []\n\n    def get_instrument_list(self):\n        try:\n            from redis import VERSION\n\n            if VERSION[0] >= 3:\n                return self.instrument_list_3\n            return self.instrument_list\n        except ImportError:\n            return self.instrument_list\n\n\nclass RedisInstrumentation(Redis3CheckMixin, AbstractInstrumentedModule):\n    name = \"redis\"\n\n    # no need to instrument StrictRedis in redis-py >= 3.0\n    instrument_list_3 = [(\"redis.client\", \"Redis.execute_command\"), (\"redis.client\", \"PubSub.execute_command\")]\n    instrument_list = [(\"redis.client\", \"Redis.execute_command\"), (\"redis.client\", \"StrictRedis.execute_command\")]\n\n    def call(self, module, method, wrapped, instance, args, kwargs):\n        if len(args) > 0:\n            wrapped_name = str(args[0])\n        else:\n            wrapped_name = self.get_wrapped_name(wrapped, instance, method)\n\n        with capture_span(wrapped_name, span_type=\"db\", span_subtype=\"redis\", span_action=\"query\", leaf=True):\n            return wrapped(*args, **kwargs)\n\n\nclass RedisPipelineInstrumentation(Redis3CheckMixin, AbstractInstrumentedModule):\n    name = \"redis\"\n\n    # BasePipeline has been renamed to Pipeline in redis-py 3\n    instrument_list_3 = [(\"redis.client\", \"Pipeline.execute\")]\n    instrument_list = [(\"redis.client\", \"BasePipeline.execute\")]\n\n    def call(self, module, method, wrapped, instance, args, kwargs):\n        wrapped_name = self.get_wrapped_name(wrapped, instance, method)\n        with capture_span(wrapped_name, span_type=\"db\", span_subtype=\"redis\", span_action=\"query\", leaf=True):\n            return wrapped(*args, **kwargs)\n\n\nclass RedisConnectionInstrumentation(AbstractInstrumentedModule):\n    name = \"redis\"\n\n    instrument_list = ((\"redis.connection\", \"Connection.send_packed_command\"),)\n\n    def call(self, module, method, wrapped, instance, args, kwargs):\n        span = execution_context.get_span()\n        if span and span.subtype == \"redis\":\n            span.context[\"destination\"] = get_destination_info(instance)\n        return wrapped(*args, **kwargs)\n\n\ndef get_destination_info(connection):\n    destination_info = {\"service\": {\"name\": \"redis\", \"resource\": \"redis\", \"type\": \"db\"}}\n    if hasattr(connection, \"port\"):\n        destination_info[\"port\"] = connection.port\n        destination_info[\"address\"] = connection.host\n    elif hasattr(connection, \"path\"):\n        destination_info[\"port\"] = None\n        destination_info[\"address\"] = \"unix://\" + connection.path\n    return destination_info\n", "path": "elasticapm/instrumentation/packages/redis.py"}]}