Django 1.2标准日志模块出现奇怪行为时的解决方案

在 Django 1.2 中,标准日志模块有时会出现意想不到的行为,例如日志消息未按预期记录、日志级别未正确应用或日志格式错乱等。这些问题可能源于日志配置不当、日志模块被多次初始化、或日志模块被其他包覆盖等原因。下面是一些常见问题的排查方法和解决方案。

1、问题背景

在 Django 1.2 中,使用标准日志模块记录信息时遇到了一个奇怪的问题。有时候它可以正常工作,而有时候它却无法记录信息。

2、解决方案

为了解决这个问题,我们采取了以下步骤:

  1. 首先,我们检查了代码结构。代码结构如下:

    /mysite/ (Django root)
    my_logging.py (logging configuration)
    settings.py
    views.py (global views)
    data_objects.py (objects only containing data, similar to POJO)
    uploader/ (application)
    views.py (uploader views) --> This is where I have problems

  2. 接着,我们检查了 my_logging.py 的代码:

python 复制代码
import logging
import logging.handlers
from django.conf import settings

is_initialized = False

def init_logger():
    """
    Initializes the logging for the application. Configure the root
    logger and creates the handlers following the settings. This function should
    not be used directly from outside the module and called only once.
    """
    # Create the logger
    server_logger = logging.getLogger()
    server_logger.setLevel(logging.DEBUG)

     # Set the logging format for files
    files_formatter = logging.Formatter(settings.LOGGING_FORMAT_FILE)

     # Rotating file handler for errors
    error_handler = logging.handlers.RotatingFileHandler(
        settings.LOGGING_ERROR_FILE,
        maxBytes=settings.LOGGING_ERROR_FILE_SIZE,
        backupCount=settings.LOGGING_ERROR_FILE_COUNT,
    )
    error_handler.setLevel(logging.WARNING)
    error_handler.setFormatter(files_formatter)

    # Rotating file handler for info
    info_handler = logging.handlers.RotatingFileHandler(
        settings.LOGGING_INFO_FILE,
        maxBytes=settings.LOGGING_INFO_FILE_SIZE,
        backupCount=settings.LOGGING_INFO_FILE_COUNT,
    )
    info_handler.setLevel(logging.INFO)
    info_handler.setFormatter(files_formatter)

    # Add the handlers to the logger
    server_logger.addHandler(info_handler)
    server_logger.addHandler(error_handler)

# Init once at first import
if not is_initialized:
    init_logger()
    is_initialized = True
  1. 然后,我们检查了 uploader/views.py 中的部分代码:
python 复制代码
#...
import mysite.my_logging
import logging
#... 
# The messages in the following view are written correctly :
@login_required
def delete(request, file_id):
    """
    Delete the file corresponding to the given ID and confirm the deletion to
    the user.

    @param request: the HTTP request object
    @type request: django.http.HttpRequest
    @return: django.http.HttpResponse - the response to the client (html)
    """
    # Get the file object form the database and raise a 404 if not found
    f = get_object_or_404(VideoFile, pk=file_id)

    # TODO: check if the deletion is successful

    # Get the video directory
    dir_path = os.path.dirname(f.file.path)

    # Delete the file
    f.delete()

    try:
        # Delete the video directory recursively
        shutil.rmtree(dir_path)
        logging.info("File \"%(file)s\" and its directory have been deleted by %(username)s",{'file': f.title,'username': request.user.username})
        messages.success(request, _('The video file "%s" has been successfully deleted.') % f.title)
    except OSError:
        logging.warning("File \"%(id)d\" directory cannot be completely deleted. Some files may still be there.",{'id': f.id,})
        messages.warning(request, _("The video file \"%s\" has been successfully deleted, but not its directory. There should not be any problem but useless disk usage.") % f.title)

    return HttpResponseRedirect(reverse('mysite.uploader.views.list'))
