We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
DDLogger rewrites LogRecord.msg, which causes Sentry events duplication
Sentry uses `LogRecord.msg` to identify log events. LogRecord.msg is the log message template, to be formatted on demand.

When rewriting `msg`, one should not enrich it with arbitrary values, like `logging_bucket.skipped`.

The line
```
                record.msg = '{}, {} additional messages skipped'.format(record.msg, logging_bucket.skipped)
```

should be something like:

```
                record.msg = '{}, %s additional messages skipped'.format(record.msg)
                record.args = record.args + (logging_bucket.skipped,)
```

Culprit:
https://github.com/DataDog/dd-trace-py/blob/914cbca4ba5ec53ff17cb67164cb51b7bcd91ac2/ddtrace/internal/logger.py#L113

Example of message duplication:
![image](https://user-images.githubusercontent.com/43616133/67687095-ae372580-f98f-11e9-9b81-2a0ee61f175c.png)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `ddtrace/internal/logger.py`
Content:
```
1 import collections
2 import logging
3 
4 from ..utils.formats import get_env
5 
6 
7 def get_logger(name):
8     """
9     Retrieve or create a ``DDLogger`` instance.
10 
11     This function mirrors the behavior of `logging.getLogger`.
12 
13     If no logger with the provided name has been fetched before then
14     a new one is created.
15 
16     If a previous logger has been created then it is returned.
17 
18     DEV: We do not want to mess with `logging.setLoggerClass()`
19          That will totally mess with the user's loggers, we want
20          just our own, selective loggers to be DDLoggers
21 
22     :param name: The name of the logger to fetch or create
23     :type name: str
24     :return: The logger instance
25     :rtype: ``DDLogger``
26     """
27     # DEV: `logging.Logger.manager` refers to the single root `logging.Manager` instance
28     #   https://github.com/python/cpython/blob/48769a28ad6ef4183508951fa6a378531ace26a4/Lib/logging/__init__.py#L1824-L1826  # noqa
29     manager = logging.Logger.manager
30 
31     # If the logger does not exist yet, create it
32     # DEV: `Manager.loggerDict` is a dict mapping logger name to logger
33     # DEV: This is a simplified version of `logging.Manager.getLogger`
34     #   https://github.com/python/cpython/blob/48769a28ad6ef4183508951fa6a378531ace26a4/Lib/logging/__init__.py#L1221-L1253  # noqa
35     if name not in manager.loggerDict:
36         manager.loggerDict[name] = DDLogger(name=name)
37 
38     # Get our logger
39     logger = manager.loggerDict[name]
40 
41     # If this log manager has a `_fixupParents` method then call it on our logger
42     # DEV: This helper is used to ensure our logger has an appropriate `Logger.parent` set,
43     #      without this then we cannot take advantage of the root loggers handlers
44     #   https://github.com/python/cpython/blob/7c7839329c2c66d051960ab1df096aed1cc9343e/Lib/logging/__init__.py#L1272-L1294  # noqa
45     # DEV: `_fixupParents` has been around for awhile, but add the `hasattr` guard... just in case.
46     if hasattr(manager, '_fixupParents'):
47         manager._fixupParents(logger)
48 
49     # Return out logger
50     return logger
51 
52 
53 class DDLogger(logging.Logger):
54     """
55     Custom rate limited logger used by ``ddtrace``
56 
57     This logger class is used to rate limit the output of
58     log messages from within the ``ddtrace`` package.
59     """
60     __slots__ = ('buckets', 'rate_limit')
61 
62     # Named tuple used for keeping track of a log lines current time bucket and the number of log lines skipped
63     LoggingBucket = collections.namedtuple('LoggingBucket', ('bucket', 'skipped'))
64 
65     def __init__(self, *args, **kwargs):
66         """Constructor for ``DDLogger``"""
67         super(DDLogger, self).__init__(*args, **kwargs)
68 
69         # Dict to keep track of the current time bucket per name/level/pathname/lineno
70         self.buckets = collections.defaultdict(lambda: DDLogger.LoggingBucket(0, 0))
71 
72         # Allow 1 log record per name/level/pathname/lineno every 60 seconds by default
73         # Allow configuring via `DD_LOGGING_RATE_LIMIT`
74         # DEV: `DD_LOGGING_RATE_LIMIT=0` means to disable all rate limiting
75         self.rate_limit = int(get_env('logging', 'rate_limit', default=60))
76 
77     def handle(self, record):
78         """
79         Function used to call the handlers for a log line.
80 
81         This implementation will first determine if this log line should
82         be logged or rate limited, and then call the base ``logging.Logger.handle``
83         function if it should be logged
84 
85         DEV: This method has all of it's code inlined to reduce on functions calls
86 
87         :param record: The log record being logged
88         :type record: ``logging.LogRecord``
89         """
90         # If rate limiting has been disabled (`DD_LOGGING_RATE_LIMIT=0`) then apply no rate limit
91         if not self.rate_limit:
92             super(DDLogger, self).handle(record)
93             return
94 
95         # Allow 1 log record by name/level/pathname/lineno every X seconds
96         # DEV: current unix time / rate (e.g. 300 seconds) = time bucket
97         #      int(1546615098.8404942 / 300) = 515538
98         # DEV: LogRecord `created` is a unix timestamp/float
99         # DEV: LogRecord has `levelname` and `levelno`, we want `levelno` e.g. `logging.DEBUG = 10`
100         current_bucket = int(record.created / self.rate_limit)
101 
102         # Limit based on logger name, record level, filename, and line number
103         #   ('ddtrace.writer', 'DEBUG', '../site-packages/ddtrace/writer.py', 137)
104         # This way each unique log message can get logged at least once per time period
105         # DEV: LogRecord has `levelname` and `levelno`, we want `levelno` e.g. `logging.DEBUG = 10`
106         key = (record.name, record.levelno, record.pathname, record.lineno)
107 
108         # Only log this message if the time bucket has changed from the previous time we ran
109         logging_bucket = self.buckets[key]
110         if logging_bucket.bucket != current_bucket:
111             # Append count of skipped messages if we have skipped some since our last logging
112             if logging_bucket.skipped:
113                 record.msg = '{}, {} additional messages skipped'.format(record.msg, logging_bucket.skipped)
114 
115             # Reset our bucket
116             self.buckets[key] = DDLogger.LoggingBucket(current_bucket, 0)
117 
118             # Call the base handle to actually log this record
119             super(DDLogger, self).handle(record)
120         else:
121             # Increment the count of records we have skipped
122             # DEV: `self.buckets[key]` is a tuple which is immutable so recreate instead
123             self.buckets[key] = DDLogger.LoggingBucket(logging_bucket.bucket, logging_bucket.skipped + 1)
124
```
--- 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/ddtrace/internal/logger.py b/ddtrace/internal/logger.py
--- a/ddtrace/internal/logger.py
+++ b/ddtrace/internal/logger.py
@@ -110,7 +110,8 @@
         if logging_bucket.bucket != current_bucket:
             # Append count of skipped messages if we have skipped some since our last logging
             if logging_bucket.skipped:
-                record.msg = '{}, {} additional messages skipped'.format(record.msg, logging_bucket.skipped)
+                record.msg = '{}, %s additional messages skipped'.format(record.msg)
+                record.args = record.args + (logging_bucket.skipped, )
 
             # Reset our bucket
             self.buckets[key] = DDLogger.LoggingBucket(current_bucket, 0)

{"golden_diff": "diff --git a/ddtrace/internal/logger.py b/ddtrace/internal/logger.py\n--- a/ddtrace/internal/logger.py\n+++ b/ddtrace/internal/logger.py\n@@ -110,7 +110,8 @@\n         if logging_bucket.bucket != current_bucket:\n             # Append count of skipped messages if we have skipped some since our last logging\n             if logging_bucket.skipped:\n-                record.msg = '{}, {} additional messages skipped'.format(record.msg, logging_bucket.skipped)\n+                record.msg = '{}, %s additional messages skipped'.format(record.msg)\n+                record.args = record.args + (logging_bucket.skipped, )\n \n             # Reset our bucket\n             self.buckets[key] = DDLogger.LoggingBucket(current_bucket, 0)\n", "issue": "DDLogger rewrites LogRecord.msg, which causes Sentry events duplication\nSentry uses `LogRecord.msg` to identify log events. LogRecord.msg is the log message template, to be formatted on demand.\r\n\r\nWhen rewriting `msg`, one should not enrich it with arbitrary values, like `logging_bucket.skipped`.\r\n\r\nThe line\r\n```\r\n                record.msg = '{}, {} additional messages skipped'.format(record.msg, logging_bucket.skipped)\r\n```\r\n\r\nshould be something like:\r\n\r\n```\r\n                record.msg = '{}, %s additional messages skipped'.format(record.msg)\r\n                record.args = record.args + (logging_bucket.skipped,)\r\n```\r\n\r\nCulprit:\r\nhttps://github.com/DataDog/dd-trace-py/blob/914cbca4ba5ec53ff17cb67164cb51b7bcd91ac2/ddtrace/internal/logger.py#L113\r\n\r\nExample of message duplication:\r\n![image](https://user-images.githubusercontent.com/43616133/67687095-ae372580-f98f-11e9-9b81-2a0ee61f175c.png)\r\n\n", "before_files": [{"content": "import collections\nimport logging\n\nfrom ..utils.formats import get_env\n\n\ndef get_logger(name):\n    \"\"\"\n    Retrieve or create a ``DDLogger`` instance.\n\n    This function mirrors the behavior of `logging.getLogger`.\n\n    If no logger with the provided name has been fetched before then\n    a new one is created.\n\n    If a previous logger has been created then it is returned.\n\n    DEV: We do not want to mess with `logging.setLoggerClass()`\n         That will totally mess with the user's loggers, we want\n         just our own, selective loggers to be DDLoggers\n\n    :param name: The name of the logger to fetch or create\n    :type name: str\n    :return: The logger instance\n    :rtype: ``DDLogger``\n    \"\"\"\n    # DEV: `logging.Logger.manager` refers to the single root `logging.Manager` instance\n    #   https://github.com/python/cpython/blob/48769a28ad6ef4183508951fa6a378531ace26a4/Lib/logging/__init__.py#L1824-L1826  # noqa\n    manager = logging.Logger.manager\n\n    # If the logger does not exist yet, create it\n    # DEV: `Manager.loggerDict` is a dict mapping logger name to logger\n    # DEV: This is a simplified version of `logging.Manager.getLogger`\n    #   https://github.com/python/cpython/blob/48769a28ad6ef4183508951fa6a378531ace26a4/Lib/logging/__init__.py#L1221-L1253  # noqa\n    if name not in manager.loggerDict:\n        manager.loggerDict[name] = DDLogger(name=name)\n\n    # Get our logger\n    logger = manager.loggerDict[name]\n\n    # If this log manager has a `_fixupParents` method then call it on our logger\n    # DEV: This helper is used to ensure our logger has an appropriate `Logger.parent` set,\n    #      without this then we cannot take advantage of the root loggers handlers\n    #   https://github.com/python/cpython/blob/7c7839329c2c66d051960ab1df096aed1cc9343e/Lib/logging/__init__.py#L1272-L1294  # noqa\n    # DEV: `_fixupParents` has been around for awhile, but add the `hasattr` guard... just in case.\n    if hasattr(manager, '_fixupParents'):\n        manager._fixupParents(logger)\n\n    # Return out logger\n    return logger\n\n\nclass DDLogger(logging.Logger):\n    \"\"\"\n    Custom rate limited logger used by ``ddtrace``\n\n    This logger class is used to rate limit the output of\n    log messages from within the ``ddtrace`` package.\n    \"\"\"\n    __slots__ = ('buckets', 'rate_limit')\n\n    # Named tuple used for keeping track of a log lines current time bucket and the number of log lines skipped\n    LoggingBucket = collections.namedtuple('LoggingBucket', ('bucket', 'skipped'))\n\n    def __init__(self, *args, **kwargs):\n        \"\"\"Constructor for ``DDLogger``\"\"\"\n        super(DDLogger, self).__init__(*args, **kwargs)\n\n        # Dict to keep track of the current time bucket per name/level/pathname/lineno\n        self.buckets = collections.defaultdict(lambda: DDLogger.LoggingBucket(0, 0))\n\n        # Allow 1 log record per name/level/pathname/lineno every 60 seconds by default\n        # Allow configuring via `DD_LOGGING_RATE_LIMIT`\n        # DEV: `DD_LOGGING_RATE_LIMIT=0` means to disable all rate limiting\n        self.rate_limit = int(get_env('logging', 'rate_limit', default=60))\n\n    def handle(self, record):\n        \"\"\"\n        Function used to call the handlers for a log line.\n\n        This implementation will first determine if this log line should\n        be logged or rate limited, and then call the base ``logging.Logger.handle``\n        function if it should be logged\n\n        DEV: This method has all of it's code inlined to reduce on functions calls\n\n        :param record: The log record being logged\n        :type record: ``logging.LogRecord``\n        \"\"\"\n        # If rate limiting has been disabled (`DD_LOGGING_RATE_LIMIT=0`) then apply no rate limit\n        if not self.rate_limit:\n            super(DDLogger, self).handle(record)\n            return\n\n        # Allow 1 log record by name/level/pathname/lineno every X seconds\n        # DEV: current unix time / rate (e.g. 300 seconds) = time bucket\n        #      int(1546615098.8404942 / 300) = 515538\n        # DEV: LogRecord `created` is a unix timestamp/float\n        # DEV: LogRecord has `levelname` and `levelno`, we want `levelno` e.g. `logging.DEBUG = 10`\n        current_bucket = int(record.created / self.rate_limit)\n\n        # Limit based on logger name, record level, filename, and line number\n        #   ('ddtrace.writer', 'DEBUG', '../site-packages/ddtrace/writer.py', 137)\n        # This way each unique log message can get logged at least once per time period\n        # DEV: LogRecord has `levelname` and `levelno`, we want `levelno` e.g. `logging.DEBUG = 10`\n        key = (record.name, record.levelno, record.pathname, record.lineno)\n\n        # Only log this message if the time bucket has changed from the previous time we ran\n        logging_bucket = self.buckets[key]\n        if logging_bucket.bucket != current_bucket:\n            # Append count of skipped messages if we have skipped some since our last logging\n            if logging_bucket.skipped:\n                record.msg = '{}, {} additional messages skipped'.format(record.msg, logging_bucket.skipped)\n\n            # Reset our bucket\n            self.buckets[key] = DDLogger.LoggingBucket(current_bucket, 0)\n\n            # Call the base handle to actually log this record\n            super(DDLogger, self).handle(record)\n        else:\n            # Increment the count of records we have skipped\n            # DEV: `self.buckets[key]` is a tuple which is immutable so recreate instead\n            self.buckets[key] = DDLogger.LoggingBucket(logging_bucket.bucket, logging_bucket.skipped + 1)\n", "path": "ddtrace/internal/logger.py"}], "after_files": [{"content": "import collections\nimport logging\n\nfrom ..utils.formats import get_env\n\n\ndef get_logger(name):\n    \"\"\"\n    Retrieve or create a ``DDLogger`` instance.\n\n    This function mirrors the behavior of `logging.getLogger`.\n\n    If no logger with the provided name has been fetched before then\n    a new one is created.\n\n    If a previous logger has been created then it is returned.\n\n    DEV: We do not want to mess with `logging.setLoggerClass()`\n         That will totally mess with the user's loggers, we want\n         just our own, selective loggers to be DDLoggers\n\n    :param name: The name of the logger to fetch or create\n    :type name: str\n    :return: The logger instance\n    :rtype: ``DDLogger``\n    \"\"\"\n    # DEV: `logging.Logger.manager` refers to the single root `logging.Manager` instance\n    #   https://github.com/python/cpython/blob/48769a28ad6ef4183508951fa6a378531ace26a4/Lib/logging/__init__.py#L1824-L1826  # noqa\n    manager = logging.Logger.manager\n\n    # If the logger does not exist yet, create it\n    # DEV: `Manager.loggerDict` is a dict mapping logger name to logger\n    # DEV: This is a simplified version of `logging.Manager.getLogger`\n    #   https://github.com/python/cpython/blob/48769a28ad6ef4183508951fa6a378531ace26a4/Lib/logging/__init__.py#L1221-L1253  # noqa\n    if name not in manager.loggerDict:\n        manager.loggerDict[name] = DDLogger(name=name)\n\n    # Get our logger\n    logger = manager.loggerDict[name]\n\n    # If this log manager has a `_fixupParents` method then call it on our logger\n    # DEV: This helper is used to ensure our logger has an appropriate `Logger.parent` set,\n    #      without this then we cannot take advantage of the root loggers handlers\n    #   https://github.com/python/cpython/blob/7c7839329c2c66d051960ab1df096aed1cc9343e/Lib/logging/__init__.py#L1272-L1294  # noqa\n    # DEV: `_fixupParents` has been around for awhile, but add the `hasattr` guard... just in case.\n    if hasattr(manager, '_fixupParents'):\n        manager._fixupParents(logger)\n\n    # Return out logger\n    return logger\n\n\nclass DDLogger(logging.Logger):\n    \"\"\"\n    Custom rate limited logger used by ``ddtrace``\n\n    This logger class is used to rate limit the output of\n    log messages from within the ``ddtrace`` package.\n    \"\"\"\n    __slots__ = ('buckets', 'rate_limit')\n\n    # Named tuple used for keeping track of a log lines current time bucket and the number of log lines skipped\n    LoggingBucket = collections.namedtuple('LoggingBucket', ('bucket', 'skipped'))\n\n    def __init__(self, *args, **kwargs):\n        \"\"\"Constructor for ``DDLogger``\"\"\"\n        super(DDLogger, self).__init__(*args, **kwargs)\n\n        # Dict to keep track of the current time bucket per name/level/pathname/lineno\n        self.buckets = collections.defaultdict(lambda: DDLogger.LoggingBucket(0, 0))\n\n        # Allow 1 log record per name/level/pathname/lineno every 60 seconds by default\n        # Allow configuring via `DD_LOGGING_RATE_LIMIT`\n        # DEV: `DD_LOGGING_RATE_LIMIT=0` means to disable all rate limiting\n        self.rate_limit = int(get_env('logging', 'rate_limit', default=60))\n\n    def handle(self, record):\n        \"\"\"\n        Function used to call the handlers for a log line.\n\n        This implementation will first determine if this log line should\n        be logged or rate limited, and then call the base ``logging.Logger.handle``\n        function if it should be logged\n\n        DEV: This method has all of it's code inlined to reduce on functions calls\n\n        :param record: The log record being logged\n        :type record: ``logging.LogRecord``\n        \"\"\"\n        # If rate limiting has been disabled (`DD_LOGGING_RATE_LIMIT=0`) then apply no rate limit\n        if not self.rate_limit:\n            super(DDLogger, self).handle(record)\n            return\n\n        # Allow 1 log record by name/level/pathname/lineno every X seconds\n        # DEV: current unix time / rate (e.g. 300 seconds) = time bucket\n        #      int(1546615098.8404942 / 300) = 515538\n        # DEV: LogRecord `created` is a unix timestamp/float\n        # DEV: LogRecord has `levelname` and `levelno`, we want `levelno` e.g. `logging.DEBUG = 10`\n        current_bucket = int(record.created / self.rate_limit)\n\n        # Limit based on logger name, record level, filename, and line number\n        #   ('ddtrace.writer', 'DEBUG', '../site-packages/ddtrace/writer.py', 137)\n        # This way each unique log message can get logged at least once per time period\n        # DEV: LogRecord has `levelname` and `levelno`, we want `levelno` e.g. `logging.DEBUG = 10`\n        key = (record.name, record.levelno, record.pathname, record.lineno)\n\n        # Only log this message if the time bucket has changed from the previous time we ran\n        logging_bucket = self.buckets[key]\n        if logging_bucket.bucket != current_bucket:\n            # Append count of skipped messages if we have skipped some since our last logging\n            if logging_bucket.skipped:\n                record.msg = '{}, %s additional messages skipped'.format(record.msg)\n                record.args = record.args + (logging_bucket.skipped, )\n\n            # Reset our bucket\n            self.buckets[key] = DDLogger.LoggingBucket(current_bucket, 0)\n\n            # Call the base handle to actually log this record\n            super(DDLogger, self).handle(record)\n        else:\n            # Increment the count of records we have skipped\n            # DEV: `self.buckets[key]` is a tuple which is immutable so recreate instead\n            self.buckets[key] = DDLogger.LoggingBucket(logging_bucket.bucket, logging_bucket.skipped + 1)\n", "path": "ddtrace/internal/logger.py"}]}