问题 Django保存了整个统计请求,可用吗?


我想保存可用于统计的所有内容,例如引用,操作系统,浏览器等。什么是可用的以及存储它的最佳方式是什么?

这仅对项目中的1个应用程序(1页)很重要,其他页面将使用一些标准分析产品,例如Google Analytics。

我看了一下django-tracking,但看起来这有点过分,因为我只想在1个视图上使用它。理想的情况是,将整个请求对象传递给TaskQue并稍后进行处理。因此,用户首先被重定向,分析处理将在幕后完成。


3249
2018-04-10 05:11


起源



答案:


我们使用一些简单的中间件..下面是摘录。您可以将其修改为直接在视图中使用。

class WebRequest(models.Model):
    time = models.DateTimeField(auto_now_add=True)
    host = models.CharField(max_length=1000)
    path = models.CharField(max_length=1000)
    method = models.CharField(max_length=50)
    uri = models.CharField(max_length=2000)
    status_code = models.IntegerField()
    user_agent = models.CharField(max_length=1000,blank=True,null=True)
    remote_addr = models.IPAddressField()
    remote_addr_fwd = models.IPAddressField(blank=True,null=True)
    meta = models.TextField()
    cookies = models.TextField(blank=True,null=True)
    get = models.TextField(blank=True,null=True)
    post = models.TextField(blank=True,null=True)
    raw_post = models.TextField(blank=True,null=True)
    is_secure = models.BooleanField()
    is_ajax = models.BooleanField()
    user = models.ForeignKey(User,blank=True,null=True)

def dumps(value):
    return json.dumps(value,default=lambda o:None)

class WebRequestMiddleware(object):
    def process_view(self, request, view_func, view_args, view_kwargs):
        setattr(request,'hide_post',view_kwargs.pop('hide_post',False))


    def process_response(self, request, response):

        if request.path.endswith('/favicon.ico'):
            return response

        if type(response) == HttpResponsePermanentRedirect and settings.APPEND_SLASH:
            new_location = response.get('location',None)
            content_length = response.get('content-length',None)

            if new_location and content_length is '0':
                new_parsed = urlparse(new_location)

                old = (('http','https')[request.is_secure()], request.get_host(), '{0}/'.format(request.path), request.META['QUERY_STRING'])
                new = (new_parsed.scheme, new_parsed.netloc, new_parsed.path, new_parsed.query)

                if old == new:
                    #dont log - it's just adding a /
                    return response
        try:
            self.save(request, response)
        except Exception as e:
            print >> sys.stderr, "Error saving request log", e

        return response

    def save(self, request, response):
        if hasattr(request, 'user'):
            user = request.user if type(request.user) == User else None
        else:
            user = None

        meta = request.META.copy()
        meta.pop('QUERY_STRING',None)
        meta.pop('HTTP_COOKIE',None)
        remote_addr_fwd = None

        if 'HTTP_X_FORWARDED_FOR' in meta:
            remote_addr_fwd = meta['HTTP_X_FORWARDED_FOR'].split(",")[0].strip()
            if remote_addr_fwd == meta['HTTP_X_FORWARDED_FOR']:
                meta.pop('HTTP_X_FORWARDED_FOR')

        post = None
        uri = request.build_absolute_uri()
        if request.POST and uri != '/login/':
            post = dumps(request.POST)

        models.WebRequest(
            host = request.get_host(),
            path = request.path,
            method = request.method,
            uri = request.build_absolute_uri(),
            status_code = response.status_code,
            user_agent = meta.pop('HTTP_USER_AGENT',None),
            remote_addr = meta.pop('REMOTE_ADDR',None),
            remote_addr_fwd = remote_addr_fwd,
            meta = None if not meta else dumps(meta),
            cookies = None if not request.COOKIES else dumps(request.COOKIES),
            get = None if not request.GET else dumps(request.GET),
            post = None if (not request.POST or getattr(request,'hide_post') == True) else dumps(request.POST),
            raw_post = None if getattr(request,'hide_post') else request.raw_post_data,
            is_secure = request.is_secure(),
            is_ajax = request.is_ajax(),
            user = user
        ).save()

12
2018-04-10 05:17



谢谢这是令人惊讶的代码。会救我很多工作!关于模型的一个问题。你在使用MySQL吗?这会创建VARCHAR(1000)吗?刚看了一下这里的文档: docs.djangoproject.com/en/dev/ref/databases/... - Sam Stoelinga
我们实际上使用的是Oracle,因此TextField(如果这就是你所指的)是一个NLOB。 - Josh Smeaton
我指的是host = models.CharField(max_length = 1000)。你为什么要明确地复制META? meta = request.META.copy() - Sam Stoelinga
我没有写这个(我的同事做了),但副本会发生,因为META正在修改 .pop()。您不希望对实际的META dict执行此操作,以后某些内容需要这些字段。长度= 1000也太大了。你可以显着降低这一点。 - Josh Smeaton
我应该在哪里写这段代码? - Hardik Gajjar


答案:


我们使用一些简单的中间件..下面是摘录。您可以将其修改为直接在视图中使用。