#...
# The messages in the following view are NOT written at all:
@csrf_exempt
def get_thumblist(request,file_id):
    """
    This view can be called only by POST and with the id of a video
    file ready for the scene editor.

    @param request: the HTTP request object. Must have POST as method.
    @type request: django.http.HttpRequest
    @return: django.http.HttpResponse - the response to the client (json)
    """
    #TODO: Security, TEST

    logging.info("Demand of metadata for file %(id)d received.",{'id': file_id,})

    if request.method == 'POST':

        if file_id:

            # Get the video file object form the database and raise a 404 if not found
            vid = get_object_or_404(VideoFile, pk=file_id)

                # ...

                try:
                    # ... file operations
                except IOError:
                    logging.error("Error when trying to read index file for file %(id)d !",{'id': file_id,})
                except TypeError:
                    logging.error("Error when trying to parse index file JSON for file %(id)d !",{'id': file_id,})

                # ...

                logging.info("Returning metadata for file %(id)d.",{'id': file_id,})
                return HttpResponse(json,content_type="application/json")

            else:
                logging.warning("File %(id)d is not ready",{'id': file_id,})
                return HttpResponseBadRequest('file_not_ready')
        else:
            logging.warning("bad POST parameters")
            return HttpResponseBadRequest('bad_parameters')
    else:
        logging.warning("The GET method is not allowed")
        return HttpResponseNotAllowed(['POST'])
  1. 最后,我们检查了 settings.py 中的部分代码:
python 复制代码
# ---------------------------------------
# Logging settings
# ---------------------------------------

#: Minimum level for logging messages. If logging.NOTSET, logging is disabled
LOGGING_MIN_LEVEL = logging.DEBUG

#: Error logging file path. Can be relative to the root of the project or absolute.
LOGGING_ERROR_FILE = os.path.join(DIRNAME,"log/error.log")

#: Size (in bytes) of the error files
LOGGING_ERROR_FILE_SIZE = 10485760 # 10 MiB

#: Number of backup error logging files
LOGGING_ERROR_FILE_COUNT = 5

#: Info logging file path. Can be relative to the root of the project or absolute.
LOGGING_INFO_FILE = os.path.join(DIRNAME,"log/info.log")

#: Size (in bytes) of the info files
LOGGING_INFO_FILE_SIZE = 10485760 # 10 MiB

#: Number of backup error info files
LOGGING_INFO_FILE_COUNT = 5

#: Format for the log files
LOGGING_FORMAT_FILE = "%(asctime)s:%(name)s:%(levelname)s:%(message)s"
  1. 通过对以上代码的检查,我们发现问题出现在 uploader/views.py 中的 get_thumblist 函数中。该函数中使用 logging.info('Demand of metadata for file %(id)d received.') 语句记录信息,但由于没有使用 logger 对象,导致信息没有被记录下来。

  2. 为了解决这个问题,我们将 get_thumblist 函数中的 logging.info('Demand of metadata for file %(id)d received.') 语句改为 logger.info('Demand of metadata for file %(id)d received.'),其中 logger 是一个 logging.getLogger() 函数返回的日志对象。

  3. 修改后的代码如下:

python 复制代码
#...
import mysite.my_logging
import logging
logger = logging.getLogger('MySite.views')
#... 
# The messages in the following view are written correctly :
@login_required
def delete(request, file_id):
    """
    Delete the file corresponding to the given ID and confirm the deletion to
    the user.

    @param request: the HTTP request object
    @type request: django.http.HttpRequest
    @return: django.http.HttpResponse - the response to the client (html)
    """
    # Get the file object form the database and raise a 404 if not found
    f = get_object_or_404(VideoFile, pk=file_id)

    # TODO: check if the deletion is successful

    # Get the video directory
    dir_path = os.path.dirname(f.file

以上方法可以帮助解决 Django 1.2 中标准日志模块的异常行为问题。通过合理配置和调整日志模块,可以确保日志记录功能稳定、可靠地运行。

相关推荐
极限实验室4 小时前
APM(一):Skywalking 与 Easyearch 集成
数据库·云原生
饕餮争锋4 小时前
SQL条件中WHERE 1=1 的功能
数据库·sql
玄斎5 小时前
MySQL 单表操作通关指南:建库 / 建表 / 插入 / 增删改查
运维·服务器·数据库·学习·程序人生·mysql·oracle
编织幻境的妖5 小时前
SQL查询连续登录用户方法详解
java·数据库·sql
编程小Y6 小时前
MySQL 与 MCP 集成全解析(核心原理 + 实战步骤 + 应用场景)
数据库·mysql·adb
零度@6 小时前
SQL 调优全解:从 20 秒到 200 ms 的 6 步实战笔记(附脚本)
数据库·笔记·sql
Miss_Chenzr6 小时前
Springboot优卖电商系统s7zmj(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端
lvbinemail7 小时前
Grafana模板自动复制图表
数据库·mysql·zabbix·grafana·监控
Miss_Chenzr7 小时前
Springboot旅游景区管理系统9fu3n(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·旅游
小虾米vivian7 小时前
dmetl5 运行失败,提示违反协议?
数据库·达梦数据库