Python 日志 HTTPHandler 在 Django 中的断管错误

0 投票
1 回答
1767 浏览
提问于 2025-04-16 12:48

我在用Python的HTTPHandler把信息发送到一个客户的Django应用时遇到了一些问题。我有一个单独的守护进程,它是我基础设施的一部分,我希望它能把日志推送到Django,这样我就能把所有东西放在一个地方。

我使用的环境是:

  • Ubuntu 10.04
  • Django 1.2.4
  • PostgreSQL 8.4
  • Python 2.6.5

这是模型:

from django.db import models

# Create your models here.


class application(models.Model):
    app_name = models.CharField(max_length= 20)
    description = models.CharField(max_length = 500, null=True)
    date = models.DateField()

    def __unicode__(self):
        return ("%s logs - %s") % (self.app_name, self.date.strftime("%d-%m-%Y"))

class log_entry(models.Model):
    application = models.ForeignKey(application)
    thread_name = models.CharField(max_length = 200,null = True)
    name = models.CharField(max_length = 200,null = True)
    thread = models.CharField(max_length=50, null = True)
    created = models.FloatField(null = True)
    process = models.IntegerField(null = True)
    args = models.CharField(max_length = 200,null = True)
    module = models.CharField(max_length = 256,null = True)
    filename = models.CharField(max_length = 256,null = True)
    levelno = models.IntegerField(null = True)
    msg = models.CharField(max_length = 4096,null = True)
    pathname = models.CharField(max_length = 1024,null = True)
    lineno = models.IntegerField(null = True)
    exc_text = models.CharField(max_length = 200, null = True)
    exc_info = models.CharField(max_length = 200, null = True)
    func_name = models.CharField(max_length = 200, null = True)
    relative_created = models.FloatField(null = True)
    levelname = models.CharField(max_length=10,null = True)
    msecs = models.FloatField(null = True)

    def __unicode__(self):
        return self.levelname + " - " + self.msg

这是视图:

# Create your views here.
from django.shortcuts import render_to_response, get_list_or_404, get_object_or_404
from django.http import HttpResponse, HttpResponseRedirect
from django.views.decorators.csrf import csrf_protect, csrf_exempt

from inthebackgroundSite.log.models import log_entry, application
import datetime


@csrf_exempt
def log(request):
    print request.POST

    for element in request.POST:
        print ('%s : %s') % (element, request.POST[element])

    data = request.POST
    today = datetime.date.today()
    print today
    app = application.objects.filter(app_name__iexact = request.POST["name"], date__iexact=today)
    if not app:
        print "didnt find a matching application. adding one now.."
        print data["name"]
        print today
        app = application.objects.create(app_name = data["name"],
                                         description = None,
                                         date = today)
        app.save()
        if not app:
            print "after save you cant get at it!"
        newApplication = app

    print app
    print "found application"
    newEntry = log_entry.objects.create(application = app,
                                        thread_name = data["threadName"] ,
                                        name = data["name"],
                                        thread = data["thread"],
                                        created = data["created"],
                                        process = data["process"],
                                        args = "'" + data["args"] + "'",
                                        module = data["module"],
                                        filename = data["filename"],
                                        levelno = data["levelno"],
                                        msg = data["msg"],
                                        pathname = data["pathname"],
                                        lineno = data["lineno"],
                                        exc_text = data["exc_text"],
                                        exc_info = data["exc_info"],
                                        func_name = data["funcName"],
                                        relative_created = data["relativeCreated"],
                                        levelname = data["levelname"],
                                        msecs = data["msecs"],
                                        )
    print newEntry
    #newEntry.save()
    return HttpResponse("OK")

这是在Python代码中发送消息的调用:

import os

import logging
import logging.handlers
import time

