We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Opsdroid logger returning errors
# Description
Hi, I have been using `opsdroid` for a few days now, and while it is a good framework, I have been having a lot of trouble. Most recently I have been getting a error `formatter refrenced before use`. I have linted my configuration.yaml and none of my python files have errors. The error message below only shows errors in opsdroid library files. Even so, I am probably doing something wrong. Any help is greatly appreciated!

## Steps to Reproduce
I just linted and built my config. Neither of those actions returned errors.

## Expected Functionality
My bot should have run on Telegram and in bash.

## Experienced Functionality
```bash
Traceback (most recent call last):
  File "/home/gideongrinberg/.local/bin/opsdroid", line 8, in <module>
    sys.exit(cli())
  File "/usr/local/lib/python3.6/dist-packages/click/core.py", line 764, in __call__
    return self.main(*args, **kwargs)
  File "/usr/local/lib/python3.6/dist-packages/click/core.py", line 717, in main
    rv = self.invoke(ctx)
  File "/usr/local/lib/python3.6/dist-packages/click/core.py", line 1137, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/usr/local/lib/python3.6/dist-packages/click/core.py", line 956, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/local/lib/python3.6/dist-packages/click/core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "/home/gideongrinberg/.local/lib/python3.6/site-packages/opsdroid/cli/start.py", line 38, in start
    configure_logging(config)
  File "/home/gideongrinberg/.local/lib/python3.6/site-packages/opsdroid/logging.py", line 93, in configure_logging
    console_handler.setFormatter(formatter)
UnboundLocalError: local variable 'formatter' referenced before assignment
```
## Versions
- **Opsdroid version:** 0.17.1
- **Python version:** Python 3.6.9
- **OS/Docker version:** Ubuntu 18.04 on Window Subsystem Linux (Windows 10)

## Configuration File
My config.yaml is to large to include, but this is the only line I've change from the example (other than adding tokens)
```yaml

  recycle-nlp:
    path: '~/opdroid_recycle/skill-recycle-nlp'
```
Again, that file returns no errors when I run `opsdroid config -f [PATH] lint` or `opsdroid config -f [PATH] build`.

Additionally, the python file:
```python
from opsdroid.skill import Skill
from opsdroid.matchers import match_luisai_intent

class recycle-nlp(Skill):
@match_luisai_intent('recycle')
    async def recycle-nlp(self, message):
        if message.luisai["topScoringIntent"]["intent"]=="recycle":
            await message.respond(str(message.luisai))
```
My directory structure (/home for WSL, not windows):
```
| /home
            |____ opsdroid_recycle
                           |
                           |_____ config.yaml
                           |_____skill-recycle-nlp
                                               |
                                               |____ __init__.py
                                                |______ README.md
                                                |______ LICENSE
           |
           |___.local/lib/python3.6/site-packages
```
## Additional Details
Interestingly, my bot worked fine with the example config