class WebRequest(models.Model):
    time = models.DateTimeField(auto_now_add=True)
    host = models.CharField(max_length=1000)
    path = models.CharField(max_length=1000)
    method = models.CharField(max_length=50)
    uri = models.CharField(max_length=2000)
    status_code = models.IntegerField()
    user_agent = models.CharField(max_length=1000,blank=True,null=True)
    remote_addr = models.IPAddressField()
    remote_addr_fwd = models.IPAddressField(blank=True,null=True)
    meta = models.TextField()
    cookies = models.TextField(blank=True,null=True)
    get = models.TextField(blank=True,null=True)
    post = models.TextField(blank=True,null=True)
    raw_post = models.TextField(blank=True,null=True)
    is_secure = models.BooleanField()
    is_ajax = models.BooleanField()
    user = models.ForeignKey(User,blank=True,null=True)

def dumps(value):
    return json.dumps(value,default=lambda o:None)

class WebRequestMiddleware(object):
    def process_view(self, request, view_func, view_args, view_kwargs):
        setattr(request,'hide_post',view_kwargs.pop('hide_post',False))


    def process_response(self, request, response):

        if request.path.endswith('/favicon.ico'):
            return response

        if type(response) == HttpResponsePermanentRedirect and settings.APPEND_SLASH:
            new_location = response.get('location',None)
            content_length = response.get('content-length',None)

            if new_location and content_length is '0':
                new_parsed = urlparse(new_location)

                old = (('http','https')[request.is_secure()], request.get_host(), '{0}/'.format(request.path), request.META['QUERY_STRING'])
                new = (new_parsed.scheme, new_parsed.netloc, new_parsed.path, new_parsed.query)

                if old == new:
                    #dont log - it's just adding a /
                    return response
        try:
            self.save(request, response)
        except Exception as e:
            print >> sys.stderr, "Error saving request log", e

        return response

    def save(self, request, response):
        if hasattr(request, 'user'):
            user = request.user if type(request.user) == User else None
        else:
            user = None

        meta = request.META.copy()
        meta.pop('QUERY_STRING',None)
        meta.pop('HTTP_COOKIE',None)
        remote_addr_fwd = None

        if 'HTTP_X_FORWARDED_FOR' in meta:
            remote_addr_fwd = meta['HTTP_X_FORWARDED_FOR'].split(",")[0].strip()
            if remote_addr_fwd == meta['HTTP_X_FORWARDED_FOR']:
                meta.pop('HTTP_X_FORWARDED_FOR')

        post = None
        uri = request.build_absolute_uri()
        if request.POST and uri != '/login/':
            post = dumps(request.POST)

        models.WebRequest(
            host = request.get_host(),
            path = request.path,
            method = request.method,
            uri = request.build_absolute_uri(),
            status_code = response.status_code,
            user_agent = meta.pop('HTTP_USER_AGENT',None),
            remote_addr = meta.pop('REMOTE_ADDR',None),
            remote_addr_fwd = remote_addr_fwd,
            meta = None if not meta else dumps(meta),
            cookies = None if not request.COOKIES else dumps(request.COOKIES),
            get = None if not request.GET else dumps(request.GET),
            post = None if (not request.POST or getattr(request,'hide_post') == True) else dumps(request.POST),
            raw_post = None if getattr(request,'hide_post') else request.raw_post_data,
            is_secure = request.is_secure(),
            is_ajax = request.is_ajax(),
            user = user
        ).save()

12
2018-04-10 05:17



谢谢这是令人惊讶的代码。会救我很多工作!关于模型的一个问题。你在使用MySQL吗?这会创建VARCHAR(1000)吗?刚看了一下这里的文档: docs.djangoproject.com/en/dev/ref/databases/... - Sam Stoelinga
我们实际上使用的是Oracle,因此TextField(如果这就是你所指的)是一个NLOB。 - Josh Smeaton
我指的是host = models.CharField(max_length = 1000)。你为什么要明确地复制META? meta = request.META.copy() - Sam Stoelinga
我没有写这个(我的同事做了),但副本会发生,因为META正在修改 .pop()。您不希望对实际的META dict执行此操作,以后某些内容需要这些字段。长度= 1000也太大了。你可以显着降低这一点。 - Josh Smeaton
我应该在哪里写这段代码? - Hardik Gajjar


只需从请求中手动拉出即可。

文档概述了可以从请求对象中提取的大量信息。

例如,标题存储在 request.META,请求GET参数.GET等
http://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.META

存储它的最佳方法是什么?取决于你在做什么。记录它,将它存储在数据库中,然后将它发送到其他地方......你说的是统计数据,所以数据库听起来像是一个放置它的好地方,因为它很容易查询。


1
2018-04-10 05:18



哪个数据库MySQL或MongoDB作为数据将是巨大的?还是多个MySQL数据库,一个专门用于存储请求数据? - Ashish Gupta


扩展到Josh的答案,如果你使用postgres作为你的后端,你可以使用JSONField作为发布数据。它将有助于直接处理json而不是手动加载它。

阅读更多: https://docs.djangoproject.com/en/2.0/ref/contrib/postgres/fields/#jsonfield

你可以做这样的事情

from django.contrib.postgres.fields import JSONField

class WebRequest(models.Model):
    post = JSONField(default=dict)

0
2017-12-25 05:00