Django2.0日志服务(logging)结合阿里云日志

公司Django项目的log之前都是写到项目下面的特定文件,然后通过url下载日志文件,再进行分析。现在是通过logging模块直接连接到阿里云日志服务,可以实时直观的访问日志信息,还可以进行各种查询分析。

一、Django中使用日志模块。

1)模块介绍:

主要包括以下四个组件:

Loggers 向应用程序提供log接口

Handlers 将log记录发送到指定的目的地(控制台输出,或写入文件或发向网络等等)

Filters 提供一个分级策略控制 log 输出等级

Formatters 指定输出的最终格式

记录器:

 记录器负责管理日志消息的默认行为,包括日志记录级别、输出目标位置、消息格式以及其它基本细节。

关键字参数描述
filename将日志消息附加到指定文件名的文件
filemode指定用于打开文件模式
format用于生成日志消息的格式字符串
datefmt用于输出日期和时间的格式字符串
level设置记录器的级别
stream提供打开的文件,用于把日志消息发送到文件。

每一个记录器都会有一个日志等级,每个等级描述了记录器即将处理的信息的严重性,python定义了以下五个等级:

debug:出于调试目的的低层次系统信息

info:普通的系统信息列表内容

warning:描述已经发生的小问题

error:描述已经发生的主要问题

critical:描述已经发生的严重问题

级别描述
CRITICAL50关键错误/消息
ERROR40错误
WARNING30警告消息
INFO20通知消息
DEBUG10调试

日志等级关系:CRITICAL>ERROR>WARNING>INFO>DEBUG

format日志消息格式

格式 描述
%(name)s  关键错误/消息
%(levelno)s   数字形式的日志记录级别
%(levelname)s 日志记录级别的文本名称
%(filename)s 执行日志记录调用的源文件的文件名称
%(pathname)s执行日志记录调用的源文件的路径名称
%(funcName)s 执行日志记录调用的函数名称
%(module)s 执行日志记录调用的模块名称
%(lineno)s  执行日志记录调用的行号
%(created)s 执行日志记录的时间
%(asctime)s日期和时间
%(thread)d 线程ID
%(msecs)s  毫秒部分
%(threadName)s    线程名称
%(process)d  进程ID
%(message)s 记录的消息

详细信息可以参考Django2.0官方文档:日志 | Django 文档 | Django,目前关于logging部分是英文的,可以参考Django1.8.2官方文档:https://yiyibooks.cn/xx/django_182/topics/logging.html

注意:Django1.8.2和2.0关于日志模块是有区别的,django2.0控制台默认不输出debug信息,详细设置下面会有说明。

2)Django项目中运用

在settings.py文件中定义logging格式。

# 日志配置
DIRNAME = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
LOGDIR = os.path.join(DIRNAME, "log")
if not os.path.exists(LOGDIR):
    os.makedirs(LOGDIR)  # 创建路径

LOGGING = {
    'version': 1,  # 指明dictConnfig的版本,目前就只有一个版本
    'disable_existing_loggers': False,  # 如果为True,默认的配置将被禁用,默认logger中的日志将会被丢弃且不传递到父logger,慎用True
    'filters': {  # 过滤器
    },
    'formatters': {  # 格式器
        'standard': {
            'format': '%(asctime)s [%(threadName)s:%(thread)d] [%(name)s:%(lineno)d] [%(levelname)s]- %(message)s'
        },
        'django.server': {  # django2.0默认是没有控制台输出信息的,自定义输出规则
            '()': 'django.utils.log.ServerFormatter',
            'format': '[%(asctime)s] %(message)s',
        }
    },
    'handlers': {  # 处理器
        'default': {  # 所有高于(包括)debug的消息会被传到default
            'level': 'DEBUG',
            'formatter': 'standard',  # 使用哪种formatters日志格式
            'class': 'logging.handlers.RotatingFileHandler',  # 如果输出到指定文件
            'filename': os.path.join(LOGDIR, 'app.log'),  # 输出文件(或者直接写路径:'c:\logs\all.log')
            'mode': 'w+',
            'maxBytes': 1024 * 1024 * 5,  # 文件最大5 MB
            'backupCount': 5,  # 备份数量
        },
        'console': {  # 流处理器,所有的高于(包括)debug的消息会被传到stderr,使用的是simple格式器
            'level': 'INFO',
            'formatter': 'standard',
            'class': 'logging.StreamHandler',
        },
        'django.server': {  # Django2.0特有处理器,输出高于info级别日志到控制台,如果不设置,控制台不会输出请求信息
            'level': 'INFO',
            'formatter': 'django.server',
            'class': 'logging.StreamHandler',
        },
    },
    'loggers': {  # 定义了三个记录器  ***注意:loggers的level的级别一定要大于handlers的级别,否则handlers会忽略掉该信息的。
        'django': {  # 使用default处理器,所有高于(包括)info的消息会被发往default处理器,不向父层次传递信息
            'handlers': ['default'],
            'level': 'ERROR',
            'propagate': False  # 是否向父层次传递信息
        },
        'django.server': {
            'handlers': ['django.server'],
            'level': 'ERROR',
            'propagate': False,
        },
        'django.request': {
            'handlers': ['console'],
            'level': 'ERROR',
            'propagate': False
        }
    },
    "root": {
        'handlers': ['default', 'console'],  # 生效的handlers,如果不写入此列表,即使上面定义了,也不会生效
        'level': "INFO",
        'propagate': False
    }
}

视图函数中使用logging:

# coding:utf-8

import logging
from django.shortcuts import render


log = logging.getLogger(__name__)


def test(request):
    try:
        if request.method == "GET":
            user = request.user
    except:
        log.exception("测试信息")

        return render(request, 'test.html')

日志生成,在settings.py文件里面指定的地址中找到写入的日志文件:

二、Django日志结合阿里云日志进行实时分析

首先附上阿里云日志服务文档:日志服务 - 帮助中心 - 阿里云

按照文档提示创建Project,logstore,注意要给账户日志服务权限。

以下是django,settings部分代码。

# 日志配置
DIRNAME = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
LOGDIR = os.path.join(DIRNAME, "log")
if not os.path.exists(LOGDIR):
    os.makedirs(LOGDIR)  # 创建路径

LOGGING = {
    'version': 1,  # 指明dictConnfig的版本,目前就只有一个版本
    'disable_existing_loggers': False,  # 如果为True,默认的配置将被禁用,默认logger中的日志将会被丢弃且不传递到父logger,慎用True
    'filters': {  # 过滤器
        'aliyun_filter': {  # 阿里云日志专用
            '()': 'yh_sls.SlsFilter',
        },
    },
    'formatters': {  # 格式器
        'standard': {
            'format': '%(asctime)s [%(threadName)s:%(thread)d] [%(name)s:%(lineno)d] [%(levelname)s]- %(message)s'
        },
        'django.server': {  # django2.0默认是没有控制台输出信息的,自定义输出规则
            '()': 'django.utils.log.ServerFormatter',
            'format': '[%(asctime)s] %(message)s',
        }
    },
    'handlers': {  # 处理器
        'default': {  # 所有高于(包括)debug的消息会被传到default
            'level': 'DEBUG',
            'formatter': 'standard',  # 使用哪种formatters日志格式
            'class': 'logging.handlers.RotatingFileHandler',  # 如果输出到指定文件
            'filename': os.path.join(LOGDIR, 'app.log'),  # 输出文件(或者直接写路径:'c:\logs\all.log')
            'mode': 'w+',
            'maxBytes': 1024 * 1024 * 5,  # 文件最大5 MB
            'backupCount': 5,  # 备份数量
        },
        'console': {  # 流处理器,所有的高于(包括)debug的消息会被传到stderr,使用的是simple格式器
            'level': 'INFO',
            'formatter': 'standard',
            'class': 'logging.StreamHandler',
        },
        'django.server': {  # Django2.0特有处理器,输出高于info级别日志到控制台
            'level': 'INFO',
            'formatter': 'django.server',
            'class': 'logging.StreamHandler',
        },
        'aliyun_handler': {
            'level': 'INFO',
            'filters': ['aliyun_filter'],
            'class': 'aliyun.log.QueuedLogHandler',  # 专有类,上传至阿里云
            # 'class': 'aliyun.log.SimpleLogHandler',   # 支持uwsgi,极端情况下测试使用,不推荐生成环境使用
            'access_key_id': ALI_SLS_ACCESSID,  # access_key_id
            'access_key': ALI_SLS_ACCESSKEY,  # access_key
            'end_point': ALI_SLS_ENDPOINT,  # end_point
            'project': ALI_SLS_PROJECT,  # project
            'log_store': ALI_SLS_STORE,  # log_store
            'topic': ALI_SLS_TOPIC,  # log_store
            'extract_json': True,
            'extract_json_drop_message': True

        },
    },
    'loggers': {  # 定义了三个记录器  ***注意:loggers的level的级别一定要大于handlers的级别,否则handlers会忽略掉该信息的。
        'django': {  # 使用default处理器,所有高于(包括)info的消息会被发往default处理器,不向父层次传递信息
            'handlers': ['default', 'aliyun_filter'],
            'level': 'ERROR',
            'propagate': False  # 是否向父层次传递信息
        },
        'django.server': {
            'handlers': ['django.server', 'aliyun_filter'],
            'level': 'ERROR',
            'propagate': False,
        },
        'django.request': {
            'handlers': ['console', 'aliyun_filter'],
            'level': 'ERROR',
            'propagate': False
        }
    },
    "root": {
        'handlers': ['default', 'console', 'aliyun_filter'],
        'level': "INFO",
        'propagate': False
    }
}

