We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Provide friendlier handling of invalid COCOTB_LOG_LEVEL
When running with `COCOTB_LOG_LEVEL=debug` I get the shown stack trace.

We should be able to improve the user experience a bit by:
- Converting values to ALLCAPS, i.e. debug becomes DEBUG
- Provide a more helpful error message with allowed options instead of a stacktrace.


```
        sim_build/fifo 
     -.--ns INFO     cocotb.gpi                         ..mbed/gpi_embed.cpp:74   in set_program_name_in_venv        Did not detect Python virtual environment. Using system-wide Python interpreter
     -.--ns INFO     cocotb.gpi                         ../gpi/GpiCommon.cpp:105  in gpi_print_registered_impl       VPI registered
     -.--ns INFO     cocotb.gpi                         ..mbed/gpi_embed.cpp:245  in embed_sim_init                  Python interpreter initialized and cocotb loaded!
Traceback (most recent call last):
  File "/home/philipp/.local/lib/python3.8/site-packages/cocotb/__init__.py", line 175, in _initialise_testbench
    _setup_logging()
  File "/home/philipp/.local/lib/python3.8/site-packages/cocotb/__init__.py", line 76, in _setup_logging
    cocotb.log.default_config()
  File "/home/philipp/.local/lib/python3.8/site-packages/cocotb/log.py", line 95, in default_config
    log.setLevel(_default_log)
  File "/usr/lib64/python3.8/logging/__init__.py", line 1409, in setLevel
    self.level = _checkLevel(level)
  File "/usr/lib64/python3.8/logging/__init__.py", line 197, in _checkLevel
    raise TypeError("Level not an integer or a valid string: %r" % level)
TypeError: Level not an integer or a valid string: <function debug at 0x7f8653508430>
```

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

{"golden_diff": "diff --git a/cocotb/log.py b/cocotb/log.py\n--- a/cocotb/log.py\n+++ b/cocotb/log.py\n@@ -52,6 +52,9 @@\n _LINENO_CHARS   = 4  # noqa\n _FUNCNAME_CHARS = 31  # noqa\n \n+# Default log level if not overwritten by the user.\n+_COCOTB_LOG_LEVEL_DEFAULT = \"INFO\"\n+\n \n def default_config():\n     \"\"\" Apply the default cocotb log formatting to the root logger.\n@@ -86,18 +89,25 @@\n \n     # apply level settings for cocotb\n     log = logging.getLogger('cocotb')\n-    level = os.getenv(\"COCOTB_LOG_LEVEL\", \"INFO\")\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-        _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+        log.setLevel(level)\n+    except ValueError:\n+        valid_levels = ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG')\n+        raise ValueError(\"Invalid log level %r passed through the \"\n+                         \"COCOTB_LOG_LEVEL environment variable. Valid log \"\n+                         \"levels: %s\" % (level, ', '.join(valid_levels)))\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-    simulator.log_level(_default_log)\n+    simulator.log_level(log.getEffectiveLevel())\n \n \n class SimBaseLog(logging.getLoggerClass()):\n", "issue": "Provide friendlier handling of invalid COCOTB_LOG_LEVEL\nWhen running with `COCOTB_LOG_LEVEL=debug` I get the shown stack trace.\r\n\r\nWe should be able to improve the user experience a bit by:\r\n- Converting values to ALLCAPS, i.e. debug becomes DEBUG\r\n- Provide a more helpful error message with allowed options instead of a stacktrace.\r\n\r\n\r\n```\r\n        sim_build/fifo \r\n     -.--ns INFO     cocotb.gpi                         ..mbed/gpi_embed.cpp:74   in set_program_name_in_venv        Did not detect Python virtual environment. Using system-wide Python interpreter\r\n     -.--ns INFO     cocotb.gpi                         ../gpi/GpiCommon.cpp:105  in gpi_print_registered_impl       VPI registered\r\n     -.--ns INFO     cocotb.gpi                         ..mbed/gpi_embed.cpp:245  in embed_sim_init                  Python interpreter initialized and cocotb loaded!\r\nTraceback (most recent call last):\r\n  File \"/home/philipp/.local/lib/python3.8/site-packages/cocotb/__init__.py\", line 175, in _initialise_testbench\r\n    _setup_logging()\r\n  File \"/home/philipp/.local/lib/python3.8/site-packages/cocotb/__init__.py\", line 76, in _setup_logging\r\n    cocotb.log.default_config()\r\n  File \"/home/philipp/.local/lib/python3.8/site-packages/cocotb/log.py\", line 95, in default_config\r\n    log.setLevel(_default_log)\r\n  File \"/usr/lib64/python3.8/logging/__init__.py\", line 1409, in setLevel\r\n    self.level = _checkLevel(level)\r\n  File \"/usr/lib64/python3.8/logging/__init__.py\", line 197, in _checkLevel\r\n    raise TypeError(\"Level not an integer or a valid string: %r\" % level)\r\nTypeError: Level not an integer or a valid string: <function debug at 0x7f8653508430>\r\n```\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.\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    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    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"}], "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# 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')\n        raise ValueError(\"Invalid log level %r passed through the \"\n                         \"COCOTB_LOG_LEVEL environment variable. Valid log \"\n                         \"levels: %s\" % (level, ', '.join(valid_levels)))\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    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, 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"}]}