【问题标题】:python logging to databasepython记录到数据库
【发布时间】:2011-01-19 20:02:54
【问题描述】:

我正在寻找一种方法让 python 记录器模块记录到数据库并在数据库关闭时回退到文件系统。

所以基本上有两件事:如何让记录器记录到数据库以及如何在数据库关闭时使其落入文件记录。

【问题讨论】:

  • 您是否碰巧找到了现有的实现?
  • 是的,这很有帮助:github.com/dcramer/django-db-log,至少可以查看所需数据库的架构。
  • 你最后得到了什么?

标签: python database logging


【解决方案1】:

给自己写一个handler,将日志定向到相关数据库。当它失败时,您可以将其从记录器的处理程序列表中删除。有很多方法可以处理故障模式。

【讨论】:

【解决方案2】:

我最近设法用 Python 编写了自己的数据库记录器。由于我找不到任何示例,我想我在这里发布我的示例。适用于 MS SQL。

数据库表可能如下所示:

CREATE TABLE [db_name].[log](
    [id] [bigint] IDENTITY(1,1) NOT NULL,
    [log_level] [int] NULL,
    [log_levelname] [char](32) NULL,
    [log] [char](2048) NOT NULL,
    [created_at] [datetime2](7) NOT NULL,
    [created_by] [char](32) NOT NULL,
) ON [PRIMARY]

类本身:

class LogDBHandler(logging.Handler):
    '''
    Customized logging handler that puts logs to the database.
    pymssql required
    '''
    def __init__(self, sql_conn, sql_cursor, db_tbl_log):
        logging.Handler.__init__(self)
        self.sql_cursor = sql_cursor
        self.sql_conn = sql_conn
        self.db_tbl_log = db_tbl_log

    def emit(self, record):
        # Set current time
        tm = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(record.created))
        # Clear the log message so it can be put to db via sql (escape quotes)
        self.log_msg = record.msg
        self.log_msg = self.log_msg.strip()
        self.log_msg = self.log_msg.replace('\'', '\'\'')
        # Make the SQL insert
        sql = 'INSERT INTO ' + self.db_tbl_log + ' (log_level, ' + \
            'log_levelname, log, created_at, created_by) ' + \
            'VALUES (' + \
            ''   + str(record.levelno) + ', ' + \
            '\'' + str(record.levelname) + '\', ' + \
            '\'' + str(self.log_msg) + '\', ' + \
            '(convert(datetime2(7), \'' + tm + '\')), ' + \
            '\'' + str(record.name) + '\')'
        try:
            self.sql_cursor.execute(sql)
            self.sql_conn.commit()
        # If error - print it out on screen. Since DB is not working - there's
        # no point making a log about it to the database :)
        except pymssql.Error as e:
            print sql
            print 'CRITICAL DB ERROR! Logging to database not possible!'

及用法示例:

import pymssql
import time
import logging

db_server = 'servername'
db_user = 'db_user'
db_password = 'db_pass'
db_dbname = 'db_name'
db_tbl_log = 'log'

log_file_path = 'C:\\Users\\Yourname\\Desktop\\test_log.txt'
log_error_level     = 'DEBUG'       # LOG error level (file)
log_to_db = True                    # LOG to database?

class LogDBHandler(logging.Handler):
    [...]

# Main settings for the database logging use
if (log_to_db):
    # Make the connection to database for the logger
    log_conn = pymssql.connect(db_server, db_user, db_password, db_dbname, 30)
    log_cursor = log_conn.cursor()
    logdb = LogDBHandler(log_conn, log_cursor, db_tbl_log)

# Set logger
logging.basicConfig(filename=log_file_path)

# Set db handler for root logger
if (log_to_db):
    logging.getLogger('').addHandler(logdb)
# Register MY_LOGGER
log = logging.getLogger('MY_LOGGER')
log.setLevel(log_error_level)

# Example variable
test_var = 'This is test message'

# Log the variable contents as an error
log.error('This error occurred: %s' % test_var)

上面将记录到数据库和文件中。如果不需要文件 - 跳过 'logging.basicConfig(filename=log_file_path)' 行。使用“日志”记录的所有内容 - 将被记录为 MY_LOGGER。如果出现一些外部错误(即在导入的模块中或其他内容中) - 错误将显示为“root”,因为“root”记录器也处于活动状态,并且正在使用数据库处理程序。

【讨论】:

  • 感谢脚本。但请注意,建议在 INSERT sql 中使用参数。这有助于提高性能(因为可以缓存 INSERT 语句)和安全性。
  • 这很好用!我也用 pyodbc 试过了,效果很好。
  • 如果您不介意存储记录,直到您想要在单个事务中将它们全部写入数据库。如果存在明显的延迟,这尤其有用。
【解决方案3】:

我正在再次挖掘这个。

SqlAlchemy 有一个解决方案(此配方不需要 Pyramid):

https://docs.pylonsproject.org/projects/pyramid-cookbook/en/latest/logging/sqlalchemy_logger.html

您可以通过添加额外字段来改进日志记录,这是一个指南:https://stackoverflow.com/a/17558764/1115187

回退到 FS

不确定这是否 100% 正确,但您可以有 2 个处理程序:

  1. 数据库处理程序(写入数据库)
  2. 文件处理程序(写入文件或流)

只需使用try-except 包装 DB-commit。但请注意:该文件将包含所有日志条目,而不仅仅是数据库保存失败的条目。