if __name__ == '__main__':
    formatter = logging.Formatter("%(name)s %(levelno)s %(levelname)s %(pathname)s %(filename)s%(module)s %(funcName)s %(lineno)d %(created)f %(asctime)s %(msecs)d %(thread)d %(threadName)s %(process)d %(processName)s %(message)s ")
    log = logging.getLogger("ShoutGen")
    #logLevel = "debug"
    #log.setLevel(logLevel)    
    http = logging.handlers.HTTPHandler("192.168.0.5:9000", "/log/","POST")
    http.setFormatter(formatter)
    log.addHandler(http)

    log.critical("Finished MountGen init")
    time.sleep(20)    
    http.close()

第一次我发送消息时,表是空的,运行得很好,创建了一个新的应用行和一条新的日志消息。但是第二次调用时,我却遇到了:

<QueryDict: {u'msecs': [u'224.281072617'], u'args': [u'()'], u'name': [u'ShoutGen'], u'thread': [u'140445579720448'], u'created': [u'1299046203.22'], u'process': [u'16172'], u'threadName': [u'MainThread'], u'module': [u'logtest'], u'filename': [u'logtest.py'], u'levelno': [u'50'], u'processName': [u'MainProcess'], u'pathname': [u'logtest.py'], u'lineno': [u'19'], u'exc_text': [u'None'], u'exc_info': [u'None'], u'funcName': [u'<module>'], u'relativeCreated': [u'7.23600387573'], u'levelname': [u'CRITICAL'], u'msg': [u'Finished MountGen init']}>
msecs : 224.281072617
args : ()
name : ShoutGen
thread : 140445579720448
created : 1299046203.22
process : 16172
threadName : MainThread
module : logtest
filename : logtest.py
levelno : 50
processName : MainProcess
pathname : logtest.py
lineno : 19
exc_text : None
exc_info : None
funcName : <module>
relativeCreated : 7.23600387573
levelname : CRITICAL
msg : Finished MountGen init
2011-03-02
    [sql] SELECT ...
      FROM "log_application"
      WHERE (UPPER("log_application"."date"::text) = UPPER(2011-03-02)
             AND UPPER("log_application"."app_name"::text) = UPPER(ShoutGen))
    [sql] (5.10ms) Found 1 matching rows
[<application: ShoutGen logs - 02-03-2011>]
found application
    [sql] SELECT ...
      FROM "log_log_entry" LIMIT 21
    [sql] (4.05ms) Found 2 matching rows
    [sql] (9.14ms) 2 queries with 0 duplicates
    [profile] Total time to render was 0.44s
Traceback (most recent call last):
  File "/usr/local/lib/python2.6/dist-packages/django/core/servers/basehttp.py", line 281, in run
    self.finish_response()
  File "/usr/local/lib/python2.6/dist-packages/django/core/servers/basehttp.py", line 321, in finish_response
    self.write(data)
  File "/usr/local/lib/python2.6/dist-packages/django/core/servers/basehttp.py", line 417, in write
    self._write(data)
  File "/usr/lib/python2.6/socket.py", line 300, in write
    self.flush()
  File "/usr/lib/python2.6/socket.py", line 286, in flush
    self._sock.sendall(buffer)
error: [Errno 32] Broken pipe

而且在log_log_entry表中没有插入额外的行。所以我现在真的不知道为什么会发生这种情况。

我查了一下,似乎“Broken pipe traceback”并不是问题,只是浏览器会出现的情况。但我并没有使用浏览器,所以不太确定问题出在哪里。

1 个回答

0

可能是因为出现了异常,导致事务回滚,从而撤销了你的更改。你有没有使用 TransactionMiddleware?你可以试试在你的视图上加上 transaction.autocommit 装饰器。

如果“断管”错误一直出现,那就值得找找原因。HTTPHandler 会正常发送一个 POST 请求,并在 emit() 调用中等待响应(也就是你视图返回的“OK”),在这之前连接不应该断开。

你可以尝试用一个测试脚本向你的视图发送一个类似的 POST 请求,使用 httpliburllib,就像 HTTPHandler 自己做的那样。基本上,就是把一个字典进行 URL 编码,作为 POST 数据,就像处理 LogRecord 的字典一样。

撰写回答