zoukankan      html  css  js  c++  java
  • Nginx之旅系列

    题记:Nginx之旅系列是用来记录Nginx从使用到源码学习的点点滴滴,分享学习Nginx的快乐

     

    Nginx 首页: http://nginx.org/

    Nginx日志功能 PK Linux内核printk


            本来只想分析一下Nginx中日志的实现,但是突发奇想,想把Nginx中的日志功能与Linux kernel中的printk进行一下横向对比,即学习了Nginx的日志功能,又总结了Linux的printk的实现,于是乎这么一篇博文就出现了。本文将从日志级别相关函数实现和日志函数使用的角度来梳理,byhankswang的初衷是使用尽量少的代码和文字说明白尽量多的事情。


    PK1:  Nginx日志级别和printk日志级别

            对与日志系统的设计,良好的日志系统应该提供丰富的日志级别和简单易用的API。丰富的日志级别对与开发人员的调试是非常有帮助的。不许要时尽可能的少显示不重要的日志,需要时又能提供丰富调试的信息,尽快的确定问题所在。日志系统对于一个产品来说不是核心功能但是是很重要的功能。

    1.1Nginx日志级别的定义

    // within file nginx-1.5.2/src/core/ngx_log.h

    #define NGX_LOG_STDERR            0
    #define NGX_LOG_EMERG              1
    #define NGX_LOG_ALERT               2
    #define NGX_LOG_CRIT                  3
    #define NGX_LOG_ERR                   4
    #define NGX_LOG_WARN               5
    #define NGX_LOG_NOTICE             6
    #define NGX_LOG_INFO                  7
    #define NGX_LOG_DEBUG              8

    //within file nginx-1.5.2/src/core/ngx_log.h
    #define NGX_LOG_DEBUG_CORE        0x010
    #define NGX_LOG_DEBUG_ALLOC       0x020
    #define NGX_LOG_DEBUG_MUTEX       0x040
    #define NGX_LOG_DEBUG_EVENT       0x080
    #define NGX_LOG_DEBUG_HTTP        0x100
    #define NGX_LOG_DEBUG_MAIL        0x200
    #define NGX_LOG_DEBUG_MYSQL       0x400


    1.2printk日志级别的定义

    //within file linux-3.10.1/include/linux/kern_levels.h

    #define KERN_SOH        "01"          /* ASCII Start Of Header */
    #define KERN_SOH_ASCII  '01'

    #define KERN_EMERG      KERN_SOH "0"    /* system is unusable */
    #define KERN_ALERT      KERN_SOH "1"    /* action must be taken immediately */
    #define KERN_CRIT       KERN_SOH "2"    /* critical conditions */
    #define KERN_ERR        KERN_SOH "3"    /* error conditions */
    #define KERN_WARNING    KERN_SOH "4"    /* warning conditions */
    #define KERN_NOTICE     KERN_SOH "5"    /* normal but significant condition */
    #define KERN_INFO       KERN_SOH "6"    /* informational */
    #define KERN_DEBUG      KERN_SOH "7"    /* debug-level messages */

    #define KERN_DEFAULT    KERN_SOH "d"    /* the default kernel loglevel */


    Nginx中关于日志的级别定义来9种,在ngx_log_init初始化的时候默认的级别是NGX_LOG_NOTICE,也就是说当日志级别高于NOTICE的时候都会被看到,而低于此级别的日志就会被忽略。Linux kernel中printk的默认级别是KERN_DEFAULT。


    PK2:Nginx日志函数实现和printk的实现

    本小节讲分析一下ngx_log_error, ngx_log_error_core, ngx_log_debug 和linux内核中printk的实现:

    2.1 Nginx的日志函数封装

    Nginx中日志相关的函数最重要的三个分别是ngx_log_error, ngx_log_error_core 和ngx_log_debug。其中ngx_log_error和ngx_log_debug又是对ngx_log_error_core的封装。封装的方式包括三种,区分了C99, GCC 和普通的可变参数方式。下面以GCC的可变参数方式说明具体问题,其他的两种与此类似。

    //within file ngnix-1.5.2/src/core/ngx_log.c

    #elif (NGX_HAVE_GCC_VARIADIC_MACROS)

    #define NGX_HAVE_VARIADIC_MACROS  1

    #define ngx_log_error(level, log, args...)                                    
        if ((log)->log_level >= level) ngx_log_error_core(level, log, args)

    void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
        const char *fmt, ...);

    #define ngx_log_debug(level, log, args...)                                    
        if ((log)->log_level & level)                                            
            ngx_log_error_core(NGX_LOG_DEBUG, log, args)


    2.2Nginx的ngx_log_error_core实现

    对于ngx_log_error_core如何内容格式化这里暂不讨论,讨论的是最终调用的那个底层函数。

       //within file ngnix-1.5.2/src/core/ngx_log.c

        while (log) {
            if (log->log_level < level && !debug_connection) {
                break;
            }
            (void) ngx_write_fd(log->file->fd, errstr, p - errstr);

            if (log->file->fd == ngx_stderr) {
                wrote_stderr = 1;
            }
            log = log->next;
        }

    其中ngx_write_fd是最终把日志信息写到文件中的,对于ngx_write_fd的实现最终是通过调用libc函数的write来直接写文件的,封装再多层,脱掉了还是一样。

    /*
     * we use inlined function instead of simple #define
     * because glibc 2.3 sets warn_unused_result attribute for write()
     * and in this case gcc 4.3 ignores (void) cast
     */
    static ngx_inline ssize_t
    ngx_write_fd(ngx_fd_t fd, void *buf, size_t n)
    {
        return write(fd, buf, n);
    }

    2.3 Linux内核printk的内核实现

    // within file linux-3.10.1/kernel/printk.c

    asmlinkage int printk(const char *fmt, ...) 
    {
            va_list args;
            int r;

    #ifdef CONFIG_KGDB_KDB
            if (unlikely(kdb_trap_printk)) {
                    va_start(args, fmt);
                    r = vkdb_printf(fmt, args);
                    va_end(args);
                    return r;
            }    
    #endif
            va_start(args, fmt);
            r = vprintk_emit(0, -1, NULL, 0, fmt, args);
            va_end(args);

            return r;
    }
    EXPORT_SYMBOL(printk);

    说明:

    A. asmlinkage指明printk直接从内存中读取参数而不是从寄存器中读取参数;

    B. CONFIG_KGDB_KDB 是不是很熟悉呢?调试内核的话用KDB或者KGDB其实就是把日志重定向到串口;

    C. va_list, va_start, va_end其实就是可变参数的实现核心,不管是用户层可变参数函数的实现还是内核层,va_*都是利器;

    D. 为了便于追踪和调试,日志应该存在文件之中,Linux中dmesg就是从/var/log/dmesg文件中读取内核的日志。当我们加printk的时候也是会被重定向到这个文件之中。Nginx的日志;

    E. vprintk_emit和更细致的内容另外开篇博客再讲。


    PK3:Nginx日志API和printk的使用

    3.1 Nginx日志函数的调用

    //within file nginx-1.5.2/src/core/connection.c

    ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, "setsockopt(SO_RCVBUF, %d) %V failed, ignored", ls[i].rcvbuf, &ls[i].addr_text);

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "eventfd handler");

    3.2 Printk的使用

    //within file linux-3.10.1/kernel/sched/core.c

    printk(  "[ BUG: circular locking deadlock detected! ] ");

    printk(KERN_DEBUG "%*s groups:", level + 1, "");


    从使用性的角度来看,ngx_log_error和ngx_log_debug把日志严格分离开了,各自用途明确。 Printk如果不加log level的话是默认level。


    PK4:日志API的可封装性

    在内核中不同的模块都有对printk的封装,例如netfilter模块对printk的重定义:#define NFDEBUG(format, args...)  printk(KERN_DEBUG format , ## args)。Nginx对ngx_log_error_core的封装在2.1中已经简单的说了一下。


    另外对日志信息的输出也是对日志信息实现的体现,既可以输出到标准输出stderr中,也可以输出到相关的文件中例如/var/log中。



  • 相关阅读:
    cake缓存
    css 总结文章
    cakephp随笔
    41.2WebSocket Security网络套接字安全
    36.1 DelegatingSecurityContextRunnable委托安全上下文可运行
    Spring Data Integration
    40. Security Database Schema安全数据库模式
    36.2 DelegatingSecurityContextExecutor委托安全上下文执行器
    41. The Security Namespace
    41.3 Authentication Services认证服务
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3201357.html
Copyright © 2011-2022 走看看