【讨论】:

    【解决方案4】:

    老问题,但为其他人放弃了这个。如果要使用 python 日志记录,可以添加两个处理程序。一种用于写入文件的旋转文件处理程序。这是稳健的,并且无论 dB 是否上升都可以完成。 另一个可以写入另一个服务/模块,例如 pymongo 集成。

    查看 logging.config 以了解如何从代码或 json 设置处理程序。

    【讨论】:

      【解决方案5】:

      使用备份记录器将 Python 记录到数据库


      问题

      我在服务器中运行 Django 项目时遇到了同样的问题,因为有时您需要远程检查日志。


      解决方案

      首先,记录器需要一个处理程序来将日志插入数据库。在此之前,由于我的 SQL 不好,所以需要一个 ORM,我选择SQLAlchemy

      型号:

      # models.py
      from sqlalchemy import Column, Integer, String, DateTime, Text
      from sqlalchemy.ext.declarative import declarative_base
      import datetime
      
      base = declarative_base()
      
      
      class Log(base):
          __tablename__ = "log"
          id = Column(Integer, primary_key=True, autoincrement=True)
          time = Column(DateTime, nullable=False, default=datetime.datetime.now)
          level_name = Column(String(10), nullable=True)
          module = Column(String(200), nullable=True)
          thread_name = Column(String(200), nullable=True)
          file_name = Column(String(200), nullable=True)
          func_name = Column(String(200), nullable=True)
          line_no = Column(Integer, nullable=True)
          process_name = Column(String(200), nullable=True)
          message = Column(Text)
          last_line = Column(Text)
      

      这是插入数据库的crud:

      #crud.py
      import sqlalchemy
      from .models import base
      from traceback import print_exc
      
      
      class Crud:
          def __init__(self, connection_string=f'sqlite:///log_db.sqlite3',
                       encoding='utf-8',
                       pool_size=10,
                       max_overflow=20,
                       pool_recycle=3600):
      
              self.connection_string = connection_string
              self.encoding = encoding
              self.pool_size = pool_size
              self.max_overflow = max_overflow
              self.pool_recycle = pool_recycle
              self.engine = None
              self.session = None
      
          def initiate(self):
              self.create_engine()
              self.create_session()
              self.create_tables()
      
          def create_engine(self):
              self.engine = sqlalchemy.create_engine(self.connection_string)
      
          def create_session(self):
              self.session = sqlalchemy.orm.Session(bind=self.engine)
      
          def create_tables(self):
              base.metadata.create_all(self.engine)
      
          def insert(self, instances):
              try:
                  self.session.add(instances)
                  self.session.commit()
                  self.session.flush()
              except:
                  self.session.rollback()
                  raise
      
          def __del__(self):
              self.close_session()
              self.close_all_connections()
      
          def close_session(self):
              try:
                  self.session.close()
              except:
                  print_exc()
              else:
                  self.session = None
      
          def close_all_connections(self):
              try:
                  self.engine.dispose()
              except:
                  print_exc()
              else:
                  self.engine = None
      

      处理程序:

      # handler.py
      from logging import Handler, getLogger
      from traceback import print_exc
      from .crud import Crud
      from .models import Log
      
      
      my_crud = Crud(
          connection_string=<connection string to reach your db>,
          encoding='utf-8',
          pool_size=10,
          max_overflow=20,
          pool_recycle=3600)
      
      my_crud.initiate()
      
      
      class DBHandler(Handler):
          backup_logger = None
      
          def __init__(self, level=0, backup_logger_name=None):
              super().__init__(level)
              if backup_logger_name:
                  self.backup_logger = getLogger(backup_logger_name)
      
          def emit(self, record):
              try:
                  message = self.format(record)
                  try:
                      last_line = message.rsplit('\n', 1)[-1]
                  except:
                      last_line = None
      
                  try:
                      new_log = Log(module=record.module,
                                    thread_name=record.threadName,
                                    file_name=record.filename,
                                    func_name=record.funcName,
                                    level_name=record.levelname,
                                    line_no=record.lineno,
                                    process_name=record.processName,
                                    message=message,
                                    last_line=last_line)
                      # raise
      
                      my_crud.insert(instances=new_log)
                  except:
                      if self.backup_logger:
                          try:
                              getattr(self.backup_logger, record.levelname.lower())(record.message)
                          except:
                              print_exc()
                      else:
                          print_exc()
      
              except:
                  print_exc()
      

      测试检查记录器:

      # test.py
      from logging import basicConfig, getLogger, DEBUG, FileHandler, Formatter
      from .handlers import DBHandler
      
      basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
                  datefmt='%d-%b-%y %H:%M:%S',
                  level=DEBUG)
      format = Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
      
      backup_logger = getLogger('backup_logger')
      file_handler = FileHandler('file.log')
      file_handler.setLevel(DEBUG)
      file_handler.setFormatter(format)
      backup_logger.addHandler(file_handler)
      
      db_logger = getLogger('logger')
      db_handler = DBHandler(backup_logger_name='backup_logger')
      db_handler.setLevel(DEBUG)
      db_handler.setFormatter(format)
      db_logger.addHandler(db_handler)
      
      if __name__ == "__main__":
          db_logger.debug('debug: hello world!')
          db_logger.info('info: hello world!')
          db_logger.warning('warning: hello world!')
          db_logger.error('error: hello world!')
          db_logger.critical('critical: hello world!!!!')
      

      您可以看到处理程序接受一个备份记录器,当数据库插入失败时可以使用它。

      一个很好的改进可以是通过线程登录到数据库。

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多