We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Update cached logging level in C when Python log level is changed
As an optimization gpi_log caches the gpi logging level. This cached value is not currently updated when the "cocotb.gpi" logger's level is updated in Python. This will require creating a `logging.Handler` subclass that calls `simulator.set_level` when its `setLevel` method is called.

--- 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 logging
33 import os
34 import sys
35 import warnings
36 
37 import cocotb.ANSI as ANSI
38 from cocotb.utils import get_sim_time, get_time_from_sim_steps, want_color_output
39 
40 try:
41     _suppress = int(os.environ.get("COCOTB_REDUCED_LOG_FMT", "1"))
42 except ValueError:
43     _suppress = 1
44 
45 # Column alignment
46 _LEVEL_CHARS = len("CRITICAL")  # noqa
47 _RECORD_CHARS = 35  # noqa
48 _FILENAME_CHARS = 20  # noqa
49 _LINENO_CHARS = 4  # noqa
50 _FUNCNAME_CHARS = 31  # noqa
51 
52 # Custom log level
53 logging.TRACE = 5
54 logging.addLevelName(5, "TRACE")
55 
56 # Default log level if not overwritten by the user.
57 _COCOTB_LOG_LEVEL_DEFAULT = "INFO"
58 
59 
60 def default_config():
61     """Apply the default cocotb log formatting to the root logger.
62 
63     This hooks up the logger to write to stdout, using either
64     :class:`SimColourLogFormatter` or :class:`SimLogFormatter` depending
65     on whether colored output is requested. It also adds a
66     :class:`SimTimeContextFilter` filter so that
67     :attr:`~logging.LogRecord.created_sim_time` is available to the formatter.
68 
69     The logging level for cocotb logs is set based on the
70     :envvar:`COCOTB_LOG_LEVEL` environment variable, which defaults to ``INFO``.
71 
72     If desired, this logging configuration can be overwritten by calling
73     ``logging.basicConfig(..., force=True)`` (in Python 3.8 onwards), or by
74     manually resetting the root logger instance.
75     An example of this can be found in the section on :ref:`rotating-logger`.
76 
77     .. versionadded:: 1.4
78     """
79     # construct an appropriate handler
80     hdlr = logging.StreamHandler(sys.stdout)
81     hdlr.addFilter(SimTimeContextFilter())
82     if want_color_output():
83         hdlr.setFormatter(SimColourLogFormatter())
84     else:
85         hdlr.setFormatter(SimLogFormatter())
86 
87     logging.setLoggerClass(SimBaseLog)  # For backwards compatibility
88     logging.basicConfig()
89     logging.getLogger().handlers = [hdlr]  # overwrite default handlers
90 
91     # apply level settings for cocotb
92     log = logging.getLogger("cocotb")
93 
94     try:
95         # All log levels are upper case, convert the user input for convenience.
96         level = os.environ["COCOTB_LOG_LEVEL"].upper()
97     except KeyError:
98         level = _COCOTB_LOG_LEVEL_DEFAULT
99 
100     try:
101         log.setLevel(level)
102     except ValueError:
103         valid_levels = ("CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "TRACE")
104         raise ValueError(
105             "Invalid log level %r passed through the "
106             "COCOTB_LOG_LEVEL environment variable. Valid log "
107             "levels: %s" % (level, ", ".join(valid_levels))
108         )
109 
110     # Notify GPI of log level, which it uses as an optimization to avoid
111     # calling into Python.
112     from cocotb import simulator
113 
114     simulator.log_level(log.getEffectiveLevel())
115 
116 
117 class SimBaseLog(logging.getLoggerClass()):
118     """This class only exists for backwards compatibility"""
119 
120     @property
121     def logger(self):
122         warnings.warn(
123             "the .logger attribute should not be used now that `SimLog` "
124             "returns a native logger instance directly.",
125             DeprecationWarning,
126             stacklevel=2,
127         )
128         return self
129 
130     @property
131     def colour(self):
132         warnings.warn(
133             "the .colour attribute may be removed in future, use the "
134             "equivalent `cocotb.utils.want_color_output()` instead",
135             DeprecationWarning,
136             stacklevel=2,
137         )
138         return want_color_output()
139 
140 
141 # this used to be a class, hence the unusual capitalization
142 def SimLog(name, ident=None):
143     """Like logging.getLogger, but append a numeric identifier to the name"""
144     if ident is not None:
145         name = f"{name}.0x{ident:x}"
146     return logging.getLogger(name)
147 
148 
149 class SimTimeContextFilter(logging.Filter):
150     """
151     A filter to inject simulator times into the log records.
152 
153     This uses the approach described in the :ref:`Python logging cookbook <python:filters-contextual>`.
154 
155     This adds the :attr:`~logging.LogRecord.created_sim_time` attribute.
156 
157     .. versionadded:: 1.4
158     """
159 
160     # needed to make our docs render well
161     def __init__(self):
162         """"""
163         super().__init__()
164 
165     def filter(self, record):
166         try:
167             record.created_sim_time = get_sim_time()
168         except RecursionError:
169             # get_sim_time may try to log - if that happens, we can't
170             # attach a simulator time to this message.
171             record.created_sim_time = None
172         return True
173 
174 
175 class SimLogFormatter(logging.Formatter):
176     """Log formatter to provide consistent log message handling.
177 
178     This will only add simulator timestamps if the handler object this
179     formatter is attached to has a :class:`SimTimeContextFilter` filter
180     attached, which cocotb ensures by default.
181     """
182 
183     # Removes the arguments from the base class. Docstring needed to make
184     # sphinx happy.
185     def __init__(self):
186         """Takes no arguments."""
187         super().__init__()
188 
189     # Justify and truncate
190     @staticmethod
191     def ljust(string, chars):
192         if len(string) > chars:
193             return ".." + string[(chars - 2) * -1 :]
194         return string.ljust(chars)
195 
196     @staticmethod
197     def rjust(string, chars):
198         if len(string) > chars:
199             return ".." + string[(chars - 2) * -1 :]
200         return string.rjust(chars)
201 
202     def _format(self, level, record, msg, coloured=False):
203         sim_time = getattr(record, "created_sim_time", None)
204         if sim_time is None:
205             sim_time_str = "  -.--ns"
206         else:
207             time_ns = get_time_from_sim_steps(sim_time, "ns")
208             sim_time_str = f"{time_ns:6.2f}ns"
209         prefix = sim_time_str.rjust(11) + " " + level + " "
210         if not _suppress:
211             prefix += (
212                 self.ljust(record.name, _RECORD_CHARS)
213                 + self.rjust(os.path.split(record.filename)[1], _FILENAME_CHARS)
214                 + ":"
215                 + self.ljust(str(record.lineno), _LINENO_CHARS)
216                 + " in "
217                 + self.ljust(str(record.funcName), _FUNCNAME_CHARS)
218                 + " "
219             )
220 
221         # these lines are copied from the builtin logger
222         if record.exc_info:
223             # Cache the traceback text to avoid converting it multiple times
224             # (it's constant anyway)
225             if not record.exc_text:
226                 record.exc_text = self.formatException(record.exc_info)
227         if record.exc_text:
228             if msg[-1:] != "\n":
229                 msg = msg + "\n"
230             msg = msg + record.exc_text
231 
232         prefix_len = len(prefix)
233         if coloured:
234             prefix_len -= len(level) - _LEVEL_CHARS
235         pad = "\n" + " " * (prefix_len)
236         return prefix + pad.join(msg.split("\n"))
237 
238     def format(self, record):
239         """Prettify the log output, annotate with simulation time"""
240 
241         msg = record.getMessage()
242         level = record.levelname.ljust(_LEVEL_CHARS)
243 
244         return self._format(level, record, msg)
245 
246 
247 class SimColourLogFormatter(SimLogFormatter):
248     """Log formatter to provide consistent log message handling."""
249 
250     loglevel2colour = {
251         logging.TRACE: "%s",
252         logging.DEBUG: "%s",
253         logging.INFO: "%s",
254         logging.WARNING: ANSI.COLOR_WARNING + "%s" + ANSI.COLOR_DEFAULT,
255         logging.ERROR: ANSI.COLOR_ERROR + "%s" + ANSI.COLOR_DEFAULT,
256         logging.CRITICAL: ANSI.COLOR_CRITICAL + "%s" + ANSI.COLOR_DEFAULT,
257     }
258 
259     def format(self, record):
260         """Prettify the log output, annotate with simulation time"""
261 
262         msg = record.getMessage()
263 
264         # Need to colour each line in case coloring is applied in the message
265         msg = "\n".join(
266             [
267                 SimColourLogFormatter.loglevel2colour.get(record.levelno, "%s") % line
268                 for line in msg.split("\n")
269             ]
270         )
271         level = SimColourLogFormatter.loglevel2colour.get(
272             record.levelno, "%s"
273         ) % record.levelname.ljust(_LEVEL_CHARS)
274 
275         return self._format(level, record, msg, coloured=True)
276 
277 
278 def _filter_from_c(logger_name, level):
279     return logging.getLogger(logger_name).isEnabledFor(level)
280 
281 
282 def _log_from_c(logger_name, level, filename, lineno, msg, function_name):
283     """
284     This is for use from the C world, and allows us to insert C stack
285     information.
286     """
287     logger = logging.getLogger(logger_name)
288     if logger.isEnabledFor(level):
289         record = logger.makeRecord(
290             logger.name, level, filename, lineno, msg, None, None, function_name
291         )
292         logger.handle(record)
293
```
--- 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
@@ -35,6 +35,7 @@
 import warnings
 
 import cocotb.ANSI as ANSI
+from cocotb import simulator
 from cocotb.utils import get_sim_time, get_time_from_sim_steps, want_color_output
 
 try:
@@ -109,8 +110,6 @@
 
     # Notify GPI of log level, which it uses as an optimization to avoid
     # calling into Python.
-    from cocotb import simulator
-
     simulator.log_level(log.getEffectiveLevel())
 
 
@@ -137,6 +136,11 @@
         )
         return want_color_output()
 
+    def setLevel(self, level: int) -> None:
+        super().setLevel(level)
+        if self.name == "gpi":
+            simulator.log_level(level)
+
 
 # this used to be a class, hence the unusual capitalization
 def SimLog(name, ident=None):

{"golden_diff": "diff --git a/cocotb/log.py b/cocotb/log.py\n--- a/cocotb/log.py\n+++ b/cocotb/log.py\n@@ -35,6 +35,7 @@\n import warnings\n \n import cocotb.ANSI as ANSI\n+from cocotb import simulator\n from cocotb.utils import get_sim_time, get_time_from_sim_steps, want_color_output\n \n try:\n@@ -109,8 +110,6 @@\n \n     # Notify GPI of log level, which it uses as an optimization to avoid\n     # calling into Python.\n-    from cocotb import simulator\n-\n     simulator.log_level(log.getEffectiveLevel())\n \n \n@@ -137,6 +136,11 @@\n         )\n         return want_color_output()\n \n+    def setLevel(self, level: int) -> None:\n+        super().setLevel(level)\n+        if self.name == \"gpi\":\n+            simulator.log_level(level)\n+\n \n # this used to be a class, hence the unusual capitalization\n def SimLog(name, ident=None):\n", "issue": "Update cached logging level in C when Python log level is changed\nAs an optimization gpi_log caches the gpi logging level. This cached value is not currently updated when the \"cocotb.gpi\" logger's level is updated in Python. This will require creating a `logging.Handler` subclass that calls `simulator.set_level` when its `setLevel` method is called.\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 logging\nimport os\nimport sys\nimport warnings\n\nimport cocotb.ANSI as ANSI\nfrom cocotb.utils import get_sim_time, get_time_from_sim_steps, want_color_output\n\ntry:\n    _suppress = int(os.environ.get(\"COCOTB_REDUCED_LOG_FMT\", \"1\"))\nexcept ValueError:\n    _suppress = 1\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# Custom log level\nlogging.TRACE = 5\nlogging.addLevelName(5, \"TRACE\")\n\n# Default log level if not overwritten by the user.\n_COCOTB_LOG_LEVEL_DEFAULT = \"INFO\"\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    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\n    try:\n        # All log levels are upper case, convert the user input for convenience.\n        level = os.environ[\"COCOTB_LOG_LEVEL\"].upper()\n    except KeyError:\n        level = _COCOTB_LOG_LEVEL_DEFAULT\n\n    try:\n        log.setLevel(level)\n    except ValueError:\n        valid_levels = (\"CRITICAL\", \"ERROR\", \"WARNING\", \"INFO\", \"DEBUG\", \"TRACE\")\n        raise ValueError(\n            \"Invalid log level %r passed through the \"\n            \"COCOTB_LOG_LEVEL environment variable. Valid log \"\n            \"levels: %s\" % (level, \", \".join(valid_levels))\n        )\n\n    # Notify GPI of log level, which it uses as an optimization to avoid\n    # calling into Python.\n    from cocotb import simulator\n\n    simulator.log_level(log.getEffectiveLevel())\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,\n            stacklevel=2,\n        )\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,\n            stacklevel=2,\n        )\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 = f\"{name}.0x{ident:x}\"\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 = f\"{time_ns:6.2f}ns\"\n        prefix = sim_time_str.rjust(11) + \" \" + level + \" \"\n        if not _suppress:\n            prefix += (\n                self.ljust(record.name, _RECORD_CHARS)\n                + self.rjust(os.path.split(record.filename)[1], _FILENAME_CHARS)\n                + \":\"\n                + self.ljust(str(record.lineno), _LINENO_CHARS)\n                + \" in \"\n                + self.ljust(str(record.funcName), _FUNCNAME_CHARS)\n                + \" \"\n            )\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.TRACE: \"%s\",\n        logging.DEBUG: \"%s\",\n        logging.INFO: \"%s\",\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(\n            [\n                SimColourLogFormatter.loglevel2colour.get(record.levelno, \"%s\") % line\n                for line in msg.split(\"\\n\")\n            ]\n        )\n        level = SimColourLogFormatter.loglevel2colour.get(\n            record.levelno, \"%s\"\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, level, filename, lineno, msg, None, None, 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 logging\nimport os\nimport sys\nimport warnings\n\nimport cocotb.ANSI as ANSI\nfrom cocotb import simulator\nfrom cocotb.utils import get_sim_time, get_time_from_sim_steps, want_color_output\n\ntry:\n    _suppress = int(os.environ.get(\"COCOTB_REDUCED_LOG_FMT\", \"1\"))\nexcept ValueError:\n    _suppress = 1\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# Custom log level\nlogging.TRACE = 5\nlogging.addLevelName(5, \"TRACE\")\n\n# Default log level if not overwritten by the user.\n_COCOTB_LOG_LEVEL_DEFAULT = \"INFO\"\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    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\n    try:\n        # All log levels are upper case, convert the user input for convenience.\n        level = os.environ[\"COCOTB_LOG_LEVEL\"].upper()\n    except KeyError:\n        level = _COCOTB_LOG_LEVEL_DEFAULT\n\n    try:\n        log.setLevel(level)\n    except ValueError:\n        valid_levels = (\"CRITICAL\", \"ERROR\", \"WARNING\", \"INFO\", \"DEBUG\", \"TRACE\")\n        raise ValueError(\n            \"Invalid log level %r passed through the \"\n            \"COCOTB_LOG_LEVEL environment variable. Valid log \"\n            \"levels: %s\" % (level, \", \".join(valid_levels))\n        )\n\n    # Notify GPI of log level, which it uses as an optimization to avoid\n    # calling into Python.\n    simulator.log_level(log.getEffectiveLevel())\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,\n            stacklevel=2,\n        )\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,\n            stacklevel=2,\n        )\n        return want_color_output()\n\n    def setLevel(self, level: int) -> None:\n        super().setLevel(level)\n        if self.name == \"gpi\":\n            simulator.log_level(level)\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 = f\"{name}.0x{ident:x}\"\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 = f\"{time_ns:6.2f}ns\"\n        prefix = sim_time_str.rjust(11) + \" \" + level + \" \"\n        if not _suppress:\n            prefix += (\n                self.ljust(record.name, _RECORD_CHARS)\n                + self.rjust(os.path.split(record.filename)[1], _FILENAME_CHARS)\n                + \":\"\n                + self.ljust(str(record.lineno), _LINENO_CHARS)\n                + \" in \"\n                + self.ljust(str(record.funcName), _FUNCNAME_CHARS)\n                + \" \"\n            )\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.TRACE: \"%s\",\n        logging.DEBUG: \"%s\",\n        logging.INFO: \"%s\",\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(\n            [\n                SimColourLogFormatter.loglevel2colour.get(record.levelno, \"%s\") % line\n                for line in msg.split(\"\\n\")\n            ]\n        )\n        level = SimColourLogFormatter.loglevel2colour.get(\n            record.levelno, \"%s\"\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, level, filename, lineno, msg, None, None, function_name\n        )\n        logger.handle(record)\n", "path": "cocotb/log.py"}]}