1、日志级别
简介
Level | Description | Example |
emerg | Emergencies - system is unusable 紧急 - 系统无法使用 | Child cannot open lock file. Exiting |
alert | Action must be taken immediately 必须立即采取措施 | getpwuid: couldn't determine user name from uid |
crit | Critical Conditions 致命情况 | socket: Failed to get a socket, exiting child |
error | Error conditions 错误情况 | Premature end of script headers |
warn | Warning conditions 警告情况 | child process 1234 did not exit, sending another SIGHUP |
notice | Normal but significant condition 一般重要情况 | httpd: caught SIGBUS, attempting to dump core in ... |
info | Informational 普通信息 | Server seems busy, (you may need to increase StartServers, or Min/MaxSpareServers)... |
debug | Debug-level messages 出错级别信息 | Opening config file ... |
nginx日志级别
#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
#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
备注:为了开发调试方便,日志被分成很多等级,Nginx的调试等级分成了两个维度,也可简称为error和debug维度,nginx支持的日志级别主要有emerg、alert、crit、error、warn、notice、info、debug,main部分默认是error级别、HTTP部分默认是crit级别,server部分默认是crit级别。
(1)第一个维度是0-8,对应nginx的配置如下所示:
#error_log logs/debug.log debug;
#error_log logs/error.log notice;
#error_log logs/error.log info;
define ngx_log_error(level, log, args...) \
if ((log)->log_level >= level) ngx_log_error_core(level, log, args)
(2)第二个维度是0x10以上,我们可以自由扩展,这个维度是按照叠加效果计算的。
#define ngx_log_debug(level, log, args...) \
if ((log)->log_level & level) \
ngx_log_error_core(NGX_LOG_DEBUG, log, args)
2、数据结构
日志的源代码放在src/core/ngx_log.h和ngx_log.c中。
日志操作函数:
typedef u_char *(*ngx_log_handler_pt) (ngx_log_t *log, u_char *buf, size_t len);
struct ngx_log_s {
ngx_uint_t log_level; //日志级别
ngx_open_file_t *file; //对应的access和error文件
ngx_atomic_uint_t connection;
ngx_log_handler_pt handler;
void *data;
char *action;
};
3、日志格式
access.log
访问日志格式(可自定义):
log_format main '$remote_addr $remote_user [$time_local] "$request" $http_host '
'$status $upstream_status $body_bytes_sent "$http_referer" '
'"$http_user_agent" $ssl_protocol $ssl_cipher $upstream_addr '
'$request_time $upstream_response_time';
有价值变量:
(1)$remote_addr:客户端IP地址,可以得到分省流量信息;
(2)$ssl_protocol: 所用协议如TLSv1、SSLv3;
(3)$ssl_cipher:传输过程中的加密算法如RC4-SHA、AES128-SHA;
真实日志格式(1):
1.1.1.1 - [21/May/2012:09:00:01 +0800] "GET /tile/service/XX?hello=123 HTTP/1.1" hello.world.com($http_host)
200 200 3583 "https://hello.cj.com/standard/XXX?dddde=ddddd"($http_referer,来源地址) "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; 360SE)" TLSv1($ssl_protocol) AES128-SHA($ssl_cipher) 3.3.3.3:80($upstream_addr)0.145 0.053
真实日志格式(2):
1.1.1.1 - [21/May/2012:09:00:01 +0800] "GET /index.htm HTTP/1.1" hello.world.com 302 302 37 "https://hello.cj.com/user/i
.htm?srcsdfsd=yysdfdsf" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)" SSLv3($ssl_protocol) RC4-SHA($ssl_cipher) 3.3.3.3:80 ($upstream_addr)0.309 0.007
关闭访问日志:
access_log off;
error.log
错误日志格式(不能自定义):
error_log logs/error.log;
真实日志格式:
2012/05/21 09:00:09 [error] 9849#0: *354826826 upstream_ci:free the peer name:"3.3.3.3:80" fails:2 while connecting to upstre
am, client: 10.228.255.101, server: , request: "HEAD / HTTP/1.1", upstream: "http://3.3.3.3:80/", host: "hello.world.com"
关闭错误日志:
error_log /dev/null crit;
备注:不能使用error_log off,会创建一个名为off的日志文件。
4、操作函数
ngx_log_init (u_char *prefix)
函数功能:main函数初始化错误日志"./logs/error.log"。
ngx_log_t *
ngx_log_init(u_char *prefix)
{
ngx_log.file = &ngx_log_file;
ngx_log.log_level = NGX_LOG_NOTICE;
name = (u_char *) NGX_ERROR_LOG_PATH; // "logs/error.log"
nlen = ngx_strlen(name);
。。。
ngx_log_file.fd = ngx_open_file(name, NGX_FILE_APPEND,
NGX_FILE_CREATE_OR_OPEN,
NGX_FILE_DEFAULT_ACCESS);
//name ="./logs/error.log"
return &ngx_log;
}
ngx_log_create(ngx_cycle_t *cycle, ngx_str_t *name)
函数功能:创建新的日志结构。
ngx_log_t *
ngx_log_create(ngx_cycle_t *cycle, ngx_str_t *name)
{
ngx_log_t *log;
log = ngx_pcalloc(cycle->pool, sizeof(ngx_log_t)); //分配空间
if (log == NULL) {
return NULL;
}
log->file = ngx_conf_open_file(cycle, name);
//循环查找list(cycle->open_files.part),如果文件已经打开,返回已有结构,否则分配空间,并放入cycle->open_files.part管理
if (log->file == NULL) {
return NULL;
}
return log;
}
ngx_log_set_levels (ngx_conf_t *cf, ngx_log_t *log)
函数功能:根据配置文件设定日志级别,error维度和debug维度。
char *
ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log)
{
ngx_uint_t i, n, d;
ngx_str_t *value;
value = cf->args->elts;
//error_log logs/error.log info;
for (i = 2; i < cf->args->nelts; i++) {
for (n = 1; n <= NGX_LOG_DEBUG; n++) {
// NGX_LOG_DEBUG是error维度最低的级别
if (ngx_strcmp(value[i].data, err_levels[n].data) == 0) {
if (log->log_level != 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"duplicate log level \"%V\"",
&value[i]);
return NGX_CONF_ERROR;
}
log->log_level = n;
continue;
}
}
//error_log logs/error.log debug_http
for (n = 0, d = NGX_LOG_DEBUG_FIRST; d <= NGX_LOG_DEBUG_LAST; d <<= 1) {
if (ngx_strcmp(value[i].data, debug_levels[n++]) == 0) {
if (log->log_level & ~NGX_LOG_DEBUG_ALL) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid log level \"%V\"",
&value[i]);
return NGX_CONF_ERROR;
}
log->log_level |= d; //叠加级别
}
}
if (log->log_level == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid log level \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
}
if (log->log_level == NGX_LOG_DEBUG) {
log->log_level = NGX_LOG_DEBUG_ALL;
}
return NGX_CONF_OK;
}
ngx_log_error_core (ngx_uint_t level, ngx_log_t *log, ngx_err_t err, const char *fmt, ...)
函数功能:错误日志中最核心的函数,打印如下格式的错误日志(不能自定义),即时间+错误级别+nginx pid+错误详细信息。
2012/05/21 09:00:09 [error] 9849#0: *354826826 upstream_ci:free the peer name:"10.228.17.149:80" fails:2 while connecting to upstre
am, client: 10.228.255.101, server: , request: "HEAD / HTTP/1.1", upstream: "http://10.228.17.149:80/", host: "alipay.com"
void
ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
const char *fmt, va_list args)
#endif
{
#if (NGX_HAVE_VARIADIC_MACROS)
va_list args;
#endif
u_char *p, *last, *msg;
u_char errstr[NGX_MAX_ERROR_STR];
if (log->file->fd == NGX_INVALID_FILE) {
return;
}
last = errstr + NGX_MAX_ERROR_STR;
ngx_memcpy(errstr, ngx_cached_err_log_time.data,
ngx_cached_err_log_time.len); //2012/05/21 09:00:09
p = errstr + ngx_cached_err_log_time.len;
p = ngx_slprintf(p, last, " [%V] ", &err_levels[level]); //错误打印级别
/* pid#tid */
p = ngx_slprintf(p, last, "%P#" NGX_TID_T_FMT ": ",
ngx_log_pid, ngx_log_tid); //nginx的pid
if (log->connection) {
p = ngx_slprintf(p, last, "*%uA ", log->connection);
}
msg = p;
#if (NGX_HAVE_VARIADIC_MACROS)
va_start(args, fmt);
p = ngx_vslprintf(p, last, fmt, args); //详细的错误信息
va_end(args);
#else
p = ngx_vslprintf(p, last, fmt, args);
#endif
if (err) {
p = ngx_log_errno(p, last, err);
}
if (level != NGX_LOG_DEBUG && log->handler) {
p = log->handler(log, p, last - p);
}
if (p > last - NGX_LINEFEED_SIZE) {
p = last - NGX_LINEFEED_SIZE;
}
ngx_linefeed(p);
(void) ngx_write_fd(log->file->fd, errstr, p - errstr); //写入文件
ngx_http_tracker_add_error_count();
if (!ngx_use_stderr
|| level > NGX_LOG_WARN
|| log->file->fd == ngx_stderr)
{
return;
}
msg -= (7 + err_levels[level].len + 3);
(void) ngx_sprintf(msg, "nginx: [%V] ", &err_levels[level]);
(void) ngx_write_console(ngx_stderr, msg, p - msg); //写到终端
}
ngx_log_debug_core (ngx_log_t *log, ngx_err_t err, const char *fmt, ...)
函数功能:将日志级别设置为NGX_LOG_DEBUG,调用ngx_log_error_core打印错误日志。
void ngx_cdecl
ngx_log_debug_core(ngx_log_t *log, ngx_err_t err, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
ngx_log_error_core(NGX_LOG_DEBUG, log, err, fmt, args);
va_end(args);
}
ngx_conf_log_error(ngx_uint_t level, ngx_conf_t *cf, ngx_err_t err
函数功能:配置错误的入口,一般level为EMEG。
ngx_log_error(level, cf->log, 0, "%*s in %s:%ui",
p - errstr, errstr,
cf->conf_file->file.name.data, cf->conf_file->line);
ngx_log_error (level, log, ...)
函数功能:比较日志错误级别,调用ngx_log_error_core打印错误日志。
#define ngx_log_error(level, log, ...) \
if ((log)->log_level >= level) ngx_log_error_core(level, log, __VA_ARGS__)
ngx_log_abort (ngx_err_t err, const char *fmt, ...)
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err,
"%*s", p - errstr, errstr);
ngx_log_stderr
函数功能:将错误信息,直接打印到终端。
ngx_http_log_handler(ngx_http_request_t *r)
函数功能:ngx_http_log_init(ngx_conf_t *cf)将ngx_http_log_handler放入NGX_HTTP_LOG_PHASE中,在请求过来的时候写access_log。
h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_log_handler;
5、切割日志
用nginx做web服务器,如果没有处理好日志,日志文件可能会很恐怖~10G、20G。解决方法:
(1)access_log /dev/null,将日志丢到系统的黑洞,延长硬盘的寿命。
(2)用定时任务来切割日志,kill –USR1来发送命令;
#crontab –l
00 * * * * /home/admin/spanner/sbin/log_rotate.sh
Filename: /home/admin/spanner/sbin/log_rotate.sh
#!/bin/bash
log_dir="/home/admin/spanner/logs"
backup_dir="/home/admin/spanner_logs"
date_dir=`date +%Y/%m/%d/%H`
/bin/mkdir -p ${backup_dir}/${date_dir} > /dev/null 2>&1
/bin/mv ${log_dir}/access.log ${backup_dir}/${date_dir}/access.log
/bin/mv ${log_dir}/error.log ${backup_dir}/${date_dir}/error.log
kill -USR1 `cat ${log_dir}/nginx.pid`
/bin/gzip ${backup_dir}/${date_dir}/access.log &
/bin/gzip ${backup_dir}/${date_dir}/error.log &
6、使用实例
ngx_log_stderr(0, "invalid option: \"%s\"", argv[i]);
函数功能:向终端打印错误信息;
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", &value[i]);
函数功能:在启动过程中,配置错误打印接口;
ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "[white list]: access allowed by rule");
函数功能:运行过程中,向错误日志文件写入信息,级别是DEBUG;
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "[black list]: access forbidden by rule");
函数功能:运行过程中,向错误日志文件写入信息,级别是ERROR;