Repository: kivy/kivy

Issue:
Kivy logger doesn't correctly handle Logger.exception(e)
Python loggers support logging exceptions. In this case the traceback of the exception is printed in the logs.

This does not work with Kivy's `Logger` object.

```
    try:
       # ...
    except Exception as e:
        Logger.exception(e)
```

Results to:

```
 Traceback (most recent call last):
   File "webkivy/main.py", line 157, in <module>
     RemoteRunnerApp().run()
   File "webkivy/main.py", line 88, in run
     super(RemoteRunnerApp, self).run()
   File "/Applications/Kivy2.app/Contents/Resources/kivy/kivy/app.py", line 828, in run
     runTouchApp()
   File "/Applications/Kivy2.app/Contents/Resources/kivy/kivy/base.py", line 487, in runTouchApp
     EventLoop.window.mainloop()
   File "/Applications/Kivy2.app/Contents/Resources/kivy/kivy/core/window/window_sdl2.py", line 619, in mainloop
     self._mainloop()
   File "/Applications/Kivy2.app/Contents/Resources/kivy/kivy/core/window/window_sdl2.py", line 362, in _mainloop
     EventLoop.idle()
   File "/Applications/Kivy2.app/Contents/Resources/kivy/kivy/base.py", line 330, in idle
     self.dispatch_input()
   File "/Applications/Kivy2.app/Contents/Resources/kivy/kivy/base.py", line 315, in dispatch_input
     post_dispatch_input(*pop(0))
   File "/Applications/Kivy2.app/Contents/Resources/kivy/kivy/base.py", line 281, in post_dispatch_input
     wid.dispatch('on_touch_up', me)
   File "kivy/_event.pyx", line 718, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:7699)
   File "/Applications/Kivy2.app/Contents/Resources/kivy/kivy/uix/behaviors/button.py", line 138, in on_touch_up
     self.dispatch('on_release')
   File "kivy/_event.pyx", line 714, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:7654)
   File "kivy/_event.pyx", line 1224, in kivy._event.EventObservers.dispatch (kivy/_event.c:13497)
   File "kivy/_event.pyx", line 1108, in kivy._event.EventObservers._dispatch (kivy/_event.c:12329)
   File "/Applications/Kivy2.app/Contents/Resources/kivy/kivy/lang.py", line 1557, in custom_callback
     exec(__kvlang__.co_value, idmap)
   File "<string>", line 19, in <module>
   File "webkivy/main.py", line 121, in run_script
     Logger.exception(e)
   File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 1198, in exception
     self.error(msg, *args, **kwargs)
   File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 1191, in error
     self._log(ERROR, msg, args, **kwargs)
   File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 1284, in _log
     self.handle(record)
   File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 1294, in handle
     self.callHandlers(record)
   File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 1334, in callHandlers
     hdlr.handle(record)
   File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 757, in handle
     self.emit(record)
   File "/Applications/Kivy2.app/Contents/Resources/kivy/kivy/logger.py", line 218, in emit
     self._write_message(message)
   File "/Applications/Kivy2.app/Contents/Resources/kivy/kivy/logger.py", line 185, in _write_message
     FileHandler.fd.write(record.msg)
 TypeError: expected a character buffer object
```



Discussion:
The Logger.exception  only receive string, not the Except, although `print(e)` is string, but `type(e)` is Except. For example:

```
import codecs

fp = codecs.open('test.txt', 'w', 'utf-8')
fp.write('test text')

try:
    print(a)
except NameError as e:
    print(type(e))
    fp.write(e)
```


Resolving patch:
diff --git a/kivy/logger.py b/kivy/logger.py
--- a/kivy/logger.py
+++ b/kivy/logger.py
@@ -180,14 +180,26 @@ def _write_message(self, record):
         if FileHandler.fd in (None, False):
             return
 
-        FileHandler.fd.write('[%-18s] ' % record.levelname)
-        try:
-            FileHandler.fd.write(record.msg)
-        except UnicodeEncodeError:
-            if PY2:
-                FileHandler.fd.write(record.msg.encode('utf8'))
-        FileHandler.fd.write('\n')
-        FileHandler.fd.flush()
+        msg = self.format(record)
+        stream = FileHandler.fd
+        fs = "%s\n"
+        stream.write('[%-18s] ' % record.levelname)
+        if PY2:
+            try:
+                if (isinstance(msg, unicode) and
+                    getattr(stream, 'encoding', None)):
+                    ufs = u'%s\n'
+                    try:
+                        stream.write(ufs % msg)
+                    except UnicodeEncodeError:
+                        stream.write((ufs % msg).encode(stream.encoding))
+                else:
+                    stream.write(fs % msg)
+            except UnicodeError:
+                stream.write(fs % msg.encode("UTF-8"))
+        else:
+            stream.write(fs % msg)
+        stream.flush()
 
     def emit(self, message):
         # during the startup, store the message in the history


Test patch: