简体   繁体   English

Django 1.2:奇怪的日志记录行为

[英]Django 1.2 : strange logging behavior

I have a really strange problem with the standard logging module used in django views. 我在django视图中使用的标准日志记录模块有一个非常奇怪的问题。 Sometimes it works perfectly and sometimes it does not log messages. 有时它可以完美运行,而有时它不记录消息。

Here is the structure of my code : 这是我的代码的结构:

/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

Here is the code of my_logging.py : 这是my_logging.py的代码:

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

Here are parts of uploader/views.py (#... = code skipped): 以下是uploader / views.py的部分(#... =跳过代码):

#...
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'])

and the interesting part of settings.py: 还有settings.py有趣的部分:

# ---------------------------------------
# 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"

Note that except logging everything is working fine. 请注意,除了记录所有内容外,其他一切都正常。 The data can be returned correctly in JSON format. 数据可以JSON格式正确返回。 I think there is no error in the rest of the code. 我认为其余代码没有错误。

Please ask if you need more information. 请询问您是否需要更多信息。 I'm sorry about the code I removed, but I have to because of confidentiality. 对于我删除的代码感到抱歉,但是由于保密原因,我不得不这样做。

Instead of using the logging.info('My statement') syntax, I suggest you use something like the following: 建议您不要使用logging.info('My statement')语法,而应使用类似以下的内容:

import logging
logger = logging.getLogger('MySite')
logger.info('My statement')

That is, call your log statements against a logger object, instead of the logging module directly. 也就是说,针对记录器对象而不是直接通过记录模块调用日志语句。 Likewise, you'll have to tweak my_logging.py to configure that logger: 同样,您必须调整my_logging.py以配置该记录器:

# Create the logger
server_logger = logging.getLogger('MySite')
server_logger.setLevel(logging.DEBUG)

In your views, you can log against logging.getLogger('MySite') or logging.getLogger('MySite.views') , etc.. Any logger which starts with 'MySite' will inherit your configuration. 在您的视图中,您可以登录logging.getLogger('MySite')logging.getLogger('MySite.views')等。任何以'MySite'开头的记录器都将继承您的配置。

Also, while you have the right idea by setting and checking is_initialized , I don't believe that approach will work. 另外,尽管您通过设置和检查is_initialized拥有正确的想法,但我认为这种方法无效。 Each time my_logging.py is imported, that variable will be set to False, thus defeating its purpose. 每次导入my_logging.py时,该变量都将设置为False,从而违背了它的目的。 You can use the following in settings.py to ensure that logging is only configured once: 您可以在settings.py中使用以下命令,以确保仅配置一次日志记录:

# Init once at first import
if not hasattr(my_logging, 'is_initialized'):
    my_logging.is_initialized = False

if not my_logging.is_initialized:
    my_logging.init_logger()
    my_logging.is_initialized = True

I start all of my modules (except settings.py) with the following two lines: 我用以下两行启动所有模块(settings.py除外):

import logging
logging.getLogger('MySite.ModuleInit').debug('Initializing %s' % str(__name__))

If you are still having trouble, please add those lines and then post the module initialization order for your site. 如果仍然遇到问题,请添加这些行,然后发布站点的模块初始化顺序。 There may be a strange quirk based on the order of your imports. 根据您的进口顺序,可能会有一个奇怪的怪癖。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM