"""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
import inspect\r
import winsound\r
import traceback\r
-import re\r
-import nvwave\r
from types import MethodType\r
import globalVars\r
\r
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
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
\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
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
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
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