We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Logger ignores custom log_dir
The new value is set but logs are created in the default log directory, tested on Windows.

```
import os
from kivy.config import Config
Config.set('kivy', 'log_dir', os.path.dirname(os.path.abspath(__file__)))
print(Config.get('kivy', 'log_dir'))

from kivy.app import App
from kivy.core.window import Window

if __name__ == '__main__':
    App().run()
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `kivy/logger.py`
Content:
```
1 '''
2 Logger object
3 =============
4 
5 Differents logging levels are available : trace, debug, info, warning, error
6 and critical.
7 
8 Examples of usage::
9 
10     from kivy.logger import Logger
11 
12     Logger.info('title: This is a info message.')
13     Logger.debug('title: This is a debug message.')
14 
15     try:
16         raise Exception('bleh')
17     except Exception:
18         Logger.exception('Something happened!')
19 
20 The message passed to the logger is split into two parts, separated by a colon
21 (:). The first part is used as a title, and the second part is used as the
22 message. This way, you can "categorize" your message easily.::
23 
24     Logger.info('Application: This is a test')
25 
26     # will appear as
27 
28     [INFO   ] [Application ] This is a test
29 
30 Logger configuration
31 --------------------
32 
33 The Logger can be controlled via the Kivy configuration file::
34 
35     [kivy]
36     log_level = info
37     log_enable = 1
38     log_dir = logs
39     log_name = kivy_%y-%m-%d_%_.txt
40 
41 More information about the allowed values are described in the
42 :mod:`kivy.config` module.
43 
44 Logger history
45 --------------
46 
47 Even if the logger is not enabled, you still have access to the last 100
48 messages::
49 
50     from kivy.logger import LoggerHistory
51 
52     print(LoggerHistory.history)
53 
54 '''
55 
56 import logging
57 import os
58 import sys
59 import kivy
60 from kivy.compat import PY2
61 from random import randint
62 from functools import partial
63 
64 __all__ = ('Logger', 'LOG_LEVELS', 'COLORS', 'LoggerHistory')
65 
66 Logger = None
67 
68 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = list(range(8))
69 
70 #These are the sequences need to get colored ouput
71 RESET_SEQ = "\033[0m"
72 COLOR_SEQ = "\033[1;%dm"
73 BOLD_SEQ = "\033[1m"
74 
75 previous_stderr = sys.stderr
76 
77 
78 def formatter_message(message, use_color=True):
79     if use_color:
80         message = message.replace("$RESET", RESET_SEQ)
81         message = message.replace("$BOLD", BOLD_SEQ)
82     else:
83         message = message.replace("$RESET", "").replace("$BOLD", "")
84     return message
85 
86 COLORS = {
87     'TRACE': MAGENTA,
88     'WARNING': YELLOW,
89     'INFO': GREEN,
90     'DEBUG': CYAN,
91     'CRITICAL': RED,
92     'ERROR': RED}
93 
94 logging.TRACE = 9
95 LOG_LEVELS = {
96     'trace': logging.TRACE,
97     'debug': logging.DEBUG,
98     'info': logging.INFO,
99     'warning': logging.WARNING,
100     'error': logging.ERROR,
101     'critical': logging.CRITICAL}
102 
103 
104 class FileHandler(logging.Handler):
105     history = []
106     filename = 'log.txt'
107     fd = None
108 
109     def purge_logs(self, directory):
110         '''Purge log is called randomly to prevent the log directory from being
111         filled by lots and lots of log files.
112         You've a chance of 1 in 20 that purge log will be fired.
113         '''
114         if randint(0, 20) != 0:
115             return
116 
117         # Use config ?
118         maxfiles = 100
119 
120         print('Purge log fired. Analysing...')
121         join = os.path.join
122         unlink = os.unlink
123 
124         # search all log files
125         l = [join(directory, x) for x in os.listdir(directory)]
126         if len(l) > maxfiles:
127             # get creation time on every files
128             l = [{'fn': x, 'ctime': os.path.getctime(x)} for x in l]
129 
130             # sort by date
131             l = sorted(l, key=lambda x: x['ctime'])
132 
133             # get the oldest (keep last maxfiles)
134             l = l[:-maxfiles]
135             print('Purge %d log files' % len(l))
136 
137             # now, unlink every files in the list
138             for filename in l:
139                 unlink(filename['fn'])
140 
141         print('Purge finished!')
142 
143     def _configure(self):
144         from time import strftime
145         from kivy.config import Config
146         log_dir = Config.get('kivy', 'log_dir')
147         log_name = Config.get('kivy', 'log_name')
148 
149         _dir = kivy.kivy_home_dir
150         if log_dir and os.path.isabs(log_dir):
151             _dir = log_dir
152         else:
153             _dir = os.path.join(_dir, log_dir)
154         if not os.path.exists(_dir):
155             os.makedirs(_dir)
156 
157         self.purge_logs(_dir)
158 
159         pattern = log_name.replace('%_', '@@NUMBER@@')
160         pattern = os.path.join(_dir, strftime(pattern))
161         n = 0
162         while True:
163             filename = pattern.replace('@@NUMBER@@', str(n))
164             if not os.path.exists(filename):
165                 break
166             n += 1
167             if n > 10000:  # prevent maybe flooding ?
168                 raise Exception('Too many logfile, remove them')
169 
170         FileHandler.filename = filename
171         FileHandler.fd = open(filename, 'w')
172 
173         Logger.info('Logger: Record log in %s' % filename)
174 
175     def _write_message(self, record):
176         if FileHandler.fd in (None, False):
177             return
178 
179         FileHandler.fd.write('[%-18s] ' % record.levelname)
180         try:
181             FileHandler.fd.write(record.msg)
182         except UnicodeEncodeError:
183             if PY2:
184                 FileHandler.fd.write(record.msg.encode('utf8'))
185         FileHandler.fd.write('\n')
186         FileHandler.fd.flush()
187 
188     def emit(self, message):
189         # during the startup, store the message in the history
190         if Logger.logfile_activated is None:
191             FileHandler.history += [message]
192             return
193 
194         # startup done, if the logfile is not activated, avoid history.
195         if Logger.logfile_activated is False:
196             FileHandler.history = []
197             return
198 
199         if FileHandler.fd is None:
200             try:
201                 self._configure()
202             except Exception:
203                 # deactivate filehandler...
204                 FileHandler.fd = False
205                 Logger.exception('Error while activating FileHandler logger')
206                 return
207             while FileHandler.history:
208                 _message = FileHandler.history.pop()
209                 self._write_message(_message)
210 
211         self._write_message(message)
212 
213 
214 class LoggerHistory(logging.Handler):
215 
216     history = []
217 
218     def emit(self, message):
219         LoggerHistory.history = [message] + LoggerHistory.history[:100]
220 
221 
222 class ColoredFormatter(logging.Formatter):
223 
224     def __init__(self, msg, use_color=True):
225         logging.Formatter.__init__(self, msg)
226         self.use_color = use_color
227 
228     def format(self, record):
229         try:
230             msg = record.msg.split(':', 1)
231             if len(msg) == 2:
232                 record.msg = '[%-12s]%s' % (msg[0], msg[1])
233         except:
234             pass
235         levelname = record.levelname
236         if record.levelno == logging.TRACE:
237             levelname = 'TRACE'
238             record.levelname = levelname
239         if self.use_color and levelname in COLORS:
240             levelname_color = (
241                 COLOR_SEQ % (30 + COLORS[levelname]) + levelname + RESET_SEQ)
242             record.levelname = levelname_color
243         return logging.Formatter.format(self, record)
244 
245 
246 class ConsoleHandler(logging.StreamHandler):
247 
248     def filter(self, record):
249         try:
250             msg = record.msg
251             k = msg.split(':', 1)
252             if k[0] == 'stderr' and len(k) == 2:
253                 previous_stderr.write(k[1] + '\n')
254                 return False
255         except:
256             pass
257         return True
258 
259 
260 class LogFile(object):
261 
262     def __init__(self, channel, func):
263         self.buffer = ''
264         self.func = func
265         self.channel = channel
266         self.errors = ''
267 
268     def write(self, s):
269         s = self.buffer + s
270         self.flush()
271         f = self.func
272         channel = self.channel
273         lines = s.split('\n')
274         for l in lines[:-1]:
275             f('%s: %s' % (channel, l))
276         self.buffer = lines[-1]
277 
278     def flush(self):
279         return
280 
281 
282 def logger_config_update(section, key, value):
283     if LOG_LEVELS.get(value) is None:
284         raise AttributeError('Loglevel {0!r} doesn\'t exists'.format(value))
285     Logger.setLevel(level=LOG_LEVELS.get(value))
286 
287 #: Kivy default logger instance
288 Logger = logging.getLogger('kivy')
289 Logger.logfile_activated = None
290 Logger.trace = partial(Logger.log, logging.TRACE)
291 
292 # set the Kivy logger as the default
293 logging.root = Logger
294 
295 # add default kivy logger
296 Logger.addHandler(LoggerHistory())
297 if 'KIVY_NO_FILELOG' not in os.environ:
298     Logger.addHandler(FileHandler())
299 
300 # Use the custom handler instead of streaming one.
301 if 'KIVY_NO_CONSOLELOG' not in os.environ:
302     if hasattr(sys, '_kivy_logging_handler'):
303         Logger.addHandler(getattr(sys, '_kivy_logging_handler'))
304     else:
305         use_color = (
306             os.name != 'nt' and
307             os.environ.get('KIVY_BUILD') not in ('android', 'ios') and
308             os.environ.get('TERM') in (
309                 'xterm', 'rxvt', 'rxvt-unicode', 'xterm-256color'))
310         color_fmt = formatter_message(
311             '[%(levelname)-18s] %(message)s', use_color)
312         formatter = ColoredFormatter(color_fmt, use_color=use_color)
313         console = ConsoleHandler()
314         console.setFormatter(formatter)
315         Logger.addHandler(console)
316 
317 # install stderr handlers
318 sys.stderr = LogFile('stderr', Logger.warning)
319 
320 #: Kivy history handler
321 LoggerHistory = LoggerHistory
322
```
--- 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/kivy/logger.py b/kivy/logger.py
--- a/kivy/logger.py
+++ b/kivy/logger.py
@@ -140,7 +140,7 @@
 
         print('Purge finished !')
 
-    def _configure(self):
+    def _configure(self, *largs, **kwargs):
         from time import strftime
         from kivy.config import Config
         log_dir = Config.get('kivy', 'log_dir')
@@ -167,7 +167,11 @@
             if n > 10000:  # prevent maybe flooding ?
                 raise Exception('Too many logfile, remove them')
 
+        if FileHandler.filename == filename and FileHandler.fd is not None:
+            return
         FileHandler.filename = filename
+        if FileHandler.fd is not None:
+            FileHandler.fd.close()
         FileHandler.fd = open(filename, 'w')
 
         Logger.info('Logger: Record log in %s' % filename)
@@ -199,6 +203,9 @@
         if FileHandler.fd is None:
             try:
                 self._configure()
+                from kivy.config import Config
+                Config.add_callback(self._configure, 'kivy', 'log_dir')
+                Config.add_callback(self._configure, 'kivy', 'log_name')
             except Exception:
                 # deactivate filehandler...
                 FileHandler.fd = False

{"golden_diff": "diff --git a/kivy/logger.py b/kivy/logger.py\n--- a/kivy/logger.py\n+++ b/kivy/logger.py\n@@ -140,7 +140,7 @@\n \n         print('Purge finished !')\n \n-    def _configure(self):\n+    def _configure(self, *largs, **kwargs):\n         from time import strftime\n         from kivy.config import Config\n         log_dir = Config.get('kivy', 'log_dir')\n@@ -167,7 +167,11 @@\n             if n > 10000:  # prevent maybe flooding ?\n                 raise Exception('Too many logfile, remove them')\n \n+        if FileHandler.filename == filename and FileHandler.fd is not None:\n+            return\n         FileHandler.filename = filename\n+        if FileHandler.fd is not None:\n+            FileHandler.fd.close()\n         FileHandler.fd = open(filename, 'w')\n \n         Logger.info('Logger: Record log in %s' % filename)\n@@ -199,6 +203,9 @@\n         if FileHandler.fd is None:\n             try:\n                 self._configure()\n+                from kivy.config import Config\n+                Config.add_callback(self._configure, 'kivy', 'log_dir')\n+                Config.add_callback(self._configure, 'kivy', 'log_name')\n             except Exception:\n                 # deactivate filehandler...\n                 FileHandler.fd = False\n", "issue": "Logger ignores custom log_dir\nThe new value is set but logs are created in the default log directory, tested on Windows.\n\n```\nimport os\nfrom kivy.config import Config\nConfig.set('kivy', 'log_dir', os.path.dirname(os.path.abspath(__file__)))\nprint(Config.get('kivy', 'log_dir'))\n\nfrom kivy.app import App\nfrom kivy.core.window import Window\n\nif __name__ == '__main__':\n    App().run()\n```\n\n", "before_files": [{"content": "'''\nLogger object\n=============\n\nDifferents logging levels are available : trace, debug, info, warning, error\nand critical.\n\nExamples of usage::\n\n    from kivy.logger import Logger\n\n    Logger.info('title: This is a info message.')\n    Logger.debug('title: This is a debug message.')\n\n    try:\n        raise Exception('bleh')\n    except Exception:\n        Logger.exception('Something happened!')\n\nThe message passed to the logger is split into two parts, separated by a colon\n(:). The first part is used as a title, and the second part is used as the\nmessage. This way, you can \"categorize\" your message easily.::\n\n    Logger.info('Application: This is a test')\n\n    # will appear as\n\n    [INFO   ] [Application ] This is a test\n\nLogger configuration\n--------------------\n\nThe Logger can be controlled via the Kivy configuration file::\n\n    [kivy]\n    log_level = info\n    log_enable = 1\n    log_dir = logs\n    log_name = kivy_%y-%m-%d_%_.txt\n\nMore information about the allowed values are described in the\n:mod:`kivy.config` module.\n\nLogger history\n--------------\n\nEven if the logger is not enabled, you still have access to the last 100\nmessages::\n\n    from kivy.logger import LoggerHistory\n\n    print(LoggerHistory.history)\n\n'''\n\nimport logging\nimport os\nimport sys\nimport kivy\nfrom kivy.compat import PY2\nfrom random import randint\nfrom functools import partial\n\n__all__ = ('Logger', 'LOG_LEVELS', 'COLORS', 'LoggerHistory')\n\nLogger = None\n\nBLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = list(range(8))\n\n#These are the sequences need to get colored ouput\nRESET_SEQ = \"\\033[0m\"\nCOLOR_SEQ = \"\\033[1;%dm\"\nBOLD_SEQ = \"\\033[1m\"\n\nprevious_stderr = sys.stderr\n\n\ndef formatter_message(message, use_color=True):\n    if use_color:\n        message = message.replace(\"$RESET\", RESET_SEQ)\n        message = message.replace(\"$BOLD\", BOLD_SEQ)\n    else:\n        message = message.replace(\"$RESET\", \"\").replace(\"$BOLD\", \"\")\n    return message\n\nCOLORS = {\n    'TRACE': MAGENTA,\n    'WARNING': YELLOW,\n    'INFO': GREEN,\n    'DEBUG': CYAN,\n    'CRITICAL': RED,\n    'ERROR': RED}\n\nlogging.TRACE = 9\nLOG_LEVELS = {\n    'trace': logging.TRACE,\n    'debug': logging.DEBUG,\n    'info': logging.INFO,\n    'warning': logging.WARNING,\n    'error': logging.ERROR,\n    'critical': logging.CRITICAL}\n\n\nclass FileHandler(logging.Handler):\n    history = []\n    filename = 'log.txt'\n    fd = None\n\n    def purge_logs(self, directory):\n        '''Purge log is called randomly to prevent the log directory from being\n        filled by lots and lots of log files.\n        You've a chance of 1 in 20 that purge log will be fired.\n        '''\n        if randint(0, 20) != 0:\n            return\n\n        # Use config ?\n        maxfiles = 100\n\n        print('Purge log fired. Analysing...')\n        join = os.path.join\n        unlink = os.unlink\n\n        # search all log files\n        l = [join(directory, x) for x in os.listdir(directory)]\n        if len(l) > maxfiles:\n            # get creation time on every files\n            l = [{'fn': x, 'ctime': os.path.getctime(x)} for x in l]\n\n            # sort by date\n            l = sorted(l, key=lambda x: x['ctime'])\n\n            # get the oldest (keep last maxfiles)\n            l = l[:-maxfiles]\n            print('Purge %d log files' % len(l))\n\n            # now, unlink every files in the list\n            for filename in l:\n                unlink(filename['fn'])\n\n        print('Purge finished!')\n\n    def _configure(self):\n        from time import strftime\n        from kivy.config import Config\n        log_dir = Config.get('kivy', 'log_dir')\n        log_name = Config.get('kivy', 'log_name')\n\n        _dir = kivy.kivy_home_dir\n        if log_dir and os.path.isabs(log_dir):\n            _dir = log_dir\n        else:\n            _dir = os.path.join(_dir, log_dir)\n        if not os.path.exists(_dir):\n            os.makedirs(_dir)\n\n        self.purge_logs(_dir)\n\n        pattern = log_name.replace('%_', '@@NUMBER@@')\n        pattern = os.path.join(_dir, strftime(pattern))\n        n = 0\n        while True:\n            filename = pattern.replace('@@NUMBER@@', str(n))\n            if not os.path.exists(filename):\n                break\n            n += 1\n            if n > 10000:  # prevent maybe flooding ?\n                raise Exception('Too many logfile, remove them')\n\n        FileHandler.filename = filename\n        FileHandler.fd = open(filename, 'w')\n\n        Logger.info('Logger: Record log in %s' % filename)\n\n    def _write_message(self, record):\n        if FileHandler.fd in (None, False):\n            return\n\n        FileHandler.fd.write('[%-18s] ' % record.levelname)\n        try:\n            FileHandler.fd.write(record.msg)\n        except UnicodeEncodeError:\n            if PY2:\n                FileHandler.fd.write(record.msg.encode('utf8'))\n        FileHandler.fd.write('\\n')\n        FileHandler.fd.flush()\n\n    def emit(self, message):\n        # during the startup, store the message in the history\n        if Logger.logfile_activated is None:\n            FileHandler.history += [message]\n            return\n\n        # startup done, if the logfile is not activated, avoid history.\n        if Logger.logfile_activated is False:\n            FileHandler.history = []\n            return\n\n        if FileHandler.fd is None:\n            try:\n                self._configure()\n            except Exception:\n                # deactivate filehandler...\n                FileHandler.fd = False\n                Logger.exception('Error while activating FileHandler logger')\n                return\n            while FileHandler.history:\n                _message = FileHandler.history.pop()\n                self._write_message(_message)\n\n        self._write_message(message)\n\n\nclass LoggerHistory(logging.Handler):\n\n    history = []\n\n    def emit(self, message):\n        LoggerHistory.history = [message] + LoggerHistory.history[:100]\n\n\nclass ColoredFormatter(logging.Formatter):\n\n    def __init__(self, msg, use_color=True):\n        logging.Formatter.__init__(self, msg)\n        self.use_color = use_color\n\n    def format(self, record):\n        try:\n            msg = record.msg.split(':', 1)\n            if len(msg) == 2:\n                record.msg = '[%-12s]%s' % (msg[0], msg[1])\n        except:\n            pass\n        levelname = record.levelname\n        if record.levelno == logging.TRACE:\n            levelname = 'TRACE'\n            record.levelname = levelname\n        if self.use_color and levelname in COLORS:\n            levelname_color = (\n                COLOR_SEQ % (30 + COLORS[levelname]) + levelname + RESET_SEQ)\n            record.levelname = levelname_color\n        return logging.Formatter.format(self, record)\n\n\nclass ConsoleHandler(logging.StreamHandler):\n\n    def filter(self, record):\n        try:\n            msg = record.msg\n            k = msg.split(':', 1)\n            if k[0] == 'stderr' and len(k) == 2:\n                previous_stderr.write(k[1] + '\\n')\n                return False\n        except:\n            pass\n        return True\n\n\nclass LogFile(object):\n\n    def __init__(self, channel, func):\n        self.buffer = ''\n        self.func = func\n        self.channel = channel\n        self.errors = ''\n\n    def write(self, s):\n        s = self.buffer + s\n        self.flush()\n        f = self.func\n        channel = self.channel\n        lines = s.split('\\n')\n        for l in lines[:-1]:\n            f('%s: %s' % (channel, l))\n        self.buffer = lines[-1]\n\n    def flush(self):\n        return\n\n\ndef logger_config_update(section, key, value):\n    if LOG_LEVELS.get(value) is None:\n        raise AttributeError('Loglevel {0!r} doesn\\'t exists'.format(value))\n    Logger.setLevel(level=LOG_LEVELS.get(value))\n\n#: Kivy default logger instance\nLogger = logging.getLogger('kivy')\nLogger.logfile_activated = None\nLogger.trace = partial(Logger.log, logging.TRACE)\n\n# set the Kivy logger as the default\nlogging.root = Logger\n\n# add default kivy logger\nLogger.addHandler(LoggerHistory())\nif 'KIVY_NO_FILELOG' not in os.environ:\n    Logger.addHandler(FileHandler())\n\n# Use the custom handler instead of streaming one.\nif 'KIVY_NO_CONSOLELOG' not in os.environ:\n    if hasattr(sys, '_kivy_logging_handler'):\n        Logger.addHandler(getattr(sys, '_kivy_logging_handler'))\n    else:\n        use_color = (\n            os.name != 'nt' and\n            os.environ.get('KIVY_BUILD') not in ('android', 'ios') and\n            os.environ.get('TERM') in (\n                'xterm', 'rxvt', 'rxvt-unicode', 'xterm-256color'))\n        color_fmt = formatter_message(\n            '[%(levelname)-18s] %(message)s', use_color)\n        formatter = ColoredFormatter(color_fmt, use_color=use_color)\n        console = ConsoleHandler()\n        console.setFormatter(formatter)\n        Logger.addHandler(console)\n\n# install stderr handlers\nsys.stderr = LogFile('stderr', Logger.warning)\n\n#: Kivy history handler\nLoggerHistory = LoggerHistory\n", "path": "kivy/logger.py"}], "after_files": [{"content": "'''\nLogger object\n=============\n\nDifferents logging levels are available : trace, debug, info, warning, error\nand critical.\n\nExamples of usage::\n\n    from kivy.logger import Logger\n\n    Logger.info('title: This is a info message.')\n    Logger.debug('title: This is a debug message.')\n\n    try:\n        raise Exception('bleh')\n    except Exception:\n        Logger.exception('Something happened!')\n\nThe message passed to the logger is split into two parts, separated by a colon\n(:). The first part is used as a title, and the second part is used as the\nmessage. This way, you can \"categorize\" your message easily.::\n\n    Logger.info('Application: This is a test')\n\n    # will appear as\n\n    [INFO   ] [Application ] This is a test\n\nLogger configuration\n--------------------\n\nThe Logger can be controlled via the Kivy configuration file::\n\n    [kivy]\n    log_level = info\n    log_enable = 1\n    log_dir = logs\n    log_name = kivy_%y-%m-%d_%_.txt\n\nMore information about the allowed values are described in the\n:mod:`kivy.config` module.\n\nLogger history\n--------------\n\nEven if the logger is not enabled, you still have access to the last 100\nmessages::\n\n    from kivy.logger import LoggerHistory\n\n    print(LoggerHistory.history)\n\n'''\n\nimport logging\nimport os\nimport sys\nimport kivy\nfrom kivy.compat import PY2\nfrom random import randint\nfrom functools import partial\n\n__all__ = ('Logger', 'LOG_LEVELS', 'COLORS', 'LoggerHistory')\n\nLogger = None\n\nBLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = list(range(8))\n\n#These are the sequences need to get colored ouput\nRESET_SEQ = \"\\033[0m\"\nCOLOR_SEQ = \"\\033[1;%dm\"\nBOLD_SEQ = \"\\033[1m\"\n\nprevious_stderr = sys.stderr\n\n\ndef formatter_message(message, use_color=True):\n    if use_color:\n        message = message.replace(\"$RESET\", RESET_SEQ)\n        message = message.replace(\"$BOLD\", BOLD_SEQ)\n    else:\n        message = message.replace(\"$RESET\", \"\").replace(\"$BOLD\", \"\")\n    return message\n\nCOLORS = {\n    'TRACE': MAGENTA,\n    'WARNING': YELLOW,\n    'INFO': GREEN,\n    'DEBUG': CYAN,\n    'CRITICAL': RED,\n    'ERROR': RED}\n\nlogging.TRACE = 9\nLOG_LEVELS = {\n    'trace': logging.TRACE,\n    'debug': logging.DEBUG,\n    'info': logging.INFO,\n    'warning': logging.WARNING,\n    'error': logging.ERROR,\n    'critical': logging.CRITICAL}\n\n\nclass FileHandler(logging.Handler):\n    history = []\n    filename = 'log.txt'\n    fd = None\n\n    def purge_logs(self, directory):\n        '''Purge log is called randomly to prevent the log directory from being\n        filled by lots and lots of log files.\n        You've a chance of 1 in 20 that purge log will be fired.\n        '''\n        if randint(0, 20) != 0:\n            return\n\n        # Use config ?\n        maxfiles = 100\n\n        print('Purge log fired. Analysing...')\n        join = os.path.join\n        unlink = os.unlink\n\n        # search all log files\n        l = [join(directory, x) for x in os.listdir(directory)]\n        if len(l) > maxfiles:\n            # get creation time on every files\n            l = [{'fn': x, 'ctime': os.path.getctime(x)} for x in l]\n\n            # sort by date\n            l = sorted(l, key=lambda x: x['ctime'])\n\n            # get the oldest (keep last maxfiles)\n            l = l[:-maxfiles]\n            print('Purge %d log files' % len(l))\n\n            # now, unlink every files in the list\n            for filename in l:\n                unlink(filename['fn'])\n\n        print('Purge finished !')\n\n    def _configure(self, *largs, **kwargs):\n        from time import strftime\n        from kivy.config import Config\n        log_dir = Config.get('kivy', 'log_dir')\n        log_name = Config.get('kivy', 'log_name')\n\n        _dir = kivy.kivy_home_dir\n        if len(log_dir) and log_dir[0] == '/':\n            _dir = log_dir\n        else:\n            _dir = os.path.join(_dir, log_dir)\n            if not os.path.exists(_dir):\n                os.mkdir(_dir)\n\n        self.purge_logs(_dir)\n\n        pattern = log_name.replace('%_', '@@NUMBER@@')\n        pattern = os.path.join(_dir, strftime(pattern))\n        n = 0\n        while True:\n            filename = pattern.replace('@@NUMBER@@', str(n))\n            if not os.path.exists(filename):\n                break\n            n += 1\n            if n > 10000:  # prevent maybe flooding ?\n                raise Exception('Too many logfile, remove them')\n\n        if FileHandler.filename == filename and FileHandler.fd is not None:\n            return\n        FileHandler.filename = filename\n        if FileHandler.fd is not None:\n            FileHandler.fd.close()\n        FileHandler.fd = open(filename, 'w')\n\n        Logger.info('Logger: Record log in %s' % filename)\n\n    def _write_message(self, record):\n        if FileHandler.fd in (None, False):\n            return\n\n        FileHandler.fd.write('[%-18s] ' % record.levelname)\n        try:\n            FileHandler.fd.write(record.msg)\n        except UnicodeEncodeError:\n            if PY2:\n                FileHandler.fd.write(record.msg.encode('utf8'))\n        FileHandler.fd.write('\\n')\n        FileHandler.fd.flush()\n\n    def emit(self, message):\n        # during the startup, store the message in the history\n        if Logger.logfile_activated is None:\n            FileHandler.history += [message]\n            return\n\n        # startup done, if the logfile is not activated, avoid history.\n        if Logger.logfile_activated is False:\n            FileHandler.history = []\n            return\n\n        if FileHandler.fd is None:\n            try:\n                self._configure()\n                from kivy.config import Config\n                Config.add_callback(self._configure, 'kivy', 'log_dir')\n                Config.add_callback(self._configure, 'kivy', 'log_name')\n            except Exception:\n                # deactivate filehandler...\n                FileHandler.fd = False\n                Logger.exception('Error while activating FileHandler logger')\n                return\n            while FileHandler.history:\n                _message = FileHandler.history.pop()\n                self._write_message(_message)\n\n        self._write_message(message)\n\n\nclass LoggerHistory(logging.Handler):\n\n    history = []\n\n    def emit(self, message):\n        LoggerHistory.history = [message] + LoggerHistory.history[:100]\n\n\nclass ColoredFormatter(logging.Formatter):\n\n    def __init__(self, msg, use_color=True):\n        logging.Formatter.__init__(self, msg)\n        self.use_color = use_color\n\n    def format(self, record):\n        try:\n            msg = record.msg.split(':', 1)\n            if len(msg) == 2:\n                record.msg = '[%-12s]%s' % (msg[0], msg[1])\n        except:\n            pass\n        levelname = record.levelname\n        if record.levelno == logging.TRACE:\n            levelname = 'TRACE'\n            record.levelname = levelname\n        if self.use_color and levelname in COLORS:\n            levelname_color = (\n                COLOR_SEQ % (30 + COLORS[levelname]) + levelname + RESET_SEQ)\n            record.levelname = levelname_color\n        return logging.Formatter.format(self, record)\n\n\nclass ConsoleHandler(logging.StreamHandler):\n\n    def filter(self, record):\n        try:\n            msg = record.msg\n            k = msg.split(':', 1)\n            if k[0] == 'stderr' and len(k) == 2:\n                previous_stderr.write(k[1] + '\\n')\n                return False\n        except:\n            pass\n        return True\n\n\nclass LogFile(object):\n\n    def __init__(self, channel, func):\n        self.buffer = ''\n        self.func = func\n        self.channel = channel\n        self.errors = ''\n\n    def write(self, s):\n        s = self.buffer + s\n        self.flush()\n        f = self.func\n        channel = self.channel\n        lines = s.split('\\n')\n        for l in lines[:-1]:\n            f('%s: %s' % (channel, l))\n        self.buffer = lines[-1]\n\n    def flush(self):\n        return\n\n\ndef logger_config_update(section, key, value):\n    if LOG_LEVELS.get(value) is None:\n        raise AttributeError('Loglevel {0!r} doesn\\'t exists'.format(value))\n    Logger.setLevel(level=LOG_LEVELS.get(value))\n\n#: Kivy default logger instance\nLogger = logging.getLogger('kivy')\nLogger.logfile_activated = None\nLogger.trace = partial(Logger.log, logging.TRACE)\n\n# set the Kivy logger as the default\nlogging.root = Logger\n\n# add default kivy logger\nLogger.addHandler(LoggerHistory())\nif 'KIVY_NO_FILELOG' not in os.environ:\n    Logger.addHandler(FileHandler())\n\n# Use the custom handler instead of streaming one.\nif 'KIVY_NO_CONSOLELOG' not in os.environ:\n    if hasattr(sys, '_kivy_logging_handler'):\n        Logger.addHandler(getattr(sys, '_kivy_logging_handler'))\n    else:\n        use_color = (\n            os.name != 'nt' and\n            os.environ.get('KIVY_BUILD') not in ('android', 'ios') and\n            os.environ.get('TERM') in ('xterm', 'rxvt', 'rxvt-unicode'))\n        color_fmt = formatter_message(\n            '[%(levelname)-18s] %(message)s', use_color)\n        formatter = ColoredFormatter(color_fmt, use_color=use_color)\n        console = ConsoleHandler()\n        console.setFormatter(formatter)\n        Logger.addHandler(console)\n\n# install stderr handlers\nsys.stderr = LogFile('stderr', Logger.warning)\n\n#: Kivy history handler\nLoggerHistory = LoggerHistory\n", "path": "kivy/logger.py"}]}