Any help is much appreciated! 


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `opsdroid/logging.py`
Content:
```
1 """Class for Filter logs and logging logic."""
2 
3 import os
4 import logging
5 import contextlib
6 
7 from logging.handlers import RotatingFileHandler
8 from opsdroid.const import DEFAULT_LOG_FILENAME, __version__
9 
10 _LOGGER = logging.getLogger(__name__)
11 
12 
13 class ParsingFilter(logging.Filter):
14     """Class that filters logs."""
15 
16     def __init__(self, config, *parse_list):
17         """Create object to implement filtering."""
18         super(ParsingFilter, self).__init__()
19         self.config = config["logging"]
20         try:
21             if (
22                 self.config["filter"]["whitelist"]
23                 and self.config["filter"]["blacklist"]
24             ):
25                 _LOGGER.warning(
26                     _(
27                         "Both whitelist and blacklist filters found in configuration. "
28                         "Only one can be used at a time - only the whitelist filter will be used."
29                     )
30                 )
31                 self.parse_list = [
32                     logging.Filter(name) for name in parse_list[0]["whitelist"]
33                 ]
34         except KeyError:
35             self.parse_list = parse_list[0].get("whitelist") or parse_list[0].get(
36                 "blacklist"
37             )
38 
39             self.parse_list = [logging.Filter(name) for name in self.parse_list]
40 
41     def filter(self, record):
42         """Apply filter to the log message.
43 
44         This is a subset of Logger.filter, this method applies the logger
45         filters and returns a bool. If the value is true the record will
46         be passed to the handlers and the log shown. If the value is
47         false it will be ignored.
48 
49         Args:
50             record: a log record containing the log message and the
51                 name of the log - example: opsdroid.core.
52 
53         Returns:
54             Boolean: If True - pass the log to handler.
55 
56         """
57 
58         if self.config["filter"].get("whitelist"):
59             return any(name.filter(record) for name in self.parse_list)
60         return not any(name.filter(record) for name in self.parse_list)
61 
62 
63 def configure_logging(config):
64     """Configure the root logger based on user config."""
65     rootlogger = logging.getLogger()
66     logging_config = config or {}
67 
68     while rootlogger.handlers:
69         rootlogger.handlers.pop()
70 
71     try:
72         if config["logging"]["path"]:
73             logfile_path = os.path.expanduser(config["logging"]["path"])
74         else:
75             logfile_path = config["logging"]["path"]
76     except KeyError:
77         logfile_path = DEFAULT_LOG_FILENAME
78 
79     try:
80         log_level = get_logging_level(config["logging"]["level"])
81     except KeyError:
82         log_level = logging.INFO
83 
84     rootlogger.setLevel(log_level)
85 
86     try:
87         if config["logging"]["extended"]:
88             formatter = logging.Formatter(
89                 "%(levelname)s %(name)s.%(funcName)s(): %(message)s"
90             )
91     except KeyError:
92         formatter = logging.Formatter("%(levelname)s %(name)s: %(message)s")
93 
94     console_handler = logging.StreamHandler()
95     console_handler.setLevel(log_level)
96     console_handler.setFormatter(formatter)
97 
98     with contextlib.suppress(KeyError):
99         console_handler.addFilter(ParsingFilter(config, config["logging"]["filter"]))
100 
101     rootlogger.addHandler(console_handler)
102 
103     with contextlib.suppress(KeyError):
104         if not config["logging"]["console"]:
105             console_handler.setLevel(logging.CRITICAL)
106 
107     if logfile_path:
108         logdir = os.path.dirname(os.path.realpath(logfile_path))
109         if not os.path.isdir(logdir):
110             os.makedirs(logdir)
111 
112         file_handler = RotatingFileHandler(
113             logfile_path, maxBytes=logging_config.get("file-size", 50e6)
114         )
115 
116         file_handler.setLevel(log_level)
117         file_handler.setFormatter(formatter)
118 
119         with contextlib.suppress(KeyError):
120             file_handler.addFilter(ParsingFilter(config, config["logging"]["filter"]))
121 
122         rootlogger.addHandler(file_handler)
123     _LOGGER.info("=" * 40)
124     _LOGGER.info(_("Started opsdroid %s."), __version__)
125 
126 
127 def get_logging_level(logging_level):
128     """Get the logger level based on the user configuration.
129 
130     Args:
131         logging_level: logging level from config file
132 
133     Returns:
134         logging LEVEL ->
135             CRITICAL = 50
136             FATAL = CRITICAL
137             ERROR = 40
138             WARNING = 30
139             WARN = WARNING
140             INFO = 20
141             DEBUG = 10
142             NOTSET = 0
143 
144     """
145     if logging_level == "critical":
146         return logging.CRITICAL
147 
148     if logging_level == "error":
149         return logging.ERROR
150     if logging_level == "warning":
151         return logging.WARNING
152 
153     if logging_level == "debug":
154         return logging.DEBUG
155 
156     return logging.INFO
157
```
--- 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/opsdroid/logging.py b/opsdroid/logging.py
--- a/opsdroid/logging.py
+++ b/opsdroid/logging.py
@@ -83,13 +83,13 @@
 
     rootlogger.setLevel(log_level)
 
-    try:
+    formatter = logging.Formatter("%(levelname)s %(name)s: %(message)s")
+
+    with contextlib.suppress(KeyError):
         if config["logging"]["extended"]:
             formatter = logging.Formatter(
                 "%(levelname)s %(name)s.%(funcName)s(): %(message)s"
             )
-    except KeyError:
-        formatter = logging.Formatter("%(levelname)s %(name)s: %(message)s")
 
     console_handler = logging.StreamHandler()
     console_handler.setLevel(log_level)

{"golden_diff": "diff --git a/opsdroid/logging.py b/opsdroid/logging.py\n--- a/opsdroid/logging.py\n+++ b/opsdroid/logging.py\n@@ -83,13 +83,13 @@\n \n     rootlogger.setLevel(log_level)\n \n-    try:\n+    formatter = logging.Formatter(\"%(levelname)s %(name)s: %(message)s\")\n+\n+    with contextlib.suppress(KeyError):\n         if config[\"logging\"][\"extended\"]:\n             formatter = logging.Formatter(\n                 \"%(levelname)s %(name)s.%(funcName)s(): %(message)s\"\n             )\n-    except KeyError:\n-        formatter = logging.Formatter(\"%(levelname)s %(name)s: %(message)s\")\n \n     console_handler = logging.StreamHandler()\n     console_handler.setLevel(log_level)\n", "issue": "Opsdroid logger returning errors\n# Description\r\nHi, I have been using `opsdroid` for a few days now, and while it is a good framework, I have been having a lot of trouble. Most recently I have been getting a error `formatter refrenced before use`. I have linted my configuration.yaml and none of my python files have errors. The error message below only shows errors in opsdroid library files. Even so, I am probably doing something wrong. Any help is greatly appreciated!\r\n\r\n## Steps to Reproduce\r\nI just linted and built my config. Neither of those actions returned errors.\r\n\r\n## Expected Functionality\r\nMy bot should have run on Telegram and in bash.\r\n\r\n## Experienced Functionality\r\n```bash\r\nTraceback (most recent call last):\r\n  File \"/home/gideongrinberg/.local/bin/opsdroid\", line 8, in <module>\r\n    sys.exit(cli())\r\n  File \"/usr/local/lib/python3.6/dist-packages/click/core.py\", line 764, in __call__\r\n    return self.main(*args, **kwargs)\r\n  File \"/usr/local/lib/python3.6/dist-packages/click/core.py\", line 717, in main\r\n    rv = self.invoke(ctx)\r\n  File \"/usr/local/lib/python3.6/dist-packages/click/core.py\", line 1137, in invoke\r\n    return _process_result(sub_ctx.command.invoke(sub_ctx))\r\n  File \"/usr/local/lib/python3.6/dist-packages/click/core.py\", line 956, in invoke\r\n    return ctx.invoke(self.callback, **ctx.params)\r\n  File \"/usr/local/lib/python3.6/dist-packages/click/core.py\", line 555, in invoke\r\n    return callback(*args, **kwargs)\r\n  File \"/home/gideongrinberg/.local/lib/python3.6/site-packages/opsdroid/cli/start.py\", line 38, in start\r\n    configure_logging(config)\r\n  File \"/home/gideongrinberg/.local/lib/python3.6/site-packages/opsdroid/logging.py\", line 93, in configure_logging\r\n    console_handler.setFormatter(formatter)\r\nUnboundLocalError: local variable 'formatter' referenced before assignment\r\n```\r\n## Versions\r\n- **Opsdroid version:** 0.17.1\r\n- **Python version:** Python 3.6.9\r\n- **OS/Docker version:** Ubuntu 18.04 on Window Subsystem Linux (Windows 10)\r\n\r\n## Configuration File\r\nMy config.yaml is to large to include, but this is the only line I've change from the example (other than adding tokens)\r\n```yaml\r\n\r\n  recycle-nlp:\r\n    path: '~/opdroid_recycle/skill-recycle-nlp'\r\n```\r\nAgain, that file returns no errors when I run `opsdroid config -f [PATH] lint` or `opsdroid config -f [PATH] build`.\r\n\r\nAdditionally, the python file:\r\n```python\r\nfrom opsdroid.skill import Skill\r\nfrom opsdroid.matchers import match_luisai_intent\r\n\r\nclass recycle-nlp(Skill):\r\n@match_luisai_intent('recycle')\r\n    async def recycle-nlp(self, message):\r\n        if message.luisai[\"topScoringIntent\"][\"intent\"]==\"recycle\":\r\n            await message.respond(str(message.luisai))\r\n```\r\nMy directory structure (/home for WSL, not windows):\r\n```\r\n| /home\r\n            |____ opsdroid_recycle\r\n                           |\r\n                           |_____ config.yaml\r\n                           |_____skill-recycle-nlp\r\n                                               |\r\n                                               |____ __init__.py\r\n                                                |______ README.md\r\n                                                |______ LICENSE\r\n           |\r\n           |___.local/lib/python3.6/site-packages\r\n```\r\n## Additional Details\r\nInterestingly, my bot worked fine with the example config\r\n\r\nAny help is much appreciated! \r\n\n", "before_files": [{"content": "\"\"\"Class for Filter logs and logging logic.\"\"\"\n\nimport os\nimport logging\nimport contextlib\n\nfrom logging.handlers import RotatingFileHandler\nfrom opsdroid.const import DEFAULT_LOG_FILENAME, __version__\n\n_LOGGER = logging.getLogger(__name__)\n\n\nclass ParsingFilter(logging.Filter):\n    \"\"\"Class that filters logs.\"\"\"\n\n    def __init__(self, config, *parse_list):\n        \"\"\"Create object to implement filtering.\"\"\"\n        super(ParsingFilter, self).__init__()\n        self.config = config[\"logging\"]\n        try:\n            if (\n                self.config[\"filter\"][\"whitelist\"]\n                and self.config[\"filter\"][\"blacklist\"]\n            ):\n                _LOGGER.warning(\n                    _(\n                        \"Both whitelist and blacklist filters found in configuration. \"\n                        \"Only one can be used at a time - only the whitelist filter will be used.\"\n                    )\n                )\n                self.parse_list = [\n                    logging.Filter(name) for name in parse_list[0][\"whitelist\"]\n                ]\n        except KeyError:\n            self.parse_list = parse_list[0].get(\"whitelist\") or parse_list[0].get(\n                \"blacklist\"\n            )\n\n            self.parse_list = [logging.Filter(name) for name in self.parse_list]\n\n    def filter(self, record):\n        \"\"\"Apply filter to the log message.\n\n        This is a subset of Logger.filter, this method applies the logger\n        filters and returns a bool. If the value is true the record will\n        be passed to the handlers and the log shown. If the value is\n        false it will be ignored.\n\n        Args:\n            record: a log record containing the log message and the\n                name of the log - example: opsdroid.core.\n\n        Returns:\n            Boolean: If True - pass the log to handler.\n\n        \"\"\"\n\n        if self.config[\"filter\"].get(\"whitelist\"):\n            return any(name.filter(record) for name in self.parse_list)\n        return not any(name.filter(record) for name in self.parse_list)\n\n\ndef configure_logging(config):\n    \"\"\"Configure the root logger based on user config.\"\"\"\n    rootlogger = logging.getLogger()\n    logging_config = config or {}\n\n    while rootlogger.handlers:\n        rootlogger.handlers.pop()\n\n    try:\n        if config[\"logging\"][\"path\"]:\n            logfile_path = os.path.expanduser(config[\"logging\"][\"path\"])\n        else:\n            logfile_path = config[\"logging\"][\"path\"]\n    except KeyError:\n        logfile_path = DEFAULT_LOG_FILENAME\n\n    try:\n        log_level = get_logging_level(config[\"logging\"][\"level\"])\n    except KeyError:\n        log_level = logging.INFO\n\n    rootlogger.setLevel(log_level)\n\n    try:\n        if config[\"logging\"][\"extended\"]:\n            formatter = logging.Formatter(\n                \"%(levelname)s %(name)s.%(funcName)s(): %(message)s\"\n            )\n    except KeyError:\n        formatter = logging.Formatter(\"%(levelname)s %(name)s: %(message)s\")\n\n    console_handler = logging.StreamHandler()\n    console_handler.setLevel(log_level)\n    console_handler.setFormatter(formatter)\n\n    with contextlib.suppress(KeyError):\n        console_handler.addFilter(ParsingFilter(config, config[\"logging\"][\"filter\"]))\n\n    rootlogger.addHandler(console_handler)\n\n    with contextlib.suppress(KeyError):\n        if not config[\"logging\"][\"console\"]:\n            console_handler.setLevel(logging.CRITICAL)\n\n    if logfile_path:\n        logdir = os.path.dirname(os.path.realpath(logfile_path))\n        if not os.path.isdir(logdir):\n            os.makedirs(logdir)\n\n        file_handler = RotatingFileHandler(\n            logfile_path, maxBytes=logging_config.get(\"file-size\", 50e6)\n        )\n\n        file_handler.setLevel(log_level)\n        file_handler.setFormatter(formatter)\n\n        with contextlib.suppress(KeyError):\n            file_handler.addFilter(ParsingFilter(config, config[\"logging\"][\"filter\"]))\n\n        rootlogger.addHandler(file_handler)\n    _LOGGER.info(\"=\" * 40)\n    _LOGGER.info(_(\"Started opsdroid %s.\"), __version__)\n\n\ndef get_logging_level(logging_level):\n    \"\"\"Get the logger level based on the user configuration.\n\n    Args:\n        logging_level: logging level from config file\n\n    Returns:\n        logging LEVEL ->\n            CRITICAL = 50\n            FATAL = CRITICAL\n            ERROR = 40\n            WARNING = 30\n            WARN = WARNING\n            INFO = 20\n            DEBUG = 10\n            NOTSET = 0\n\n    \"\"\"\n    if logging_level == \"critical\":\n        return logging.CRITICAL\n\n    if logging_level == \"error\":\n        return logging.ERROR\n    if logging_level == \"warning\":\n        return logging.WARNING\n\n    if logging_level == \"debug\":\n        return logging.DEBUG\n\n    return logging.INFO\n", "path": "opsdroid/logging.py"}], "after_files": [{"content": "\"\"\"Class for Filter logs and logging logic.\"\"\"\n\nimport os\nimport logging\nimport contextlib\n\nfrom logging.handlers import RotatingFileHandler\nfrom opsdroid.const import DEFAULT_LOG_FILENAME, __version__\n\n_LOGGER = logging.getLogger(__name__)\n\n\nclass ParsingFilter(logging.Filter):\n    \"\"\"Class that filters logs.\"\"\"\n\n    def __init__(self, config, *parse_list):\n        \"\"\"Create object to implement filtering.\"\"\"\n        super(ParsingFilter, self).__init__()\n        self.config = config[\"logging\"]\n        try:\n            if (\n                self.config[\"filter\"][\"whitelist\"]\n                and self.config[\"filter\"][\"blacklist\"]\n            ):\n                _LOGGER.warning(\n                    _(\n                        \"Both whitelist and blacklist filters found in configuration. \"\n                        \"Only one can be used at a time - only the whitelist filter will be used.\"\n                    )\n                )\n                self.parse_list = [\n                    logging.Filter(name) for name in parse_list[0][\"whitelist\"]\n                ]\n        except KeyError:\n            self.parse_list = parse_list[0].get(\"whitelist\") or parse_list[0].get(\n                \"blacklist\"\n            )\n\n            self.parse_list = [logging.Filter(name) for name in self.parse_list]\n\n    def filter(self, record):\n        \"\"\"Apply filter to the log message.\n\n        This is a subset of Logger.filter, this method applies the logger\n        filters and returns a bool. If the value is true the record will\n        be passed to the handlers and the log shown. If the value is\n        false it will be ignored.\n\n        Args:\n            record: a log record containing the log message and the\n                name of the log - example: opsdroid.core.\n\n        Returns:\n            Boolean: If True - pass the log to handler.\n\n        \"\"\"\n\n        if self.config[\"filter\"].get(\"whitelist\"):\n            return any(name.filter(record) for name in self.parse_list)\n        return not any(name.filter(record) for name in self.parse_list)\n\n\ndef configure_logging(config):\n    \"\"\"Configure the root logger based on user config.\"\"\"\n    rootlogger = logging.getLogger()\n    logging_config = config or {}\n\n    while rootlogger.handlers:\n        rootlogger.handlers.pop()\n\n    try:\n        if config[\"logging\"][\"path\"]:\n            logfile_path = os.path.expanduser(config[\"logging\"][\"path\"])\n        else:\n            logfile_path = config[\"logging\"][\"path\"]\n    except KeyError:\n        logfile_path = DEFAULT_LOG_FILENAME\n\n    try:\n        log_level = get_logging_level(config[\"logging\"][\"level\"])\n    except KeyError:\n        log_level = logging.INFO\n\n    rootlogger.setLevel(log_level)\n\n    formatter = logging.Formatter(\"%(levelname)s %(name)s: %(message)s\")\n\n    with contextlib.suppress(KeyError):\n        if config[\"logging\"][\"extended\"]:\n            formatter = logging.Formatter(\n                \"%(levelname)s %(name)s.%(funcName)s(): %(message)s\"\n            )\n\n    console_handler = logging.StreamHandler()\n    console_handler.setLevel(log_level)\n    console_handler.setFormatter(formatter)\n\n    with contextlib.suppress(KeyError):\n        console_handler.addFilter(ParsingFilter(config, config[\"logging\"][\"filter\"]))\n\n    rootlogger.addHandler(console_handler)\n\n    with contextlib.suppress(KeyError):\n        if not config[\"logging\"][\"console\"]:\n            console_handler.setLevel(logging.CRITICAL)\n\n    if logfile_path:\n        logdir = os.path.dirname(os.path.realpath(logfile_path))\n        if not os.path.isdir(logdir):\n            os.makedirs(logdir)\n\n        file_handler = RotatingFileHandler(\n            logfile_path, maxBytes=logging_config.get(\"file-size\", 50e6)\n        )\n\n        file_handler.setLevel(log_level)\n        file_handler.setFormatter(formatter)\n\n        with contextlib.suppress(KeyError):\n            file_handler.addFilter(ParsingFilter(config, config[\"logging\"][\"filter\"]))\n\n        rootlogger.addHandler(file_handler)\n    _LOGGER.info(\"=\" * 40)\n    _LOGGER.info(_(\"Started opsdroid %s.\"), __version__)\n\n\ndef get_logging_level(logging_level):\n    \"\"\"Get the logger level based on the user configuration.\n\n    Args:\n        logging_level: logging level from config file\n\n    Returns:\n        logging LEVEL ->\n            CRITICAL = 50\n            FATAL = CRITICAL\n            ERROR = 40\n            WARNING = 30\n            WARN = WARNING\n            INFO = 20\n            DEBUG = 10\n            NOTSET = 0\n\n    \"\"\"\n    if logging_level == \"critical\":\n        return logging.CRITICAL\n\n    if logging_level == \"error\":\n        return logging.ERROR\n    if logging_level == \"warning\":\n        return logging.WARNING\n\n    if logging_level == \"debug\":\n        return logging.DEBUG\n\n    return logging.INFO\n", "path": "opsdroid/logging.py"}]}