如何为我的 Python 记录器创建 SQLite 3 数据库处理程序?
How do I create an SQLite 3 database handler for my Python logger?
我一直在尝试使用 Python 中的内置日志库为我的记录器创建一个数据库处理程序。我试图通过 subclassing 主日志记录模块的“Handler
”class 来做到这一点,但是当 StreamHandler
class 完美时,它会抛出很多错误很好,它的写法就像我的 DatabaseHandler
class 的写法一样。
我查看了日志库的文档,似乎没有 class 用于创建自定义日志处理程序。所以,我决定编写 DatabaseHandler
class 而不从 Handler
class 继承属性,SQLite 3 代码抛出一个错误,说没有这样的 table 作为 'logs'
table 当我在第一行创建 table 如果它不存在。
我的代码:
from logging import getLogger, StreamHandler, Formatter, Handler, NOTSET, getLevelName
from datetime import datetime as date_time
from sqlite3 import connect
class DatabaseHandler(Handler):
def __init__(self, db_file):
super().__init__(self)
self.db_file = db_file
self.db_file = connect(self.db_file)
def emit(self, record):
"""
Conditionally emit the specified logging record.
Emission depends on filters which may have been added to the handler.
Wrap the actual emission of the record with acquisition/release of
the I/O thread lock. Returns whether the filter passed the record for
emission.
"""
self.db_file.executescript(
'CREATE TABLE IF NOT EXISTS logs (date TEXT, '
'time TEXT, lvl INTEGER, lvl_name TEXT, msg TEXT, '
'logger TEXT, lineno INTEGER);'
'INSERT INTO logs VALUES ("%s", "%s", %s, "%s", "%s", "%s", %s)' % (
date_time.now().strftime('%A, the %d of %B, %Y'),
date_time.now().strftime('%I:%M %p'),
record.levelno,
record.level,
record.msg,
record.name,
record.lineno
)
)
self.db_file.commit()
self.db_file.close()
logger = getLogger(__name__)
logger_formatter = Formatter(
fmt = '<LVL: %(levelno)s (%(levelname)s), LOGGER: %(name)s> - "%(message)s at %(asctime)s"',
datefmt = '%I:%M %p on %A, the %d of %B, %Y'
)
logger_stream_handler = StreamHandler()
logger_stream_handler.setFormatter(logger_formatter)
logger_stream_handler.setLevel(10)
logger_database_handler = DatabaseHandler('test.db')
logger.addHandler(logger_stream_handler)
logger.addHandler(logger_database_handler)
logger.log(
msg = 'Something happened',
level = 10
)
print(connect('test.db').execute('SELECT name FROM sqlite_master WHERE type = "table"').fetchall())
它的工作原理是将其添加为记录器的处理程序,然后每次记录某些内容并创建 LogRecord
时,它还将存储 LogRecord
在数据库的 logs
table.
我还为 class 创建了一个“emit
”方法,因为我认为这是 Handler
class 在日志记录 __init__.py
模块文件在进行新的日志记录调用并创建 LogRecord
时触发。
但是,问题来了:每当我在 class' __init__
方法上调用 super().__init__(self)
以从 Handler
class 继承属性时,它抛出这个异常:
Traceback (most recent call last):
File "<string>", line 47, in <module>
File "<string>", line 7, in _init_
File"/lib/python3.8/logging/_init_.py", line 865, in _init_
self.level = checkLevel(level)
File "/lib/python3.8/logging/__init_.py", line 192, in checkLevel
elif str(level) == level:
File "/lib/python3.8/logging/__init_.py", line 1035, in _repr_
level = getLevelName(self.level)
AttributeError: 'DatabaseHandler' object has no attribute 'level'
[Program finished]
[Program finished]
好吧,你没那么远...
调用__init__
的错误是由传递self
引起的,它被解释为级别。即使这不是真正的错误,创建 table 也是一种数据定义语言操作,不应在每条消息上重复:它应该进入 __init__
方法:
def __init__(self, db_file):
super().__init__()
self.db_file = db_file
self.db_file = connect(self.db_file)
self.db_file.execute('CREATE TABLE IF NOT EXISTS logs (date TEXT, '
'time TEXT, lvl INTEGER, lvl_name TEXT, msg TEXT, '
'logger TEXT, lineno INTEGER)')
稍后在 emit
中,您传递了一个 record.level
参数,该参数应该是 record.levelname
。而且你不应该在 emit 方法中关闭数据库:一个记录器应该能够记录多条消息!此外,您在查询本身中注入了参数,这很糟糕,因为几十年来它一直是 SQL 注入攻击的原因。您应该使用参数化查询:
self.db_file.execute(
'INSERT INTO logs VALUES (:1,:2,:3,:4, :5, :6, :7)', (
date_time.now().strftime('%A, the %d of %B, %Y'),
date_time.now().strftime('%I:%M %p'),
record.levelno,
record.levelname,
record.msg,
record.name,
record.lineno
)
)
self.db_file.commit()
并且由于默认级别是 NOTSET,您应该在记录器及其处理程序上设置它:
logger = getLogger(__name__)
logger.setLevel(10)
logger_database_handler = DatabaseHandler('test.db')
logger.addHandler(logger_database_handler)
logger_database_handler.setLevel(10)
logger.log(
msg = 'Something happened',
level = 10
)
完成这些更改后,您应该会在 logs
table...
中找到记录内容
我一直在尝试使用 Python 中的内置日志库为我的记录器创建一个数据库处理程序。我试图通过 subclassing 主日志记录模块的“Handler
”class 来做到这一点,但是当 StreamHandler
class 完美时,它会抛出很多错误很好,它的写法就像我的 DatabaseHandler
class 的写法一样。
我查看了日志库的文档,似乎没有 class 用于创建自定义日志处理程序。所以,我决定编写 DatabaseHandler
class 而不从 Handler
class 继承属性,SQLite 3 代码抛出一个错误,说没有这样的 table 作为 'logs'
table 当我在第一行创建 table 如果它不存在。
我的代码:
from logging import getLogger, StreamHandler, Formatter, Handler, NOTSET, getLevelName
from datetime import datetime as date_time
from sqlite3 import connect
class DatabaseHandler(Handler):
def __init__(self, db_file):
super().__init__(self)
self.db_file = db_file
self.db_file = connect(self.db_file)
def emit(self, record):
"""
Conditionally emit the specified logging record.
Emission depends on filters which may have been added to the handler.
Wrap the actual emission of the record with acquisition/release of
the I/O thread lock. Returns whether the filter passed the record for
emission.
"""
self.db_file.executescript(
'CREATE TABLE IF NOT EXISTS logs (date TEXT, '
'time TEXT, lvl INTEGER, lvl_name TEXT, msg TEXT, '
'logger TEXT, lineno INTEGER);'
'INSERT INTO logs VALUES ("%s", "%s", %s, "%s", "%s", "%s", %s)' % (
date_time.now().strftime('%A, the %d of %B, %Y'),
date_time.now().strftime('%I:%M %p'),
record.levelno,
record.level,
record.msg,
record.name,
record.lineno
)
)
self.db_file.commit()
self.db_file.close()
logger = getLogger(__name__)
logger_formatter = Formatter(
fmt = '<LVL: %(levelno)s (%(levelname)s), LOGGER: %(name)s> - "%(message)s at %(asctime)s"',
datefmt = '%I:%M %p on %A, the %d of %B, %Y'
)
logger_stream_handler = StreamHandler()
logger_stream_handler.setFormatter(logger_formatter)
logger_stream_handler.setLevel(10)
logger_database_handler = DatabaseHandler('test.db')
logger.addHandler(logger_stream_handler)
logger.addHandler(logger_database_handler)
logger.log(
msg = 'Something happened',
level = 10
)
print(connect('test.db').execute('SELECT name FROM sqlite_master WHERE type = "table"').fetchall())
它的工作原理是将其添加为记录器的处理程序,然后每次记录某些内容并创建 LogRecord
时,它还将存储 LogRecord
在数据库的 logs
table.
我还为 class 创建了一个“emit
”方法,因为我认为这是 Handler
class 在日志记录 __init__.py
模块文件在进行新的日志记录调用并创建 LogRecord
时触发。
但是,问题来了:每当我在 class' __init__
方法上调用 super().__init__(self)
以从 Handler
class 继承属性时,它抛出这个异常:
Traceback (most recent call last):
File "<string>", line 47, in <module>
File "<string>", line 7, in _init_
File"/lib/python3.8/logging/_init_.py", line 865, in _init_
self.level = checkLevel(level)
File "/lib/python3.8/logging/__init_.py", line 192, in checkLevel
elif str(level) == level:
File "/lib/python3.8/logging/__init_.py", line 1035, in _repr_
level = getLevelName(self.level)
AttributeError: 'DatabaseHandler' object has no attribute 'level'
[Program finished]
[Program finished]
好吧,你没那么远...
调用__init__
的错误是由传递self
引起的,它被解释为级别。即使这不是真正的错误,创建 table 也是一种数据定义语言操作,不应在每条消息上重复:它应该进入 __init__
方法:
def __init__(self, db_file):
super().__init__()
self.db_file = db_file
self.db_file = connect(self.db_file)
self.db_file.execute('CREATE TABLE IF NOT EXISTS logs (date TEXT, '
'time TEXT, lvl INTEGER, lvl_name TEXT, msg TEXT, '
'logger TEXT, lineno INTEGER)')
稍后在 emit
中,您传递了一个 record.level
参数,该参数应该是 record.levelname
。而且你不应该在 emit 方法中关闭数据库:一个记录器应该能够记录多条消息!此外,您在查询本身中注入了参数,这很糟糕,因为几十年来它一直是 SQL 注入攻击的原因。您应该使用参数化查询:
self.db_file.execute(
'INSERT INTO logs VALUES (:1,:2,:3,:4, :5, :6, :7)', (
date_time.now().strftime('%A, the %d of %B, %Y'),
date_time.now().strftime('%I:%M %p'),
record.levelno,
record.levelname,
record.msg,
record.name,
record.lineno
)
)
self.db_file.commit()
并且由于默认级别是 NOTSET,您应该在记录器及其处理程序上设置它:
logger = getLogger(__name__)
logger.setLevel(10)
logger_database_handler = DatabaseHandler('test.db')
logger.addHandler(logger_database_handler)
logger_database_handler.setLevel(10)
logger.log(
msg = 'Something happened',
level = 10
)
完成这些更改后,您应该会在 logs
table...