题记: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        "\001"          /* ASCII Start Of Header */
#define KERN_SOH_ASCII  '\001'

#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! ]\n");

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中。

Nginx之旅系列 - Nginx日志功能 PK Linux内核printk的更多相关文章

  1. Nginx之旅系列 - Nginx的configuration

    题记:Nginx之旅系列是用来记录Nginx从使用到源码学习的点点滴滴,分享学习Nginx的快乐 Nginx 首页: http://nginx.org/ Nginx的configuration 今天对 ...

  2. Nginx服务编译安装、日志功能、状态模块及访问认证模式实操

    系统环境 [root@web ~]# cat /etc/redhat-release CentOS release 6.9 (Final) [root@web ~]# uname -a Linux d ...

  3. 【转】TI-Davinci开发系列之六CCS5.2调试Linux内核

    上转博文<TI-Davinci开发系列之五CCS5.2使用gdbserver远程调试应用程序> 使用CCS5.2远程调试内核时,只需导入Linux内核源码,而不需要编译内核,也就不会用到交 ...

  4. nginx配置之错误和访问日志功能

    错误日志功能:logs/error.log nginx.conf中: #error_log logs/error.log; #error_log logs/error.log notice; #err ...

  5. nginx 日志功能详解

    nginx 日志功能 在 nginx 中有两种日志: access_log:访问日志,通过访问日志可以获取用户的IP.请求处理的时间.浏览器信息等 error_log:错误日志,记录了访问出错的信息, ...

  6. nginx之旅(第二篇):nginx日志管理、nginx防盗链、nginx虚拟主机

    一.nginx日志管理 Nginx访问日志主要有两个参数控制 1) log_format #用来定义记录日志的格式(可以定义多种日志格式,取不不同名字即可) log_format log_name s ...

  7. [Linux] PHP程序员玩转Linux系列-nginx初学者引导

    1.PHP程序员玩转Linux系列-怎么安装使用CentOS 2.PHP程序员玩转Linux系列-lnmp环境的搭建 3.PHP程序员玩转Linux系列-搭建FTP代码开发环境 4.PHP程序员玩转L ...

  8. Nginx技术研究系列6-配置详解

    前两篇文章介绍了Nginx反向代理和动态路由: Ngnix技术研究系列1-通过应用场景看Nginx的反向代理 Ngnix技术研究系列2-基于Redis实现动态路由 随着研究的深入,很重要的一点就是了解 ...

  9. Nginx知多少系列之(一)前言

    目录 1.前言 2.安装 3.配置文件详解 4.工作原理 5.Linux下托管.NET Core项目 6.Linux下.NET Core项目负载均衡 7.Linux下.NET Core项目Nginx+ ...

随机推荐

  1. Ionic项目中使用极光推送-android

    对于Ionic项目中使用消息推送服务,Ionic官方提供了ngCordova项目,这个里面的提供了用angularjs封装好的消息推送服务(官方文档),使用的是GitHub上的 PushPlugin ...

  2. 宏观CMS--&gt;功能体系结构内容管理系统

      CMS,Content Management System,一个非常普通的站点内容管理系统.本文章旨在从一定的高度把CMS的功能概念做一个分解论述 ,希望读者能够有所感. 1.前台 前台是站点中给 ...

  3. OR导致笛卡尔积

    近期监控数据库,发现以下语句跑得很慢,原来运行计划走了导致笛卡尔积,来看以下语句: SQL> explain plan for 2 SELECT COUNT(*) 3 FROM "GD ...

  4. 分析Cocos2d-x横版ACT手游源 2、server场景

    仍然一样 直接在代码 资源 下一个 上传 你可以看到自己 NFServerChangeLayer.h </pre><pre name="code" class=& ...

  5. c语言下多线程

    原文:c语言下多线程 [问题]创建了10个线程,10个线程公用一个线程体,创建如下: int t1=0,t2=1,t3=2,t4=3,t5=4,t6=5,t7=6,t8=7,t9=8,t10=9; i ...

  6. white-space的值

    white-space的值:normal 默认.空白会被浏览器忽略.pre 空白会被浏览器保留.其行为方式类似 HTML 中的 标签.nowrap 文本不会换行,文本会在在同一行上继续,直到遇到 标签 ...

  7. 查看oracle数据库服务器的名字

    原文:查看oracle数据库服务器的名字 windows 中 1. select name from v$database ; 直接运行就可以查看了, 2.查看tnsnames.ora 的连接,有个S ...

  8. UC编程:字符读取与行读取

    字符读取函数的应用 下面的演示程序实现从/etc/passwd文件中提取用户名,打印到屏幕上并保存在copyname.txt文件中 使用的函数是getc().putc().putchar() [c] ...

  9. OCP-1Z0-051-题目解析-第3题

    3. You need to extract details of those products in the SALES table where the PROD_ID columncontains ...

  10. [译]JVM运行时数据区

    (本篇文章翻译自JVM Run-Time Data Areas) 这是我阅读JVM规范的笔记,而且我画了一个图来帮助我理解. 1.每一个单独的线程(非共享的)的数据区 针对每一个单独的线程的数据区包括 ...