以下是自定义封装filter,yh_sls.SlsFilter

# coding: utf-8
import logging
import sys



log = logging.getLogger(__name__)


class SlsFilter(logging.Filter):
    """
    功能说明:自定义logging.Filter,增加request部分信息到日志字段,阿里云日志专用,示例如下:
   
    LOGGING = {
        'version': 1,
        'disable_existing_loggers': False,
        'filters': {
            'aliyun_filter': {                                  # 必须
                '()': 'yh_sls.SlsFilter',
            },
        },
        'handlers': {
            'aliyun_handler': {
                'level': 'INFO',
                'filters': ['aliyun_filter'],                   # 必须
                'class': 'aliyun.log.SimpleLogHandler',
                'access_key_id': ALI_SLS_ACCESSID,
                'access_key': ALI_SLS_ACCESSKEY,
                'end_point': ALI_SLS_ENDPOINT,
                'project': ALI_SLS_PROJECT,
                'log_store': ALI_SLS_STORE,
                'topic': ALI_SLS_TOPIC,
                'extract_json': True,                           # 必须
                'extract_json_drop_message': True               # 可选
            },
        },
        "root": {
            'handlers': ['aliyun_handler'],                     # 必须
            'level': "DEBUG",
            'propagate': False
        }
    }
     """
    def filter(self, record):
        try:
            extra = {}
            # request信息
            if hasattr(record, 'req'):
                from django.http import HttpRequest
                if isinstance(record.req, HttpRequest):
                    request = record.req
                    extra.update(get_request_env(request))

            extra['asctime'] = record.asctime

            if record.args:
                extra['msg'] = record.msg % record.args
            else:
                extra['msg'] = record.msg

            extra['exc_text'] = record.exc_text or ''
            exc_text_lines = extra['exc_text'].split("\n")
            extra['exc_text_last_line'] = exc_text_lines[-1] if exc_text_lines else ''

            record.msg = extra

            return True
        except:
            log.exception("")


def get_request_env(request):
    """获取django的request的部分信息"""
    extra = {}
    try:
        extra['req_path'] = request.path
        extra['req_method'] = request.method
        extra['req_full_path'] = request.get_full_path()

        if sys.version_info < (3, 5):
            extra['req_body'] = request.body
        else:
            extra['req_body'] = request.body.decode()

        if not hasattr(request, 'environ'):
            request.environ = {}
        extra['req_referer'] = request.environ.get('HTTP_REFERER', '')

        # 客户端ip
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR', '')
        if x_forwarded_for:
            remote_ip = x_forwarded_for.split(",")[0]
        else:
            remote_ip = request.META.get('REMOTE_ADDR', '')
        extra['req_client_ip'] = remote_ip

        # 用户id
        uid = ''
        if hasattr(request, 'uid'):
            uid = request.uid
        elif hasattr(request, 'user'):
            from django.db.models import Model
            if isinstance(request.user, Model):
                uid = request.user.id
        extra['req_uid'] = uid
    except:
        pass
    return extra

日志分析部分参考官方文档。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值