Skip to content

Commit d691c6c

Browse files
committed
Update NTEventLogHandler to use _winapi to emit event logs
1 parent a0e331a commit d691c6c

File tree

2 files changed

+59
-34
lines changed

2 files changed

+59
-34
lines changed

Lib/logging/handlers.py

Lines changed: 47 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,46 +1125,53 @@ class NTEventLogHandler(logging.Handler):
11251125
"""
11261126
A handler class which sends events to the NT Event Log. Adds a
11271127
registry entry for the specified application name. If no dllname is
1128-
provided, win32service.pyd (which contains some basic message
1128+
provided and pywin32 installed, win32service.pyd (which contains some basic message
11291129
placeholders) is used. Note that use of these placeholders will make
11301130
your event logs big, as the entire message source is held in the log.
11311131
If you want slimmer logs, you have to pass in the name of your own DLL
11321132
which contains the message definitions you want to use in the event log.
11331133
"""
11341134
def __init__(self, appname, dllname=None, logtype="Application"):
11351135
logging.Handler.__init__(self)
1136-
try:
1137-
import win32evtlogutil, win32evtlog
1138-
self.appname = appname
1139-
self._welu = win32evtlogutil
1140-
if not dllname:
1141-
dllname = os.path.split(self._welu.__file__)
1136+
import _winapi
1137+
self._winapi = _winapi
1138+
self.appname = appname
1139+
if not dllname:
1140+
# backward compatibility
1141+
try:
1142+
import win32evtlogutil
1143+
dllname = os.path.split(win32evtlogutil.__file__)
11421144
dllname = os.path.split(dllname[0])
11431145
dllname = os.path.join(dllname[0], r'win32service.pyd')
1144-
self.dllname = dllname
1145-
self.logtype = logtype
1146-
# Administrative privileges are required to add a source to the registry.
1147-
# This may not be available for a user that just wants to add to an
1148-
# existing source - handle this specific case.
1149-
try:
1150-
self._welu.AddSourceToRegistry(appname, dllname, logtype)
1151-
except Exception as e:
1152-
# This will probably be a pywintypes.error. Only raise if it's not
1153-
# an "access denied" error, else let it pass
1154-
if getattr(e, 'winerror', None) != 5: # not access denied
1155-
raise
1156-
self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE
1157-
self.typemap = {
1158-
logging.DEBUG : win32evtlog.EVENTLOG_INFORMATION_TYPE,
1159-
logging.INFO : win32evtlog.EVENTLOG_INFORMATION_TYPE,
1160-
logging.WARNING : win32evtlog.EVENTLOG_WARNING_TYPE,
1161-
logging.ERROR : win32evtlog.EVENTLOG_ERROR_TYPE,
1162-
logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE,
1163-
}
1164-
except ImportError:
1165-
print("The Python Win32 extensions for NT (service, event "\
1166-
"logging) appear not to be available.")
1167-
self._welu = None
1146+
except ImportError:
1147+
pass
1148+
self.dllname = dllname
1149+
self.logtype = logtype
1150+
# Administrative privileges are required to add a source to the registry.
1151+
# This may not be available for a user that just wants to add to an
1152+
# existing source - handle this specific case.
1153+
try:
1154+
self._add_source_to_registry(appname, dllname, logtype)
1155+
except PermissionError:
1156+
pass
1157+
self.deftype = _winapi.EVENTLOG_ERROR_TYPE
1158+
self.typemap = {
1159+
logging.DEBUG : _winapi.EVENTLOG_INFORMATION_TYPE,
1160+
logging.INFO : _winapi.EVENTLOG_INFORMATION_TYPE,
1161+
logging.WARNING : _winapi.EVENTLOG_WARNING_TYPE,
1162+
logging.ERROR : _winapi.EVENTLOG_ERROR_TYPE,
1163+
logging.CRITICAL: _winapi.EVENTLOG_ERROR_TYPE,
1164+
}
1165+
1166+
def _add_source_to_registry(self, appname, dllname, logtype):
1167+
import winreg
1168+
1169+
key_path = f"SYSTEM\\CurrentControlSet\\Services\\EventLog\\{logtype}\\{appname}"
1170+
1171+
with winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, key_path) as key:
1172+
if dllname:
1173+
winreg.SetValueEx(key, "EventMessageFile", 0, winreg.REG_EXPAND_SZ, dllname)
1174+
winreg.SetValueEx(key, "TypesSupported", 0, winreg.REG_DWORD, 7) # All types are supported
11681175

11691176
def getMessageID(self, record):
11701177
"""
@@ -1205,13 +1212,20 @@ def emit(self, record):
12051212
Determine the message ID, event category and event type. Then
12061213
log the message in the NT event log.
12071214
"""
1208-
if self._welu:
1215+
if self._winapi:
12091216
try:
12101217
id = self.getMessageID(record)
12111218
cat = self.getEventCategory(record)
12121219
type = self.getEventType(record)
12131220
msg = self.format(record)
1214-
self._welu.ReportEvent(self.appname, id, cat, type, [msg])
1221+
1222+
# Get a handle to the event log
1223+
handle = self._winapi.RegisterEventSource(None, self.appname)
1224+
if handle != self._winapi.INVALID_HANDLE_VALUE:
1225+
try:
1226+
self._winapi.ReportEvent(handle, type, cat, id, [msg])
1227+
finally:
1228+
self._winapi.DeregisterEventSource(handle)
12151229
except Exception:
12161230
self.handleError(record)
12171231

Lib/test/test_logging.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7185,8 +7185,8 @@ def test_compute_rollover(self, when=when, interval=interval, exp=exp):
71857185
setattr(TimedRotatingFileHandlerTest, name, test_compute_rollover)
71867186

71877187

7188-
@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil/pywintypes required for this test.')
71897188
class NTEventLogHandlerTest(BaseTest):
7189+
@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil/pywintypes required for this test.')
71907190
def test_basic(self):
71917191
logtype = 'Application'
71927192
elh = win32evtlog.OpenEventLog(None, logtype)
@@ -7220,6 +7220,17 @@ def test_basic(self):
72207220
msg = 'Record not found in event log, went back %d records' % GO_BACK
72217221
self.assertTrue(found, msg=msg)
72227222

7223+
@unittest.skipUnless(sys.platform == "win32", "Windows required for this test")
7224+
def test_updated_implementation(self):
7225+
h = logging.handlers.NTEventLogHandler('test_updated')
7226+
self.addCleanup(h.close)
7227+
7228+
# Verify that the handler uses _winapi module
7229+
self.assertIsNotNone(h._winapi, "_winapi module should be available")
7230+
7231+
r = logging.makeLogRecord({'msg': 'Test Updated Implementation'})
7232+
h.emit(r)
7233+
72237234

72247235
class MiscTestCase(unittest.TestCase):
72257236
def test__all__(self):

0 commit comments

Comments
 (0)