We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Document how to get a rotating logger
In #1224 we had the request to support a "rotating" logger in cocotb. 
Now that #1266 has gone in there is a reasonably comfortable way to do that without modifying cocotb itself.

```python
root_logger = logging.getLogger()

# undo the setup cocotb did
for handler in root_logger.handlers:
    root_logger.remove_handler(handler)
    handler.close()

# do whatever configuration you want instead
file_handler = RotatingFileHandler(logfile, maxBytes=(1048576*5), backupCount=4)
file_handler.setFormatter(cocotb.log.SimLogFormatter())
root_logger.addHandler(file_handler)
```
at which point this seems like something that doesn't need to be in cocotb itself.

_Originally posted by @eric-wieser in https://github.com/cocotb/cocotb/pull/1224#issuecomment-581368001_


This issue is to document this as a useful snippet somewhere in the docs, as the general problem seems to be something other users might stumble across.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `cocotb/log.py`
Content:
```
1 # Copyright (c) 2013, 2018 Potential Ventures Ltd
2 # Copyright (c) 2013 SolarFlare Communications Inc
3 # All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are met:
7 #     * Redistributions of source code must retain the above copyright
8 #       notice, this list of conditions and the following disclaimer.
9 #     * Redistributions in binary form must reproduce the above copyright
10 #       notice, this list of conditions and the following disclaimer in the
11 #       documentation and/or other materials provided with the distribution.
12 #     * Neither the name of Potential Ventures Ltd,
13 #       SolarFlare Communications Inc nor the
14 #       names of its contributors may be used to endorse or promote products
15 #       derived from this software without specific prior written permission.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 # DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY
21 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28 """
29 Everything related to logging
30 """
31 
32 import os
33 import sys
34 import logging
35 import warnings
36 
37 from cocotb.utils import (
38     get_sim_time, get_time_from_sim_steps, want_color_output
39 )
40 
41 import cocotb.ANSI as ANSI
42 
43 if "COCOTB_REDUCED_LOG_FMT" in os.environ:
44     _suppress = True
45 else:
46     _suppress = False
47 
48 # Column alignment
49 _LEVEL_CHARS    = len("CRITICAL")  # noqa
50 _RECORD_CHARS   = 35  # noqa
51 _FILENAME_CHARS = 20  # noqa
52 _LINENO_CHARS   = 4  # noqa
53 _FUNCNAME_CHARS = 31  # noqa
54 
55 
56 def default_config():
57     """ Apply the default cocotb log formatting to the root logger.
58 
59     This hooks up the logger to write to stdout, using either
60     :class:`SimColourLogFormatter` or :class:`SimLogFormatter` depending
61     on whether colored output is requested. It also adds a
62     :class:`SimTimeContextFilter` filter so that
63     :attr:`~logging.LogRecord.created_sim_time` is available to the formatter.
64 
65     The logging level for cocotb logs is set based on the
66     :envvar:`COCOTB_LOG_LEVEL` environment variable, which defaults to ``INFO``.
67 
68     If desired, this logging configuration can be overwritten by calling
69     ``logging.basicConfig(..., force=True)`` (in Python 3.8 onwards), or by
70     manually resetting the root logger instance, for which examples can be
71     found online.
72     """
73     # construct an appropriate handler
74     hdlr = logging.StreamHandler(sys.stdout)
75     hdlr.addFilter(SimTimeContextFilter())
76     if want_color_output():
77         hdlr.setFormatter(SimColourLogFormatter())
78     else:
79         hdlr.setFormatter(SimLogFormatter())
80 
81     logging.setLoggerClass(SimBaseLog)  # For backwards compatibility
82     logging.basicConfig()
83     logging.getLogger().handlers = [hdlr]  # overwrite default handlers
84 
85     # apply level settings for cocotb
86     log = logging.getLogger('cocotb')
87     level = os.getenv("COCOTB_LOG_LEVEL", "INFO")
88     try:
89         _default_log = getattr(logging, level)
90     except AttributeError:
91         log.error("Unable to set logging level to %r" % level)
92         _default_log = logging.INFO
93     log.setLevel(_default_log)
94 
95     # Notify GPI of log level, which it uses as an optimization to avoid
96     # calling into Python.
97     if "COCOTB_SIM" in os.environ:
98         from cocotb import simulator
99         simulator.log_level(_default_log)
100 
101 
102 class SimBaseLog(logging.getLoggerClass()):
103     """ This class only exists for backwards compatibility """
104 
105     @property
106     def logger(self):
107         warnings.warn(
108             "the .logger attribute should not be used now that `SimLog` "
109             "returns a native logger instance directly.",
110             DeprecationWarning, stacklevel=2)
111         return self
112 
113     @property
114     def colour(self):
115         warnings.warn(
116             "the .colour attribute may be removed in future, use the "
117             "equivalent `cocotb.utils.want_color_output()` instead",
118             DeprecationWarning, stacklevel=2)
119         return want_color_output()
120 
121 
122 # this used to be a class, hence the unusual capitalization
123 def SimLog(name, ident=None):
124     """ Like logging.getLogger, but append a numeric identifier to the name """
125     if ident is not None:
126         name = "%s.0x%x" % (name, ident)
127     return logging.getLogger(name)
128 
129 
130 class SimTimeContextFilter(logging.Filter):
131     """
132     A filter to inject simulator times into the log records.
133 
134     This uses the approach described in the :ref:`Python logging cookbook <python:filters-contextual>`.
135 
136     This adds the :attr:`~logging.LogRecord.created_sim_time` attribute.
137     """
138 
139     # needed to make our docs render well
140     def __init__(self):
141         """ Takes no arguments """
142         super().__init__()
143 
144     def filter(self, record):
145         try:
146             record.created_sim_time = get_sim_time()
147         except RecursionError:
148             # get_sim_time may try to log - if that happens, we can't
149             # attach a simulator time to this message.
150             record.created_sim_time = None
151         return True
152 
153 
154 class SimLogFormatter(logging.Formatter):
155     """Log formatter to provide consistent log message handling.
156 
157     This will only add simulator timestamps if the handler object this
158     formatter is attached to has a :class:`SimTimeContextFilter` filter
159     attached, which cocotb ensures by default.
160     """
161 
162     # Removes the arguments from the base class. Docstring needed to make
163     # sphinx happy.
164     def __init__(self):
165         """ Takes no arguments. """
166         super().__init__()
167 
168     # Justify and truncate
169     @staticmethod
170     def ljust(string, chars):
171         if len(string) > chars:
172             return ".." + string[(chars - 2) * -1:]
173         return string.ljust(chars)
174 
175     @staticmethod
176     def rjust(string, chars):
177         if len(string) > chars:
178             return ".." + string[(chars - 2) * -1:]
179         return string.rjust(chars)
180 
181     def _format(self, level, record, msg, coloured=False):
182         sim_time = getattr(record, 'created_sim_time', None)
183         if sim_time is None:
184             sim_time_str = "  -.--ns"
185         else:
186             time_ns = get_time_from_sim_steps(sim_time, 'ns')
187             sim_time_str = "{:6.2f}ns".format(time_ns)
188         prefix = sim_time_str.rjust(11) + ' ' + level + ' '
189         if not _suppress:
190             prefix += self.ljust(record.name, _RECORD_CHARS) + \
191                       self.rjust(os.path.split(record.filename)[1], _FILENAME_CHARS) + \
192                       ':' + self.ljust(str(record.lineno), _LINENO_CHARS) + \
193                       ' in ' + self.ljust(str(record.funcName), _FUNCNAME_CHARS) + ' '
194 
195         # these lines are copied from the builtin logger
196         if record.exc_info:
197             # Cache the traceback text to avoid converting it multiple times
198             # (it's constant anyway)
199             if not record.exc_text:
200                 record.exc_text = self.formatException(record.exc_info)
201         if record.exc_text:
202             if msg[-1:] != "\n":
203                 msg = msg + "\n"
204             msg = msg + record.exc_text
205 
206         prefix_len = len(prefix)
207         if coloured:
208             prefix_len -= (len(level) - _LEVEL_CHARS)
209         pad = "\n" + " " * (prefix_len)
210         return prefix + pad.join(msg.split('\n'))
211 
212     def format(self, record):
213         """Prettify the log output, annotate with simulation time"""
214 
215         msg = record.getMessage()
216         level = record.levelname.ljust(_LEVEL_CHARS)
217 
218         return self._format(level, record, msg)
219 
220 
221 class SimColourLogFormatter(SimLogFormatter):
222     """Log formatter to provide consistent log message handling."""
223 
224     loglevel2colour = {
225         logging.DEBUG   :       "%s",
226         logging.INFO    :       ANSI.COLOR_INFO + "%s" + ANSI.COLOR_DEFAULT,
227         logging.WARNING :       ANSI.COLOR_WARNING + "%s" + ANSI.COLOR_DEFAULT,
228         logging.ERROR   :       ANSI.COLOR_ERROR + "%s" + ANSI.COLOR_DEFAULT,
229         logging.CRITICAL:       ANSI.COLOR_CRITICAL + "%s" + ANSI.COLOR_DEFAULT,
230     }
231 
232     def format(self, record):
233         """Prettify the log output, annotate with simulation time"""
234 
235         msg = record.getMessage()
236 
237         # Need to colour each line in case coloring is applied in the message
238         msg = '\n'.join([SimColourLogFormatter.loglevel2colour[record.levelno] % line for line in msg.split('\n')])
239         level = (SimColourLogFormatter.loglevel2colour[record.levelno] %
240                  record.levelname.ljust(_LEVEL_CHARS))
241 
242         return self._format(level, record, msg, coloured=True)
243 
244 
245 def _filter_from_c(logger_name, level):
246     return logging.getLogger(logger_name).isEnabledFor(level)
247 
248 
249 def _log_from_c(logger_name, level, filename, lineno, msg, function_name):
250     """
251     This is for use from the C world, and allows us to insert C stack
252     information.
253     """
254     logger = logging.getLogger(logger_name)
255     if logger.isEnabledFor(level):
256         record = logger.makeRecord(
257             logger.name,
258             level,
259             filename,
260             lineno,
261             msg,
262             None,
263             None,
264             function_name
265         )
266         logger.handle(record)
267
```
--- 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/cocotb/log.py b/cocotb/log.py
--- a/cocotb/log.py
+++ b/cocotb/log.py
@@ -67,8 +67,10 @@
 
     If desired, this logging configuration can be overwritten by calling
     ``logging.basicConfig(..., force=True)`` (in Python 3.8 onwards), or by
