OSDN Git Service

Merge 2013.1.
[nvdajp/nvdajp.git] / source / logHandler.py
index b8ce57b..fb70fba 100755 (executable)
@@ -1,6 +1,7 @@
 """Utilities and classes to manage logging in NVDA"""\r
 \r
 import os\r
+import ctypes\r
 import sys\r
 import warnings\r
 from encodings import utf_8\r
@@ -9,8 +10,6 @@ from logging import _levelNames as levelNames
 import inspect\r
 import winsound\r
 import traceback\r
-import re\r
-import nvwave\r
 from types import MethodType\r
 import globalVars\r
 \r
@@ -18,6 +17,7 @@ ERROR_INVALID_WINDOW_HANDLE = 1400
 ERROR_TIMEOUT = 1460\r
 RPC_S_SERVER_UNAVAILABLE = 1722\r
 RPC_S_CALL_FAILED_DNE = 1727\r
+EPT_S_NOT_REGISTERED = 1753\r
 E_ACCESSDENIED = -2147024891\r
 EVENT_E_ALL_SUBSCRIBERS_FAILED = -2147220991\r
 RPC_E_CALL_REJECTED = -2147418111\r
@@ -108,7 +108,7 @@ class Logger(logging.Logger):
                        codepath=getCodePath(f)\r
                extra["codepath"] = codepath\r
 \r
-               if globalVars.appArgs.secure:\r
+               if not globalVars.appArgs or globalVars.appArgs.secure:\r
                        # The log might expose sensitive information and the Save As dialog in the Log Viewer is a security risk.\r
                        activateLogViewer = False\r
 \r
@@ -162,7 +162,7 @@ class Logger(logging.Logger):
 \r
                exc = exc_info[1]\r
                if (\r
-                       (isinstance(exc, WindowsError) and exc.winerror in (ERROR_INVALID_WINDOW_HANDLE, ERROR_TIMEOUT, RPC_S_SERVER_UNAVAILABLE, RPC_S_CALL_FAILED_DNE, RPC_E_CALL_CANCELED))\r
+                       (isinstance(exc, WindowsError) and exc.winerror in (ERROR_INVALID_WINDOW_HANDLE, ERROR_TIMEOUT, RPC_S_SERVER_UNAVAILABLE, RPC_S_CALL_FAILED_DNE, EPT_S_NOT_REGISTERED, RPC_E_CALL_CANCELED))\r
                        or (isinstance(exc, comtypes.COMError) and (exc.hresult in (E_ACCESSDENIED, EVENT_E_ALL_SUBSCRIBERS_FAILED, RPC_E_CALL_REJECTED, RPC_E_CALL_CANCELED, RPC_E_DISCONNECTED) or exc.hresult & 0xFFFF == RPC_S_SERVER_UNAVAILABLE))\r
                        or isinstance(exc, watchdog.CallCancelled)\r
                ):\r
@@ -170,8 +170,26 @@ class Logger(logging.Logger):
                else:\r
                        level = self.ERROR\r
 \r
+               if not self.isEnabledFor(level):\r
+                       return\r
                self._log(level, msg, (), exc_info=exc_info, **kwargs)\r
 \r
+class RemoteHandler(logging.Handler):\r
+\r
+       def __init__(self):\r
+               #Load nvdaHelperRemote.dll but with an altered search path so it can pick up other dlls in lib\r
+               h=ctypes.windll.kernel32.LoadLibraryExW(os.path.abspath(ur"lib\nvdaHelperRemote.dll"),0,0x8)\r
+               self._remoteLib=ctypes.WinDLL("nvdaHelperRemote",handle=h) if h else None\r
+               logging.Handler.__init__(self)\r
+\r
+       def emit(self, record):\r
+               msg = self.format(record)\r
+               if self._remoteLib:\r
+                       try:\r
+                               self._remoteLib.nvdaControllerInternal_logMessage(record.levelno, ctypes.windll.kernel32.GetCurrentProcessId(), msg)\r
+                       except WindowsError:\r
+                               pass\r
+\r
 class FileHandler(logging.StreamHandler):\r
 \r
        def __init__(self, filename, mode):\r
@@ -197,6 +215,7 @@ class FileHandler(logging.StreamHandler):
                        except:\r
                                pass\r
                elif record.levelno>=logging.ERROR and shouldPlayErrorSound:\r
+                       import nvwave\r
                        try:\r
                                nvwave.playWaveFile("waves\\error.wav")\r
                        except:\r
@@ -267,25 +286,40 @@ def _excepthook(*exc_info):
 def _showwarning(message, category, filename, lineno, file=None, line=None):\r
        log.debugWarning(warnings.formatwarning(message, category, filename, lineno, line).rstrip(), codepath="Python warning")\r
 \r
-def initialize():\r
+def initialize(shouldDoRemoteLogging=False):\r
        """Initialize logging.\r
        This must be called before any logging can occur.\r
        @precondition: The command line arguments have been parsed into L{globalVars.appArgs}.\r
+       @var shouldDoRemoteLogging: True if all logging should go to the real NVDA via rpc (for slave)\r
+       @type shouldDoRemoteLogging: bool\r
        """\r
        global log\r
        logging.addLevelName(Logger.DEBUGWARNING, "DEBUGWARNING")\r
        logging.addLevelName(Logger.IO, "IO")\r
-       if globalVars.appArgs.secure:\r
-               # Don't log in secure mode.\r
-               logHandler = logging.NullHandler()\r
-               # There's no point in logging anything at all, since it'll go nowhere.\r
-               log.setLevel(100)\r
+       if not shouldDoRemoteLogging:\r
+               logFormatter=Formatter("%(levelname)s - %(codepath)s (%(asctime)s):\n%(message)s", "%H:%M:%S")\r
+               if globalVars.appArgs.secure:\r
+                       # Don't log in secure mode.\r
+                       logHandler = logging.NullHandler()\r
+                       # There's no point in logging anything at all, since it'll go nowhere.\r
+                       log.setLevel(100)\r
+               else:\r
+                       if not globalVars.appArgs.logFileName:\r
+                               globalVars.appArgs.logFileName = _getDefaultLogFilePath()\r
+                       # Keep a backup of the previous log file so we can access it even if NVDA crashes or restarts.\r
+                       oldLogFileName = os.path.join(os.path.dirname(globalVars.appArgs.logFileName), "nvda-old.log")\r
+                       try:\r
+                               # We must remove the old log file first as os.rename does replace it.\r
+                               if os.path.exists(oldLogFileName):\r
+                                       os.unlink(oldLogFileName)\r
+                               os.rename(globalVars.appArgs.logFileName, oldLogFileName)\r
+                       except (IOError, WindowsError):\r
+                               pass # Probably log does not exist, don't care.\r
+                       # Our FileHandler always outputs in UTF-8.\r
+                       logHandler = FileHandler(globalVars.appArgs.logFileName, mode="wt")\r
        else:\r
-               if not globalVars.appArgs.logFileName:\r
-                       globalVars.appArgs.logFileName = _getDefaultLogFilePath()\r
-               # Our FileHandler always outputs in UTF-8.\r
-               logHandler = FileHandler(globalVars.appArgs.logFileName, mode="wt")\r
-       logFormatter=Formatter("%(levelname)s - %(codepath)s (%(asctime)s):\n%(message)s", "%H:%M:%S")\r
+               logHandler = RemoteHandler()\r
+               logFormatter = Formatter("%(codepath)s:\n%(message)s")\r
        logHandler.setFormatter(logFormatter)\r
        log.addHandler(logHandler)\r
        redirectStdout(log)\r