We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
logging crash
Just got this error for some log output, its nothing serious, but just wanted to report it.

Python 3.5.2 + Flexget 2.8.1
```
--- Logging error ---
Traceback (most recent call last):
  File "c:\users\andy\appdata\local\programs\python\python35-32\lib\logging\__init__.py", line 982, in emit
    stream.write(msg)
  File "c:\users\andy\appdata\local\programs\python\python35-32\lib\site-packages\colorclass\windows.py", line 275, in write
    print(segment, file=self._original_stream, end='')
  File "c:\users\andy\appdata\local\programs\python\python35-32\lib\encodings\cp850.py", line 19, in encode
    return codecs.charmap_encode(input,self.errors,encoding_map)[0]
UnicodeEncodeError: 'charmap' codec can't encode character '\u2022' in position 67: character maps to <undefined>
Call stack:
  File "c:\users\andy\appdata\local\programs\python\python35-32\lib\threading.py", line 882, in _bootstrap
    self._bootstrap_inner()
  File "c:\users\andy\appdata\local\programs\python\python35-32\lib\threading.py", line 914, in _bootstrap_inner
    self.run()
  File "c:\users\andy\appdata\local\programs\python\python35-32\lib\threading.py", line 862, in run
    self._target(*self._args, **self._kwargs)
  File "c:\users\andy\appdata\local\programs\python\python35-32\lib\site-packages\flexget\task_queue.py", line 48, in run
    self.current_task.execute()
  File "c:\users\andy\appdata\local\programs\python\python35-32\lib\site-packages\flexget\task.py", line 72, in wrapper
    return func(self, *args, **kw)
  File "c:\users\andy\appdata\local\programs\python\python35-32\lib\site-packages\flexget\task.py", line 646, in execute
    self._execute()
  File "c:\users\andy\appdata\local\programs\python\python35-32\lib\site-packages\flexget\task.py", line 612, in _execute
    self.__run_task_phase(phase)
  File "c:\users\andy\appdata\local\programs\python\python35-32\lib\site-packages\flexget\task.py", line 453, in __run_task_phase
    response = self.__run_plugin(plugin, phase, args)
  File "c:\users\andy\appdata\local\programs\python\python35-32\lib\site-packages\flexget\task.py", line 483, in __run_plugin
    return method(*args, **kwargs)
  File "c:\users\andy\appdata\local\programs\python\python35-32\lib\site-packages\flexget\event.py", line 23, in __call__
    return self.func(*args, **kwargs)
  File "c:\users\andy\appdata\local\programs\python\python35-32\lib\site-packages\flexget\plugins\filter\quality.py", line 37, in on_task_filter
    entry.reject('%s does not match quality requirement %s' % (entry['quality'], reqs))
  File "c:\users\andy\appdata\local\programs\python\python35-32\lib\site-packages\flexget\entry.py", line 152, in reject
    self.run_hooks('reject', reason=reason, **kwargs)
  File "c:\users\andy\appdata\local\programs\python\python35-32\lib\site-packages\flexget\entry.py", line 80, in run_hooks
    func(self, **kwargs)
  File "c:\users\andy\appdata\local\programs\python\python35-32\lib\site-packages\flexget\plugins\operate\verbose.py", line 34, in verbose_details
    task_log.verbose(msg)
  File "c:\users\andy\appdata\local\programs\python\python35-32\lib\site-packages\flexget\logger.py", line 127, in verbose
    self.log(VERBOSE, msg, *args, **kwargs)
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `flexget/logger.py`
Content:
```
1 from __future__ import unicode_literals, division, absolute_import, print_function
2 from builtins import *  # noqa pylint: disable=unused-import, redefined-builtin
3 
4 import collections
5 import contextlib
6 import logging
7 import logging.handlers
8 import sys
9 import threading
10 import uuid
11 import warnings
12 
13 from flexget import __version__
14 
15 # A level more detailed than DEBUG
16 TRACE = 5
17 # A level more detailed than INFO
18 VERBOSE = 15
19 
20 # Stores `task`, logging `session_id`, and redirected `output` stream in a thread local context
21 local_context = threading.local()
22 
23 
24 def get_level_no(level):
25     if not isinstance(level, int):
26         # Cannot use getLevelName here as in 3.4.0 it returns a string.
27         level = level.upper()
28         if level == 'TRACE':
29             level = TRACE
30         elif level == 'VERBOSE':
31             level = VERBOSE
32         else:
33             level = getattr(logging, level)
34 
35     return level
36 
37 
38 @contextlib.contextmanager
39 def task_logging(task):
40     """Context manager which adds task information to log messages."""
41     old_task = getattr(local_context, 'task', '')
42     local_context.task = task
43     try:
44         yield
45     finally:
46         local_context.task = old_task
47 
48 
49 class SessionFilter(logging.Filter):
50     def __init__(self, session_id):
51         self.session_id = session_id
52 
53     def filter(self, record):
54         return getattr(record, 'session_id', None) == self.session_id
55 
56 
57 @contextlib.contextmanager
58 def capture_output(stream, loglevel=None):
59     """Context manager which captures all log and console output to given `stream` while in scope."""
60     root_logger = logging.getLogger()
61     old_level = root_logger.getEffectiveLevel()
62     old_id = getattr(local_context, 'session_id', None)
63     # Keep using current, or create one if none already set
64     local_context.session_id = old_id or uuid.uuid4()
65     old_output = getattr(local_context, 'output', None)
66     old_loglevel = getattr(local_context, 'loglevel', None)
67     streamhandler = logging.StreamHandler(stream)
68     streamhandler.setFormatter(FlexGetFormatter())
69     streamhandler.addFilter(SessionFilter(local_context.session_id))
70     if loglevel is not None:
71         loglevel = get_level_no(loglevel)
72         streamhandler.setLevel(loglevel)
73         # If requested loglevel is lower than the root logger is filtering for, we need to turn it down.
74         # All existing handlers should have their desired level set and not be affected.
75         if not root_logger.isEnabledFor(loglevel):
76             root_logger.setLevel(loglevel)
77     local_context.output = stream
78     local_context.loglevel = loglevel
79     root_logger.addHandler(streamhandler)
80     try:
81         yield
82     finally:
83         root_logger.removeHandler(streamhandler)
84         root_logger.setLevel(old_level)
85         local_context.session_id = old_id
86         local_context.output = old_output
87         local_context.loglevel = old_loglevel
88 
89 
90 def get_capture_stream():
91     """If output is currently being redirected to a stream, returns that stream."""
92     return getattr(local_context, 'output', None)
93 
94 
95 def get_capture_loglevel():
96     """If output is currently being redirected to a stream, returns declared loglevel for that stream."""
97     return getattr(local_context, 'loglevel', None)
98 
99 
100 class RollingBuffer(collections.deque):
101     """File-like that keeps a certain number of lines of text in memory."""
102 
103     def write(self, line):
104         self.append(line)
105 
106 
107 class FlexGetLogger(logging.Logger):
108     """Custom logger that adds trace and verbose logging methods, and contextual information to log records."""
109 
110     def makeRecord(self, name, level, fn, lno, msg, args, exc_info, func, extra, *exargs):
111         extra = extra or {}
112         extra.update(
113             task=getattr(local_context, 'task', ''),
114             session_id=getattr(local_context, 'session_id', ''))
115         # Replace newlines in log messages with \n
116         if isinstance(msg, str):
117             msg = msg.replace('\n', '\\n')
118 
119         return logging.Logger.makeRecord(self, name, level, fn, lno, msg, args, exc_info, func, extra, *exargs)
120 
121     def trace(self, msg, *args, **kwargs):
122         """Log at TRACE level (more detailed than DEBUG)."""
123         self.log(TRACE, msg, *args, **kwargs)
124 
125     def verbose(self, msg, *args, **kwargs):
126         """Log at VERBOSE level (displayed when FlexGet is run interactively.)"""
127         self.log(VERBOSE, msg, *args, **kwargs)
128 
129 
130 class FlexGetFormatter(logging.Formatter):
131     """Custom formatter that can handle both regular log records and those created by FlexGetLogger"""
132     flexget_fmt = '%(asctime)-15s %(levelname)-8s %(name)-13s %(task)-15s %(message)s'
133 
134     def __init__(self):
135         logging.Formatter.__init__(self, self.flexget_fmt, '%Y-%m-%d %H:%M')
136 
137     def format(self, record):
138         if not hasattr(record, 'task'):
139             record.task = ''
140         return logging.Formatter.format(self, record)
141 
142 
143 _logging_configured = False
144 _buff_handler = None
145 _logging_started = False
146 # Stores the last 50 debug messages
147 debug_buffer = RollingBuffer(maxlen=50)
148 
149 
150 def initialize(unit_test=False):
151     """Prepare logging.
152     """
153     global _logging_configured, _logging_started, _buff_handler
154 
155     if _logging_configured:
156         return
157 
158     if 'dev' in __version__:
159         warnings.filterwarnings('always', category=DeprecationWarning, module='flexget.*')
160     warnings.simplefilter('once', append=True)
161     logging.addLevelName(TRACE, 'TRACE')
162     logging.addLevelName(VERBOSE, 'VERBOSE')
163     _logging_configured = True
164 
165     # with unit test we want pytest to add the handlers
166     if unit_test:
167         _logging_started = True
168         return
169 
170     # Store any log messages in a buffer until we `start` function is run
171     logger = logging.getLogger()
172     _buff_handler = logging.handlers.BufferingHandler(1000 * 1000)
173     logger.addHandler(_buff_handler)
174     logger.setLevel(logging.NOTSET)
175 
176     # Add a handler that sores the last 50 debug lines to `debug_buffer` for use in crash reports
177     crash_handler = logging.StreamHandler(debug_buffer)
178     crash_handler.setLevel(logging.DEBUG)
179     crash_handler.setFormatter(FlexGetFormatter())
180     logger.addHandler(crash_handler)
181 
182 
183 def start(filename=None, level=logging.INFO, to_console=True, to_file=True):
184     """After initialization, start file logging.
185     """
186     global _logging_started
187 
188     assert _logging_configured
189     if _logging_started:
190         return
191 
192     # root logger
193     logger = logging.getLogger()
194     level = get_level_no(level)
195     logger.setLevel(level)
196 
197     formatter = FlexGetFormatter()
198     if to_file:
199         file_handler = logging.handlers.RotatingFileHandler(filename, maxBytes=1000 * 1024, backupCount=9)
200         file_handler.setFormatter(formatter)
201         file_handler.setLevel(level)
202         logger.addHandler(file_handler)
203 
204     # without --cron we log to console
205     if to_console:
206         console_handler = logging.StreamHandler(sys.stdout)
207         console_handler.setFormatter(formatter)
208         console_handler.setLevel(level)
209         logger.addHandler(console_handler)
210 
211     # flush what we have stored from the plugin initialization
212     logger.removeHandler(_buff_handler)
213     if _buff_handler:
214         for record in _buff_handler.buffer:
215             if logger.isEnabledFor(record.levelno):
216                 logger.handle(record)
217         _buff_handler.flush()
218     _logging_started = True
219 
220 
221 # Set our custom logger class as default
222 logging.setLoggerClass(FlexGetLogger)
223
```
--- 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/flexget/logger.py b/flexget/logger.py
--- a/flexget/logger.py
+++ b/flexget/logger.py
@@ -1,6 +1,7 @@
 from __future__ import unicode_literals, division, absolute_import, print_function
 from builtins import *  # noqa pylint: disable=unused-import, redefined-builtin
 
+import codecs
 import collections
 import contextlib
 import logging
@@ -11,6 +12,7 @@
 import warnings
 
 from flexget import __version__
+from flexget.utils.tools import io_encoding
 
 # A level more detailed than DEBUG
 TRACE = 5
@@ -203,7 +205,13 @@
 
     # without --cron we log to console
     if to_console:
-        console_handler = logging.StreamHandler(sys.stdout)
+        # Make sure we don't send any characters that the current terminal doesn't support printing
+        stdout = sys.stdout
+        if hasattr(stdout, 'buffer'):
+            # On python 3, we need to get the buffer directly to support writing bytes
+            stdout = stdout.buffer
+        safe_stdout = codecs.getwriter(io_encoding)(stdout, 'replace')
+        console_handler = logging.StreamHandler(safe_stdout)
         console_handler.setFormatter(formatter)
         console_handler.setLevel(level)
         logger.addHandler(console_handler)

{"golden_diff": "diff --git a/flexget/logger.py b/flexget/logger.py\n--- a/flexget/logger.py\n+++ b/flexget/logger.py\n@@ -1,6 +1,7 @@\n from __future__ import unicode_literals, division, absolute_import, print_function\n from builtins import *  # noqa pylint: disable=unused-import, redefined-builtin\n \n+import codecs\n import collections\n import contextlib\n import logging\n@@ -11,6 +12,7 @@\n import warnings\n \n from flexget import __version__\n+from flexget.utils.tools import io_encoding\n \n # A level more detailed than DEBUG\n TRACE = 5\n@@ -203,7 +205,13 @@\n \n     # without --cron we log to console\n     if to_console:\n-        console_handler = logging.StreamHandler(sys.stdout)\n+        # Make sure we don't send any characters that the current terminal doesn't support printing\n+        stdout = sys.stdout\n+        if hasattr(stdout, 'buffer'):\n+            # On python 3, we need to get the buffer directly to support writing bytes\n+            stdout = stdout.buffer\n+        safe_stdout = codecs.getwriter(io_encoding)(stdout, 'replace')\n+        console_handler = logging.StreamHandler(safe_stdout)\n         console_handler.setFormatter(formatter)\n         console_handler.setLevel(level)\n         logger.addHandler(console_handler)\n", "issue": "logging crash\nJust got this error for some log output, its nothing serious, but just wanted to report it.\r\n\r\nPython 3.5.2 + Flexget 2.8.1\r\n```\r\n--- Logging error ---\r\nTraceback (most recent call last):\r\n  File \"c:\\users\\andy\\appdata\\local\\programs\\python\\python35-32\\lib\\logging\\__init__.py\", line 982, in emit\r\n    stream.write(msg)\r\n  File \"c:\\users\\andy\\appdata\\local\\programs\\python\\python35-32\\lib\\site-packages\\colorclass\\windows.py\", line 275, in write\r\n    print(segment, file=self._original_stream, end='')\r\n  File \"c:\\users\\andy\\appdata\\local\\programs\\python\\python35-32\\lib\\encodings\\cp850.py\", line 19, in encode\r\n    return codecs.charmap_encode(input,self.errors,encoding_map)[0]\r\nUnicodeEncodeError: 'charmap' codec can't encode character '\\u2022' in position 67: character maps to <undefined>\r\nCall stack:\r\n  File \"c:\\users\\andy\\appdata\\local\\programs\\python\\python35-32\\lib\\threading.py\", line 882, in _bootstrap\r\n    self._bootstrap_inner()\r\n  File \"c:\\users\\andy\\appdata\\local\\programs\\python\\python35-32\\lib\\threading.py\", line 914, in _bootstrap_inner\r\n    self.run()\r\n  File \"c:\\users\\andy\\appdata\\local\\programs\\python\\python35-32\\lib\\threading.py\", line 862, in run\r\n    self._target(*self._args, **self._kwargs)\r\n  File \"c:\\users\\andy\\appdata\\local\\programs\\python\\python35-32\\lib\\site-packages\\flexget\\task_queue.py\", line 48, in run\r\n    self.current_task.execute()\r\n  File \"c:\\users\\andy\\appdata\\local\\programs\\python\\python35-32\\lib\\site-packages\\flexget\\task.py\", line 72, in wrapper\r\n    return func(self, *args, **kw)\r\n  File \"c:\\users\\andy\\appdata\\local\\programs\\python\\python35-32\\lib\\site-packages\\flexget\\task.py\", line 646, in execute\r\n    self._execute()\r\n  File \"c:\\users\\andy\\appdata\\local\\programs\\python\\python35-32\\lib\\site-packages\\flexget\\task.py\", line 612, in _execute\r\n    self.__run_task_phase(phase)\r\n  File \"c:\\users\\andy\\appdata\\local\\programs\\python\\python35-32\\lib\\site-packages\\flexget\\task.py\", line 453, in __run_task_phase\r\n    response = self.__run_plugin(plugin, phase, args)\r\n  File \"c:\\users\\andy\\appdata\\local\\programs\\python\\python35-32\\lib\\site-packages\\flexget\\task.py\", line 483, in __run_plugin\r\n    return method(*args, **kwargs)\r\n  File \"c:\\users\\andy\\appdata\\local\\programs\\python\\python35-32\\lib\\site-packages\\flexget\\event.py\", line 23, in __call__\r\n    return self.func(*args, **kwargs)\r\n  File \"c:\\users\\andy\\appdata\\local\\programs\\python\\python35-32\\lib\\site-packages\\flexget\\plugins\\filter\\quality.py\", line 37, in on_task_filter\r\n    entry.reject('%s does not match quality requirement %s' % (entry['quality'], reqs))\r\n  File \"c:\\users\\andy\\appdata\\local\\programs\\python\\python35-32\\lib\\site-packages\\flexget\\entry.py\", line 152, in reject\r\n    self.run_hooks('reject', reason=reason, **kwargs)\r\n  File \"c:\\users\\andy\\appdata\\local\\programs\\python\\python35-32\\lib\\site-packages\\flexget\\entry.py\", line 80, in run_hooks\r\n    func(self, **kwargs)\r\n  File \"c:\\users\\andy\\appdata\\local\\programs\\python\\python35-32\\lib\\site-packages\\flexget\\plugins\\operate\\verbose.py\", line 34, in verbose_details\r\n    task_log.verbose(msg)\r\n  File \"c:\\users\\andy\\appdata\\local\\programs\\python\\python35-32\\lib\\site-packages\\flexget\\logger.py\", line 127, in verbose\r\n    self.log(VERBOSE, msg, *args, **kwargs)\r\n```\n", "before_files": [{"content": "from __future__ import unicode_literals, division, absolute_import, print_function\nfrom builtins import *  # noqa pylint: disable=unused-import, redefined-builtin\n\nimport collections\nimport contextlib\nimport logging\nimport logging.handlers\nimport sys\nimport threading\nimport uuid\nimport warnings\n\nfrom flexget import __version__\n\n# A level more detailed than DEBUG\nTRACE = 5\n# A level more detailed than INFO\nVERBOSE = 15\n\n# Stores `task`, logging `session_id`, and redirected `output` stream in a thread local context\nlocal_context = threading.local()\n\n\ndef get_level_no(level):\n    if not isinstance(level, int):\n        # Cannot use getLevelName here as in 3.4.0 it returns a string.\n        level = level.upper()\n        if level == 'TRACE':\n            level = TRACE\n        elif level == 'VERBOSE':\n            level = VERBOSE\n        else:\n            level = getattr(logging, level)\n\n    return level\n\n\n@contextlib.contextmanager\ndef task_logging(task):\n    \"\"\"Context manager which adds task information to log messages.\"\"\"\n    old_task = getattr(local_context, 'task', '')\n    local_context.task = task\n    try:\n        yield\n    finally:\n        local_context.task = old_task\n\n\nclass SessionFilter(logging.Filter):\n    def __init__(self, session_id):\n        self.session_id = session_id\n\n    def filter(self, record):\n        return getattr(record, 'session_id', None) == self.session_id\n\n\n@contextlib.contextmanager\ndef capture_output(stream, loglevel=None):\n    \"\"\"Context manager which captures all log and console output to given `stream` while in scope.\"\"\"\n    root_logger = logging.getLogger()\n    old_level = root_logger.getEffectiveLevel()\n    old_id = getattr(local_context, 'session_id', None)\n    # Keep using current, or create one if none already set\n    local_context.session_id = old_id or uuid.uuid4()\n    old_output = getattr(local_context, 'output', None)\n    old_loglevel = getattr(local_context, 'loglevel', None)\n    streamhandler = logging.StreamHandler(stream)\n    streamhandler.setFormatter(FlexGetFormatter())\n    streamhandler.addFilter(SessionFilter(local_context.session_id))\n    if loglevel is not None:\n        loglevel = get_level_no(loglevel)\n        streamhandler.setLevel(loglevel)\n        # If requested loglevel is lower than the root logger is filtering for, we need to turn it down.\n        # All existing handlers should have their desired level set and not be affected.\n        if not root_logger.isEnabledFor(loglevel):\n            root_logger.setLevel(loglevel)\n    local_context.output = stream\n    local_context.loglevel = loglevel\n    root_logger.addHandler(streamhandler)\n    try:\n        yield\n    finally:\n        root_logger.removeHandler(streamhandler)\n        root_logger.setLevel(old_level)\n        local_context.session_id = old_id\n        local_context.output = old_output\n        local_context.loglevel = old_loglevel\n\n\ndef get_capture_stream():\n    \"\"\"If output is currently being redirected to a stream, returns that stream.\"\"\"\n    return getattr(local_context, 'output', None)\n\n\ndef get_capture_loglevel():\n    \"\"\"If output is currently being redirected to a stream, returns declared loglevel for that stream.\"\"\"\n    return getattr(local_context, 'loglevel', None)\n\n\nclass RollingBuffer(collections.deque):\n    \"\"\"File-like that keeps a certain number of lines of text in memory.\"\"\"\n\n    def write(self, line):\n        self.append(line)\n\n\nclass FlexGetLogger(logging.Logger):\n    \"\"\"Custom logger that adds trace and verbose logging methods, and contextual information to log records.\"\"\"\n\n    def makeRecord(self, name, level, fn, lno, msg, args, exc_info, func, extra, *exargs):\n        extra = extra or {}\n        extra.update(\n            task=getattr(local_context, 'task', ''),\n            session_id=getattr(local_context, 'session_id', ''))\n        # Replace newlines in log messages with \\n\n        if isinstance(msg, str):\n            msg = msg.replace('\\n', '\\\\n')\n\n        return logging.Logger.makeRecord(self, name, level, fn, lno, msg, args, exc_info, func, extra, *exargs)\n\n    def trace(self, msg, *args, **kwargs):\n        \"\"\"Log at TRACE level (more detailed than DEBUG).\"\"\"\n        self.log(TRACE, msg, *args, **kwargs)\n\n    def verbose(self, msg, *args, **kwargs):\n        \"\"\"Log at VERBOSE level (displayed when FlexGet is run interactively.)\"\"\"\n        self.log(VERBOSE, msg, *args, **kwargs)\n\n\nclass FlexGetFormatter(logging.Formatter):\n    \"\"\"Custom formatter that can handle both regular log records and those created by FlexGetLogger\"\"\"\n    flexget_fmt = '%(asctime)-15s %(levelname)-8s %(name)-13s %(task)-15s %(message)s'\n\n    def __init__(self):\n        logging.Formatter.__init__(self, self.flexget_fmt, '%Y-%m-%d %H:%M')\n\n    def format(self, record):\n        if not hasattr(record, 'task'):\n            record.task = ''\n        return logging.Formatter.format(self, record)\n\n\n_logging_configured = False\n_buff_handler = None\n_logging_started = False\n# Stores the last 50 debug messages\ndebug_buffer = RollingBuffer(maxlen=50)\n\n\ndef initialize(unit_test=False):\n    \"\"\"Prepare logging.\n    \"\"\"\n    global _logging_configured, _logging_started, _buff_handler\n\n    if _logging_configured:\n        return\n\n    if 'dev' in __version__:\n        warnings.filterwarnings('always', category=DeprecationWarning, module='flexget.*')\n    warnings.simplefilter('once', append=True)\n    logging.addLevelName(TRACE, 'TRACE')\n    logging.addLevelName(VERBOSE, 'VERBOSE')\n    _logging_configured = True\n\n    # with unit test we want pytest to add the handlers\n    if unit_test:\n        _logging_started = True\n        return\n\n    # Store any log messages in a buffer until we `start` function is run\n    logger = logging.getLogger()\n    _buff_handler = logging.handlers.BufferingHandler(1000 * 1000)\n    logger.addHandler(_buff_handler)\n    logger.setLevel(logging.NOTSET)\n\n    # Add a handler that sores the last 50 debug lines to `debug_buffer` for use in crash reports\n    crash_handler = logging.StreamHandler(debug_buffer)\n    crash_handler.setLevel(logging.DEBUG)\n    crash_handler.setFormatter(FlexGetFormatter())\n    logger.addHandler(crash_handler)\n\n\ndef start(filename=None, level=logging.INFO, to_console=True, to_file=True):\n    \"\"\"After initialization, start file logging.\n    \"\"\"\n    global _logging_started\n\n    assert _logging_configured\n    if _logging_started:\n        return\n\n    # root logger\n    logger = logging.getLogger()\n    level = get_level_no(level)\n    logger.setLevel(level)\n\n    formatter = FlexGetFormatter()\n    if to_file:\n        file_handler = logging.handlers.RotatingFileHandler(filename, maxBytes=1000 * 1024, backupCount=9)\n        file_handler.setFormatter(formatter)\n        file_handler.setLevel(level)\n        logger.addHandler(file_handler)\n\n    # without --cron we log to console\n    if to_console:\n        console_handler = logging.StreamHandler(sys.stdout)\n        console_handler.setFormatter(formatter)\n        console_handler.setLevel(level)\n        logger.addHandler(console_handler)\n\n    # flush what we have stored from the plugin initialization\n    logger.removeHandler(_buff_handler)\n    if _buff_handler:\n        for record in _buff_handler.buffer:\n            if logger.isEnabledFor(record.levelno):\n                logger.handle(record)\n        _buff_handler.flush()\n    _logging_started = True\n\n\n# Set our custom logger class as default\nlogging.setLoggerClass(FlexGetLogger)\n", "path": "flexget/logger.py"}], "after_files": [{"content": "from __future__ import unicode_literals, division, absolute_import, print_function\nfrom builtins import *  # noqa pylint: disable=unused-import, redefined-builtin\n\nimport codecs\nimport collections\nimport contextlib\nimport logging\nimport logging.handlers\nimport sys\nimport threading\nimport uuid\nimport warnings\n\nfrom flexget import __version__\nfrom flexget.utils.tools import io_encoding\n\n# A level more detailed than DEBUG\nTRACE = 5\n# A level more detailed than INFO\nVERBOSE = 15\n\n# Stores `task`, logging `session_id`, and redirected `output` stream in a thread local context\nlocal_context = threading.local()\n\n\ndef get_level_no(level):\n    if not isinstance(level, int):\n        # Cannot use getLevelName here as in 3.4.0 it returns a string.\n        level = level.upper()\n        if level == 'TRACE':\n            level = TRACE\n        elif level == 'VERBOSE':\n            level = VERBOSE\n        else:\n            level = getattr(logging, level)\n\n    return level\n\n\n@contextlib.contextmanager\ndef task_logging(task):\n    \"\"\"Context manager which adds task information to log messages.\"\"\"\n    old_task = getattr(local_context, 'task', '')\n    local_context.task = task\n    try:\n        yield\n    finally:\n        local_context.task = old_task\n\n\nclass SessionFilter(logging.Filter):\n    def __init__(self, session_id):\n        self.session_id = session_id\n\n    def filter(self, record):\n        return getattr(record, 'session_id', None) == self.session_id\n\n\n@contextlib.contextmanager\ndef capture_output(stream, loglevel=None):\n    \"\"\"Context manager which captures all log and console output to given `stream` while in scope.\"\"\"\n    root_logger = logging.getLogger()\n    old_level = root_logger.getEffectiveLevel()\n    old_id = getattr(local_context, 'session_id', None)\n    # Keep using current, or create one if none already set\n    local_context.session_id = old_id or uuid.uuid4()\n    old_output = getattr(local_context, 'output', None)\n    old_loglevel = getattr(local_context, 'loglevel', None)\n    streamhandler = logging.StreamHandler(stream)\n    streamhandler.setFormatter(FlexGetFormatter())\n    streamhandler.addFilter(SessionFilter(local_context.session_id))\n    if loglevel is not None:\n        loglevel = get_level_no(loglevel)\n        streamhandler.setLevel(loglevel)\n        # If requested loglevel is lower than the root logger is filtering for, we need to turn it down.\n        # All existing handlers should have their desired level set and not be affected.\n        if not root_logger.isEnabledFor(loglevel):\n            root_logger.setLevel(loglevel)\n    local_context.output = stream\n    local_context.loglevel = loglevel\n    root_logger.addHandler(streamhandler)\n    try:\n        yield\n    finally:\n        root_logger.removeHandler(streamhandler)\n        root_logger.setLevel(old_level)\n        local_context.session_id = old_id\n        local_context.output = old_output\n        local_context.loglevel = old_loglevel\n\n\ndef get_capture_stream():\n    \"\"\"If output is currently being redirected to a stream, returns that stream.\"\"\"\n    return getattr(local_context, 'output', None)\n\n\ndef get_capture_loglevel():\n    \"\"\"If output is currently being redirected to a stream, returns declared loglevel for that stream.\"\"\"\n    return getattr(local_context, 'loglevel', None)\n\n\nclass RollingBuffer(collections.deque):\n    \"\"\"File-like that keeps a certain number of lines of text in memory.\"\"\"\n\n    def write(self, line):\n        self.append(line)\n\n\nclass FlexGetLogger(logging.Logger):\n    \"\"\"Custom logger that adds trace and verbose logging methods, and contextual information to log records.\"\"\"\n\n    def makeRecord(self, name, level, fn, lno, msg, args, exc_info, func, extra, *exargs):\n        extra = extra or {}\n        extra.update(\n            task=getattr(local_context, 'task', ''),\n            session_id=getattr(local_context, 'session_id', ''))\n        # Replace newlines in log messages with \\n\n        if isinstance(msg, str):\n            msg = msg.replace('\\n', '\\\\n')\n\n        return logging.Logger.makeRecord(self, name, level, fn, lno, msg, args, exc_info, func, extra, *exargs)\n\n    def trace(self, msg, *args, **kwargs):\n        \"\"\"Log at TRACE level (more detailed than DEBUG).\"\"\"\n        self.log(TRACE, msg, *args, **kwargs)\n\n    def verbose(self, msg, *args, **kwargs):\n        \"\"\"Log at VERBOSE level (displayed when FlexGet is run interactively.)\"\"\"\n        self.log(VERBOSE, msg, *args, **kwargs)\n\n\nclass FlexGetFormatter(logging.Formatter):\n    \"\"\"Custom formatter that can handle both regular log records and those created by FlexGetLogger\"\"\"\n    flexget_fmt = '%(asctime)-15s %(levelname)-8s %(name)-13s %(task)-15s %(message)s'\n\n    def __init__(self):\n        logging.Formatter.__init__(self, self.flexget_fmt, '%Y-%m-%d %H:%M')\n\n    def format(self, record):\n        if not hasattr(record, 'task'):\n            record.task = ''\n        return logging.Formatter.format(self, record)\n\n\n_logging_configured = False\n_buff_handler = None\n_logging_started = False\n# Stores the last 50 debug messages\ndebug_buffer = RollingBuffer(maxlen=50)\n\n\ndef initialize(unit_test=False):\n    \"\"\"Prepare logging.\n    \"\"\"\n    global _logging_configured, _logging_started, _buff_handler\n\n    if _logging_configured:\n        return\n\n    if 'dev' in __version__:\n        warnings.filterwarnings('always', category=DeprecationWarning, module='flexget.*')\n    warnings.simplefilter('once', append=True)\n    logging.addLevelName(TRACE, 'TRACE')\n    logging.addLevelName(VERBOSE, 'VERBOSE')\n    _logging_configured = True\n\n    # with unit test we want pytest to add the handlers\n    if unit_test:\n        _logging_started = True\n        return\n\n    # Store any log messages in a buffer until we `start` function is run\n    logger = logging.getLogger()\n    _buff_handler = logging.handlers.BufferingHandler(1000 * 1000)\n    logger.addHandler(_buff_handler)\n    logger.setLevel(logging.NOTSET)\n\n    # Add a handler that sores the last 50 debug lines to `debug_buffer` for use in crash reports\n    crash_handler = logging.StreamHandler(debug_buffer)\n    crash_handler.setLevel(logging.DEBUG)\n    crash_handler.setFormatter(FlexGetFormatter())\n    logger.addHandler(crash_handler)\n\n\ndef start(filename=None, level=logging.INFO, to_console=True, to_file=True):\n    \"\"\"After initialization, start file logging.\n    \"\"\"\n    global _logging_started\n\n    assert _logging_configured\n    if _logging_started:\n        return\n\n    # root logger\n    logger = logging.getLogger()\n    level = get_level_no(level)\n    logger.setLevel(level)\n\n    formatter = FlexGetFormatter()\n    if to_file:\n        file_handler = logging.handlers.RotatingFileHandler(filename, maxBytes=1000 * 1024, backupCount=9)\n        file_handler.setFormatter(formatter)\n        file_handler.setLevel(level)\n        logger.addHandler(file_handler)\n\n    # without --cron we log to console\n    if to_console:\n        # Make sure we don't send any characters that the current terminal doesn't support printing\n        stdout = sys.stdout\n        if hasattr(stdout, 'buffer'):\n            # On python 3, we need to get the buffer directly to support writing bytes\n            stdout = stdout.buffer\n        safe_stdout = codecs.getwriter(io_encoding)(stdout, 'replace')\n        console_handler = logging.StreamHandler(safe_stdout)\n        console_handler.setFormatter(formatter)\n        console_handler.setLevel(level)\n        logger.addHandler(console_handler)\n\n    # flush what we have stored from the plugin initialization\n    logger.removeHandler(_buff_handler)\n    if _buff_handler:\n        for record in _buff_handler.buffer:\n            if logger.isEnabledFor(record.levelno):\n                logger.handle(record)\n        _buff_handler.flush()\n    _logging_started = True\n\n\n# Set our custom logger class as default\nlogging.setLoggerClass(FlexGetLogger)\n", "path": "flexget/logger.py"}]}