-    manually resetting the root logger instance, for which examples can be
-    found online.
+    manually resetting the root logger instance.
+    An example of this can be found in the section on :ref:`rotating-logger`.
+
+    .. versionadded:: 1.4
     """
     # construct an appropriate handler
     hdlr = logging.StreamHandler(sys.stdout)
@@ -135,11 +137,13 @@
     This uses the approach described in the :ref:`Python logging cookbook <python:filters-contextual>`.
 
     This adds the :attr:`~logging.LogRecord.created_sim_time` attribute.
+
+    .. versionadded:: 1.4
     """
 
     # needed to make our docs render well
     def __init__(self):
-        """ Takes no arguments """
+        """"""
         super().__init__()
 
     def filter(self, record):

{"golden_diff": "diff --git a/cocotb/log.py b/cocotb/log.py\n--- a/cocotb/log.py\n+++ b/cocotb/log.py\n@@ -67,8 +67,10 @@\n \n     If desired, this logging configuration can be overwritten by calling\n     ``logging.basicConfig(..., force=True)`` (in Python 3.8 onwards), or by\n-    manually resetting the root logger instance, for which examples can be\n-    found online.\n+    manually resetting the root logger instance.\n+    An example of this can be found in the section on :ref:`rotating-logger`.\n+\n+    .. versionadded:: 1.4\n     \"\"\"\n     # construct an appropriate handler\n     hdlr = logging.StreamHandler(sys.stdout)\n@@ -135,11 +137,13 @@\n     This uses the approach described in the :ref:`Python logging cookbook <python:filters-contextual>`.\n \n     This adds the :attr:`~logging.LogRecord.created_sim_time` attribute.\n+\n+    .. versionadded:: 1.4\n     \"\"\"\n \n     # needed to make our docs render well\n     def __init__(self):\n-        \"\"\" Takes no arguments \"\"\"\n+        \"\"\"\"\"\"\n         super().__init__()\n \n     def filter(self, record):\n", "issue": "Document how to get a rotating logger\nIn #1224 we had the request to support a \"rotating\" logger in cocotb. \r\nNow that #1266 has gone in there is a reasonably comfortable way to do that without modifying cocotb itself.\r\n\r\n```python\r\nroot_logger = logging.getLogger()\r\n\r\n# undo the setup cocotb did\r\nfor handler in root_logger.handlers:\r\n    root_logger.remove_handler(handler)\r\n    handler.close()\r\n\r\n# do whatever configuration you want instead\r\nfile_handler = RotatingFileHandler(logfile, maxBytes=(1048576*5), backupCount=4)\r\nfile_handler.setFormatter(cocotb.log.SimLogFormatter())\r\nroot_logger.addHandler(file_handler)\r\n```\r\nat which point this seems like something that doesn't need to be in cocotb itself.\r\n\r\n_Originally posted by @eric-wieser in https://github.com/cocotb/cocotb/pull/1224#issuecomment-581368001_\r\n\r\n\r\nThis issue is to document this as a useful snippet somewhere in the docs, as the general problem seems to be something other users might stumble across.\n", "before_files": [{"content": "# Copyright (c) 2013, 2018 Potential Ventures Ltd\n# Copyright (c) 2013 SolarFlare Communications Inc\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#     * Redistributions of source code must retain the above copyright\n#       notice, this list of conditions and the following disclaimer.\n#     * Redistributions in binary form must reproduce the above copyright\n#       notice, this list of conditions and the following disclaimer in the\n#       documentation and/or other materials provided with the distribution.\n#     * Neither the name of Potential Ventures Ltd,\n#       SolarFlare Communications Inc nor the\n#       names of its contributors may be used to endorse or promote products\n#       derived from this software without specific prior written permission.\n#\n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY\n# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n\"\"\"\nEverything related to logging\n\"\"\"\n\nimport os\nimport sys\nimport logging\nimport warnings\n\nfrom cocotb.utils import (\n    get_sim_time, get_time_from_sim_steps, want_color_output\n)\n\nimport cocotb.ANSI as ANSI\n\nif \"COCOTB_REDUCED_LOG_FMT\" in os.environ:\n    _suppress = True\nelse:\n    _suppress = False\n\n# Column alignment\n_LEVEL_CHARS    = len(\"CRITICAL\")  # noqa\n_RECORD_CHARS   = 35  # noqa\n_FILENAME_CHARS = 20  # noqa\n_LINENO_CHARS   = 4  # noqa\n_FUNCNAME_CHARS = 31  # noqa\n\n\ndef default_config():\n    \"\"\" Apply the default cocotb log formatting to the root logger.\n\n    This hooks up the logger to write to stdout, using either\n    :class:`SimColourLogFormatter` or :class:`SimLogFormatter` depending\n    on whether colored output is requested. It also adds a\n    :class:`SimTimeContextFilter` filter so that\n    :attr:`~logging.LogRecord.created_sim_time` is available to the formatter.\n\n    The logging level for cocotb logs is set based on the\n    :envvar:`COCOTB_LOG_LEVEL` environment variable, which defaults to ``INFO``.\n\n    If desired, this logging configuration can be overwritten by calling\n    ``logging.basicConfig(..., force=True)`` (in Python 3.8 onwards), or by\n    manually resetting the root logger instance, for which examples can be\n    found online.\n    \"\"\"\n    # construct an appropriate handler\n    hdlr = logging.StreamHandler(sys.stdout)\n    hdlr.addFilter(SimTimeContextFilter())\n    if want_color_output():\n        hdlr.setFormatter(SimColourLogFormatter())\n    else:\n        hdlr.setFormatter(SimLogFormatter())\n\n    logging.setLoggerClass(SimBaseLog)  # For backwards compatibility\n    logging.basicConfig()\n    logging.getLogger().handlers = [hdlr]  # overwrite default handlers\n\n    # apply level settings for cocotb\n    log = logging.getLogger('cocotb')\n    level = os.getenv(\"COCOTB_LOG_LEVEL\", \"INFO\")\n    try:\n        _default_log = getattr(logging, level)\n    except AttributeError:\n        log.error(\"Unable to set logging level to %r\" % level)\n        _default_log = logging.INFO\n    log.setLevel(_default_log)\n\n    # Notify GPI of log level, which it uses as an optimization to avoid\n    # calling into Python.\n    if \"COCOTB_SIM\" in os.environ:\n        from cocotb import simulator\n        simulator.log_level(_default_log)\n\n\nclass SimBaseLog(logging.getLoggerClass()):\n    \"\"\" This class only exists for backwards compatibility \"\"\"\n\n    @property\n    def logger(self):\n        warnings.warn(\n            \"the .logger attribute should not be used now that `SimLog` \"\n            \"returns a native logger instance directly.\",\n            DeprecationWarning, stacklevel=2)\n        return self\n\n    @property\n    def colour(self):\n        warnings.warn(\n            \"the .colour attribute may be removed in future, use the \"\n            \"equivalent `cocotb.utils.want_color_output()` instead\",\n            DeprecationWarning, stacklevel=2)\n        return want_color_output()\n\n\n# this used to be a class, hence the unusual capitalization\ndef SimLog(name, ident=None):\n    \"\"\" Like logging.getLogger, but append a numeric identifier to the name \"\"\"\n    if ident is not None:\n        name = \"%s.0x%x\" % (name, ident)\n    return logging.getLogger(name)\n\n\nclass SimTimeContextFilter(logging.Filter):\n    \"\"\"\n    A filter to inject simulator times into the log records.\n\n    This uses the approach described in the :ref:`Python logging cookbook <python:filters-contextual>`.\n\n    This adds the :attr:`~logging.LogRecord.created_sim_time` attribute.\n    \"\"\"\n\n    # needed to make our docs render well\n    def __init__(self):\n        \"\"\" Takes no arguments \"\"\"\n        super().__init__()\n\n    def filter(self, record):\n        try:\n            record.created_sim_time = get_sim_time()\n        except RecursionError:\n            # get_sim_time may try to log - if that happens, we can't\n            # attach a simulator time to this message.\n            record.created_sim_time = None\n        return True\n\n\nclass SimLogFormatter(logging.Formatter):\n    \"\"\"Log formatter to provide consistent log message handling.\n\n    This will only add simulator timestamps if the handler object this\n    formatter is attached to has a :class:`SimTimeContextFilter` filter\n    attached, which cocotb ensures by default.\n    \"\"\"\n\n    # Removes the arguments from the base class. Docstring needed to make\n    # sphinx happy.\n    def __init__(self):\n        \"\"\" Takes no arguments. \"\"\"\n        super().__init__()\n\n    # Justify and truncate\n    @staticmethod\n    def ljust(string, chars):\n        if len(string) > chars:\n            return \"..\" + string[(chars - 2) * -1:]\n        return string.ljust(chars)\n\n    @staticmethod\n    def rjust(string, chars):\n        if len(string) > chars:\n            return \"..\" + string[(chars - 2) * -1:]\n        return string.rjust(chars)\n\n    def _format(self, level, record, msg, coloured=False):\n        sim_time = getattr(record, 'created_sim_time', None)\n        if sim_time is None:\n            sim_time_str = \"  -.--ns\"\n        else:\n            time_ns = get_time_from_sim_steps(sim_time, 'ns')\n            sim_time_str = \"{:6.2f}ns\".format(time_ns)\n        prefix = sim_time_str.rjust(11) + ' ' + level + ' '\n        if not _suppress:\n            prefix += self.ljust(record.name, _RECORD_CHARS) + \\\n                      self.rjust(os.path.split(record.filename)[1], _FILENAME_CHARS) + \\\n                      ':' + self.ljust(str(record.lineno), _LINENO_CHARS) + \\\n                      ' in ' + self.ljust(str(record.funcName), _FUNCNAME_CHARS) + ' '\n\n        # these lines are copied from the builtin logger\n        if record.exc_info:\n            # Cache the traceback text to avoid converting it multiple times\n            # (it's constant anyway)\n            if not record.exc_text:\n                record.exc_text = self.formatException(record.exc_info)\n        if record.exc_text:\n            if msg[-1:] != \"\\n\":\n                msg = msg + \"\\n\"\n            msg = msg + record.exc_text\n\n        prefix_len = len(prefix)\n        if coloured:\n            prefix_len -= (len(level) - _LEVEL_CHARS)\n        pad = \"\\n\" + \" \" * (prefix_len)\n        return prefix + pad.join(msg.split('\\n'))\n\n    def format(self, record):\n        \"\"\"Prettify the log output, annotate with simulation time\"\"\"\n\n        msg = record.getMessage()\n        level = record.levelname.ljust(_LEVEL_CHARS)\n\n        return self._format(level, record, msg)\n\n\nclass SimColourLogFormatter(SimLogFormatter):\n    \"\"\"Log formatter to provide consistent log message handling.\"\"\"\n\n    loglevel2colour = {\n        logging.DEBUG   :       \"%s\",\n        logging.INFO    :       ANSI.COLOR_INFO + \"%s\" + ANSI.COLOR_DEFAULT,\n        logging.WARNING :       ANSI.COLOR_WARNING + \"%s\" + ANSI.COLOR_DEFAULT,\n        logging.ERROR   :       ANSI.COLOR_ERROR + \"%s\" + ANSI.COLOR_DEFAULT,\n        logging.CRITICAL:       ANSI.COLOR_CRITICAL + \"%s\" + ANSI.COLOR_DEFAULT,\n    }\n\n    def format(self, record):\n        \"\"\"Prettify the log output, annotate with simulation time\"\"\"\n\n        msg = record.getMessage()\n\n        # Need to colour each line in case coloring is applied in the message\n        msg = '\\n'.join([SimColourLogFormatter.loglevel2colour[record.levelno] % line for line in msg.split('\\n')])\n        level = (SimColourLogFormatter.loglevel2colour[record.levelno] %\n                 record.levelname.ljust(_LEVEL_CHARS))\n\n        return self._format(level, record, msg, coloured=True)\n\n\ndef _filter_from_c(logger_name, level):\n    return logging.getLogger(logger_name).isEnabledFor(level)\n\n\ndef _log_from_c(logger_name, level, filename, lineno, msg, function_name):\n    \"\"\"\n    This is for use from the C world, and allows us to insert C stack\n    information.\n    \"\"\"\n    logger = logging.getLogger(logger_name)\n    if logger.isEnabledFor(level):\n        record = logger.makeRecord(\n            logger.name,\n            level,\n            filename,\n            lineno,\n            msg,\n            None,\n            None,\n            function_name\n        )\n        logger.handle(record)\n", "path": "cocotb/log.py"}], "after_files": [{"content": "# Copyright (c) 2013, 2018 Potential Ventures Ltd\n# Copyright (c) 2013 SolarFlare Communications Inc\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#     * Redistributions of source code must retain the above copyright\n#       notice, this list of conditions and the following disclaimer.\n#     * Redistributions in binary form must reproduce the above copyright\n#       notice, this list of conditions and the following disclaimer in the\n#       documentation and/or other materials provided with the distribution.\n#     * Neither the name of Potential Ventures Ltd,\n#       SolarFlare Communications Inc nor the\n#       names of its contributors may be used to endorse or promote products\n#       derived from this software without specific prior written permission.\n#\n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY\n# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n\"\"\"\nEverything related to logging\n\"\"\"\n\nimport os\nimport sys\nimport logging\nimport warnings\n\nfrom cocotb.utils import (\n    get_sim_time, get_time_from_sim_steps, want_color_output\n)\n\nimport cocotb.ANSI as ANSI\n\nif \"COCOTB_REDUCED_LOG_FMT\" in os.environ:\n    _suppress = True\nelse:\n    _suppress = False\n\n# Column alignment\n_LEVEL_CHARS    = len(\"CRITICAL\")  # noqa\n_RECORD_CHARS   = 35  # noqa\n_FILENAME_CHARS = 20  # noqa\n_LINENO_CHARS   = 4  # noqa\n_FUNCNAME_CHARS = 31  # noqa\n\n\ndef default_config():\n    \"\"\" Apply the default cocotb log formatting to the root logger.\n\n    This hooks up the logger to write to stdout, using either\n    :class:`SimColourLogFormatter` or :class:`SimLogFormatter` depending\n    on whether colored output is requested. It also adds a\n    :class:`SimTimeContextFilter` filter so that\n    :attr:`~logging.LogRecord.created_sim_time` is available to the formatter.\n\n    The logging level for cocotb logs is set based on the\n    :envvar:`COCOTB_LOG_LEVEL` environment variable, which defaults to ``INFO``.\n\n    If desired, this logging configuration can be overwritten by calling\n    ``logging.basicConfig(..., force=True)`` (in Python 3.8 onwards), or by\n    manually resetting the root logger instance.\n    An example of this can be found in the section on :ref:`rotating-logger`.\n\n    .. versionadded:: 1.4\n    \"\"\"\n    # construct an appropriate handler\n    hdlr = logging.StreamHandler(sys.stdout)\n    hdlr.addFilter(SimTimeContextFilter())\n    if want_color_output():\n        hdlr.setFormatter(SimColourLogFormatter())\n    else:\n        hdlr.setFormatter(SimLogFormatter())\n\n\n    logging.setLoggerClass(SimBaseLog)  # For backwards compatibility\n    logging.basicConfig()\n    logging.getLogger().handlers = [hdlr]  # overwrite default handlers\n\n    # apply level settings for cocotb\n    log = logging.getLogger('cocotb')\n    level = os.getenv(\"COCOTB_LOG_LEVEL\", \"INFO\")\n    try:\n        _default_log = getattr(logging, level)\n    except AttributeError:\n        log.error(\"Unable to set logging level to %r\" % level)\n        _default_log = logging.INFO\n    log.setLevel(_default_log)\n\n    # Notify GPI of log level, which it uses as an optimization to avoid\n    # calling into Python.\n    if \"COCOTB_SIM\" in os.environ:\n        from cocotb import simulator\n        simulator.log_level(_default_log)\n\n\nclass SimBaseLog(logging.getLoggerClass()):\n    \"\"\" This class only exists for backwards compatibility \"\"\"\n\n    @property\n    def logger(self):\n        warnings.warn(\n            \"the .logger attribute should not be used now that `SimLog` \"\n            \"returns a native logger instance directly.\",\n            DeprecationWarning, stacklevel=2)\n        return self\n\n    @property\n    def colour(self):\n        warnings.warn(\n            \"the .colour attribute may be removed in future, use the \"\n            \"equivalent `cocotb.utils.want_color_output()` instead\",\n            DeprecationWarning, stacklevel=2)\n        return want_color_output()\n\n\n# this used to be a class, hence the unusual capitalization\ndef SimLog(name, ident=None):\n    \"\"\" Like logging.getLogger, but append a numeric identifier to the name \"\"\"\n    if ident is not None:\n        name = \"%s.0x%x\" % (name, ident)\n    return logging.getLogger(name)\n\n\nclass SimTimeContextFilter(logging.Filter):\n    \"\"\"\n    A filter to inject simulator times into the log records.\n\n    This uses the approach described in the :ref:`Python logging cookbook <python:filters-contextual>`.\n\n    This adds the :attr:`~logging.LogRecord.created_sim_time` attribute.\n\n    .. versionadded:: 1.4\n    \"\"\"\n\n    # needed to make our docs render well\n    def __init__(self):\n        \"\"\"\"\"\"\n        super().__init__()\n\n    def filter(self, record):\n        try:\n            record.created_sim_time = get_sim_time()\n        except RecursionError:\n            # get_sim_time may try to log - if that happens, we can't\n            # attach a simulator time to this message.\n            record.created_sim_time = None\n        return True\n\n\nclass SimLogFormatter(logging.Formatter):\n    \"\"\"Log formatter to provide consistent log message handling.\n\n    This will only add simulator timestamps if the handler object this\n    formatter is attached to has a :class:`SimTimeContextFilter` filter\n    attached, which cocotb ensures by default.\n    \"\"\"\n\n    # Removes the arguments from the base class. Docstring needed to make\n    # sphinx happy.\n    def __init__(self):\n        \"\"\" Takes no arguments. \"\"\"\n        super().__init__()\n\n    # Justify and truncate\n    @staticmethod\n    def ljust(string, chars):\n        if len(string) > chars:\n            return \"..\" + string[(chars - 2) * -1:]\n        return string.ljust(chars)\n\n    @staticmethod\n    def rjust(string, chars):\n        if len(string) > chars:\n            return \"..\" + string[(chars - 2) * -1:]\n        return string.rjust(chars)\n\n    def _format(self, level, record, msg, coloured=False):\n        sim_time = getattr(record, 'created_sim_time', None)\n        if sim_time is None:\n            sim_time_str = \"  -.--ns\"\n        else:\n            time_ns = get_time_from_sim_steps(sim_time, 'ns')\n            sim_time_str = \"{:6.2f}ns\".format(time_ns)\n        prefix = sim_time_str.rjust(11) + ' ' + level + ' '\n        if not _suppress:\n            prefix += self.ljust(record.name, _RECORD_CHARS) + \\\n                      self.rjust(os.path.split(record.filename)[1], _FILENAME_CHARS) + \\\n                      ':' + self.ljust(str(record.lineno), _LINENO_CHARS) + \\\n                      ' in ' + self.ljust(str(record.funcName), _FUNCNAME_CHARS) + ' '\n\n        # these lines are copied from the builtin logger\n        if record.exc_info:\n            # Cache the traceback text to avoid converting it multiple times\n            # (it's constant anyway)\n            if not record.exc_text:\n                record.exc_text = self.formatException(record.exc_info)\n        if record.exc_text:\n            if msg[-1:] != \"\\n\":\n                msg = msg + \"\\n\"\n            msg = msg + record.exc_text\n\n        prefix_len = len(prefix)\n        if coloured:\n            prefix_len -= (len(level) - _LEVEL_CHARS)\n        pad = \"\\n\" + \" \" * (prefix_len)\n        return prefix + pad.join(msg.split('\\n'))\n\n    def format(self, record):\n        \"\"\"Prettify the log output, annotate with simulation time\"\"\"\n\n        msg = record.getMessage()\n        level = record.levelname.ljust(_LEVEL_CHARS)\n\n        return self._format(level, record, msg)\n\n\nclass SimColourLogFormatter(SimLogFormatter):\n    \"\"\"Log formatter to provide consistent log message handling.\"\"\"\n\n    loglevel2colour = {\n        logging.DEBUG   :       \"%s\",\n        logging.INFO    :       ANSI.COLOR_INFO + \"%s\" + ANSI.COLOR_DEFAULT,\n        logging.WARNING :       ANSI.COLOR_WARNING + \"%s\" + ANSI.COLOR_DEFAULT,\n        logging.ERROR   :       ANSI.COLOR_ERROR + \"%s\" + ANSI.COLOR_DEFAULT,\n        logging.CRITICAL:       ANSI.COLOR_CRITICAL + \"%s\" + ANSI.COLOR_DEFAULT,\n    }\n\n    def format(self, record):\n        \"\"\"Prettify the log output, annotate with simulation time\"\"\"\n\n        msg = record.getMessage()\n\n        # Need to colour each line in case coloring is applied in the message\n        msg = '\\n'.join([SimColourLogFormatter.loglevel2colour[record.levelno] % line for line in msg.split('\\n')])\n        level = (SimColourLogFormatter.loglevel2colour[record.levelno] %\n                 record.levelname.ljust(_LEVEL_CHARS))\n\n        return self._format(level, record, msg, coloured=True)\n\n\ndef _filter_from_c(logger_name, level):\n    return logging.getLogger(logger_name).isEnabledFor(level)\n\n\ndef _log_from_c(logger_name, level, filename, lineno, msg, function_name):\n    \"\"\"\n    This is for use from the C world, and allows us to insert C stack\n    information.\n    \"\"\"\n    logger = logging.getLogger(logger_name)\n    if logger.isEnabledFor(level):\n        record = logger.makeRecord(\n            logger.name,\n            level,\n            filename,\n            lineno,\n            msg,\n            None,\n            None,\n            function_name\n        )\n        logger.handle(record)\n", "path": "cocotb/log.py"}]}