秀米云香港服务器

nginx配置中有关日志的配置主要是围绕着下面两个指令:

1、error_log

序列号 CPU RAM HDD 带宽 售价(美元) 免费试用
香港服务器1 E5-2620 32G 1T HDD 50M/无限流量 $196.00 立即申请
香港服务器2 E5-2650 32G 1T HDD 50M/无限流量 $256.00 立即申请
香港服务器3 E5-2680 32G 1T HDD 50M/无限流量 $316.00 立即申请
香港服务器4 E5-2690 32G 1T HDD 50M/无限流量 $336.00 立即申请
香港服务器5 E5-2697 32G 1T HDD 50M/无限流量 $376.00 立即申请
香港服务器6 E5-2620*2 32G 1T HDD 50M/无限流量 $376.00 立即申请
香港服务器7 E5-2650*2 32G 1T HDD 50M/无限流量 $436.00 立即申请
香港服务器8 E5-2680*2 32G 1T HDD 50M/无限流量 $476.00 立即申请
香港服务器9 E5-2690*2 32G 1T HDD 50M/无限流量 $556.00 立即申请
香港服务器10 E5-2697*2 32G 1T HDD 50M/无限流量 $596.00 立即申请
香港服务器11 E5-2680v4*2 32G 1T HDD 50M/无限流量 $696.00 立即申请
香港服务器12 E5-2698v4*2 32G 1T HDD 50M/无限流量 $796.00 立即申请

2、access_log:记录访问日志

首先要强调的一点是,如果access日志和error日志都是常量文件名(因为access支持变量文件名,后续会讲到),那么nginx进程会缓存文件描述符直到进程结束。

什么时候日志的fd会改变呢?

1)进程重启

2)收到了NGX_REOPEN_SIGNAL信号,会产生新的日志文件

其他情况下,日志的fd不变,所以当进程运行中,删除了日志文件的话,并不会生成新的日志文件,且日志都会丢失

下面详细讲一下这两个指令的来龙去脉

一:先说error_log:

nginx有两个模块支持error_log指令:

一个是 ngx_errlog_module ,这个模块只有一个指令,就是error_log,配置类型为:NGX_MAIN_CONF,回调函数为:ngx_error_log;

另一个是 ngx_http_core_module,这个模块中也有指令:error_log,配置类型为:NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF,回调函数为:ngx_http_core_error_log。

staticngx_command_tngx_errlog_commands[]={{ngx_string("error_log"),NGX_MAIN_CONF|NGX_CONF_1MORE,ngx_error_log,0,0,NULL},ngx_null_command};staticngx_command_tngx_http_core_commands[]={{ngx_string("error_log"),NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,ngx_http_core_error_log,NGX_HTTP_LOC_CONF_OFFSET,0,NULL},}

这样会产生几个疑问:

1:为什么需要两个相同的指令实现相同的功能。

2:两个指令的类型均支持NGX_HTTP_MAIN_CONF,那么在main中配置的error_log到底使用的是哪一个

3:两者的作用关系。

下面来解释一下:

nginx在进行模块注册时,会发现 ngx_errlog_module 模块是先于 ngx_http_core_module模块注册的 。

在nginx在解析配置文件的时候 ,见到error_log,会按照注册模块的顺序查找指令,这样,会先找到ngx_errlog_module模块,如果此时,error_log是在main配置的,那么和ngx_errlog_module模块error_log的NGX_HTTP_MAIN_CONFmatch,执行ngx_error_log。

如果error_log是在http{}配置的,也会按照注册模块的顺序查找指令,找到ngx_errlog_module模块的error_log,发现type是NGX_HTTP_MAIN_CONF,不match,继续往下找,找到ngx_http_core_module的error_log,type是NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF,match后执行ngx_http_core_error_log。

上面提到了ngx_error_log 和ngx_http_core_error_log两个函数,这两个函数的功能基本一致,但是因为两个配置作用域不同,所以配置存储位置不同:ngx_errlog_module存储在cycle->new_log,ngx_http_core_module存储在httpcore模块数据结构ngx_http_core_loc_conf_s的error_log(在此简写成:clcf->error_log)。

clcf->error_log是http模块中的,其主要记录和http请求相关的日志。

cycle->new_log主要记录如进程启动,event等。

但是主进程启动的时候,此时还没有读取配置文件,即没有指定日志打印在哪里。nginx这时候虽然可以将一些出错内容或者结果输到标准输出,但是如果要记录一些系统初始化情况,socket监听状况,还是需要写到日志文件中去的。在nginx的main函数中,首先会调用ngx_log_init函数,默认日志文件为:安装路径/logs/error.log,如果这个文件没有权限访问的话,会直接报错退出。在mian函数结尾处,在ngx_master_process_cycle函数调用之前,会close掉这个日志文件。

如果只在main配置了error_log,http{}中没有设置,那么clcf->error_log赋值为clcf->error_log,如下:

staticchar*ngx_http_core_merge_loc_conf(ngx_conf_t*cf,void*parent,void*child){ngx_http_core_loc_conf_t*prev=parent;ngx_http_core_loc_conf_t*conf=child;。。。。。。if(conf->error_log==NULL){if(prev->error_log){conf->error_log=prev->error_log;}else{conf->error_log=&cf->cycle->new_log;}}。。。。。。}

那为什么不把两个指令合并到一起呢。

首先看一下模块注册顺序:

ngx_module_t*ngx_modules[]={&ngx_core_module,&ngx_errlog_module,&ngx_conf_module,&ngx_events_module,&ngx_event_core_module,&ngx_rtsig_module,&ngx_epoll_module,&ngx_regex_module,&ngx_http_module,&ngx_http_core_module,&ngx_http_log_module,......}

可见ngx_errlog_module 和ngx_http_core_module中间有n多模块。这些模块是独立于http的,而且需要日志的打印,这些日志肯定是需要记录的。

则此模块不可省,那么考虑把ngx_http_core_module的error_log合并过来呢,想想也不行,为什么呢?

想想ngx_errlog_module将error_log信息存在哪里,想想ngx_http_core_module的error_log信息存在哪里。

在调用ngx_error_log时候,把针对不同server或者location的error_log信息存储在哪里。(模块之间不能深度耦合)

为了两个模块互不影响,这是个好办法呀!

二:access_log

接下来看一下access_log,access_log指令是属于ngx_http_log_module模块。

ngx_http_log_module有三个指令:

staticngx_command_tngx_http_log_commands[]={{ngx_string("log_format"),NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_2MORE,ngx_http_log_set_format,NGX_HTTP_MAIN_CONF_OFFSET,0,NULL},{ngx_string("access_log"),NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE123,ngx_http_log_set_log,NGX_HTTP_LOC_CONF_OFFSET,0,NULL},{ngx_string("open_log_file_cache"),NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,ngx_http_log_open_file_cache,NGX_HTTP_LOC_CONF_OFFSET,0,NULL},ngx_null_command};

每个block都可以配置上面的指令,这些指令对应着各个block配置中的loc[ngx_http_log_module.index](要理解这个,需要知道配置文件的整个解析过程和block对应关系),即下面这个数据结构:

typedefstruct{ngx_array_t*logs;/*arrayofngx_http_log_t*/ngx_open_file_cache_t*open_file_cache;time_topen_file_cache_valid;ngx_uint_topen_file_cache_min_uses;ngx_uint_toff;/*unsignedoff:1*/}ngx_http_log_loc_conf_t;

关于access_log主要有以下几个点:

1、ngx_http_log_module是属于nginx状态机最后一个阶段NGX_HTTP_LOG_PHASE的处理模块,即一个http请求结束时执行的,它的任务就是打印这次request的访问情况。

2、access_log支持根据变量指令路径,如:

access_loglogs/'$remote_addr'access.log main;

3、不管是变量路径还是常量路径,都将信息放入了ngx_http_log_loc_conf_t的logs这个数组里进行管理,logs数组的元素是ngx_http_log_t。

typedefstruct{ngx_open_file_t*file;ngx_http_log_script_t*script;time_tdisk_full_time;time_terror_log_time;ngx_http_log_fmt_t*format;}ngx_http_log_t;

当是常量的时候使用file记录,这个文件记录会放入到cycle->open_files

structngx_open_file_s{ngx_fd_tfd;ngx_str_tname;u_char*buffer;u_char*pos;u_char*last;};

当是变量的时候使用script记录

typedefstruct{ngx_array_t*lengths;ngx_array_t*values;}ngx_http_log_script_t;

4、不管是error_log还是access_log,nginx都是通过保存文件句柄来进行快速写日志文件的。但是因为access_log支持根据变量指令路径,如果按照request或者ip来分隔不同的access日志,那么可想而至,若还按照保存文件句柄的方式来写日志文件,会造成系统fd的大量占用。nginx在此进行了优化:

1)如果用常量指定acess日志路径:

access_log logs/access.logmain;

那么和error_log一样,将文件路径名称放到cycle->open_files中去,这是个list,在路径加入这个list的时候会进行除重操作的。在所有的模块初始化完毕,会依次打开这些文件路径,获取到fd,以备打印日志使用。

打印日志的时候调用函数:ngx_write_fd

2)如果用变量指定acess日志路径:

使用script标记日志文件为变量文件名的。

打印日志的时候调用函数:ngx_http_log_script_write

在这个函数里,体现出了对缓存fd的管理。这些和指令open_file_log_cache的配置是息息相关的(后面会详细介绍)。

打日志的函数:

staticvoidngx_http_log_write(ngx_http_request_t*r,ngx_http_log_t*log,u_char*buf,size_tlen){u_char*name;time_tnow;ssize_tn;ngx_err_terr;if(log->script==NULL){name=log->file->name.data;n=ngx_write_fd(log->file->fd,buf,len);}else{name=NULL;n=ngx_http_log_script_write(r,log->script,&name,buf,len);}......}

5、说到缓存文件描述符,nginx有两个指令是管理缓存文件描述符的

一个就是本文中说到的ngx_http_log_module模块的open_file_log_cache;

一个是ngx_http_core_module模块的 open_file_cache;

前者是只用来管理access变量日志文件。

后者用来管理的就多了,包括:static,index,tryfiles,gzip,mp4,flv,看到了没,都是静态文件哦!

这两个指令的handler都调用了函数 ngx_open_file_cache_init,这就是用来管理缓存文件描述符的第一步:初始化

ngx_open_file_cache_t*ngx_open_file_cache_init(ngx_pool_t*pool,ngx_uint_tmax,time_tinactive){ngx_pool_cleanup_t*cln;ngx_open_file_cache_t*cache;cache=ngx_palloc(pool,sizeof(ngx_open_file_cache_t));if(cache==NULL){returnNULL;}ngx_rbtree_init(&cache->rbtree,&cache->sentinel,ngx_open_file_cache_rbtree_insert_value);ngx_queue_init(&cache->expire_queue);cache->current=0;cache->max=max;cache->inactive=inactive;cln=ngx_pool_cleanup_add(pool,0);if(cln==NULL){returnNULL;}cln->handler=ngx_open_file_cache_cleanup;cln->data=cache;returncache;}

可以看到nginx管理缓存文件描述符,使用了红黑树和队列,这个后续还是作为一篇文章来叙述吧,涉及的内容有点多,本文还是以分析日志模块为主。

6、说一下指令 open_file_log_cache

1)nginx下默认这个指令的配置是:open_file_log_cache off;

也就是说不对access变量日志文件的fd做缓存,每写一个文件就打开,然后写日志。那么这个文件fd什么时候关闭呢。

这就涉及到nginx内存管理的cleanup了,cleanup可以注册,在内存池被销毁的时候,调用cleanup链表中各个cleanup的handler(详细可以去翻阅nginx内存池管理)

而此时的文件fd就是在request完毕后,销毁内存池的时候,关闭fd。

配置open_log_file_cache off; 时的运行

这是获取access变量文件fd的函数,返回值应该是access日志的fd。

ngx_int_tngx_open_cached_file(ngx_open_file_cache_t*cache,ngx_str_t*name,ngx_open_file_info_t*of,ngx_pool_t*pool){time_tnow;uint32_thash;ngx_int_trc;ngx_file_info_tfi;ngx_pool_cleanup_t*cln;ngx_cached_open_file_t*file;ngx_pool_cleanup_file_t*clnf;ngx_open_file_cache_cleanup_t*ofcln;of->fd=NGX_INVALID_FILE;of->err=0;//配置open_log_file_cacheoff的话,cache为空if(cache==NULL){......//添加一个cleanupcln=ngx_pool_cleanup_add(pool,sizeof(ngx_pool_cleanup_file_t));if(cln==NULL){returnNGX_ERROR;}//打开日志文件,获得fdrc=ngx_open_and_stat_file(name,of,pool->log);rc=ngx_open_and_stat_file(name,of,pool->log);//打开日志文件成功if(rc==NGX_OK&&!of->is_dir){//添加cleanup回调,在销毁内存池的时候调用ngx_pool_cleanup_file,函数内采用了close关闭fdcln->handler=ngx_pool_cleanup_file;clnf=cln->data;clnf->fd=of->fd;clnf->name=name->data;clnf->log=pool->log;}returnrc;}......}

2)如果配置open_file_log_cache的话,支持四种参数:

max = N [ inactive = time ] [ min_uses = N ] [ valid = time]

max 最大缓存文件描述符数量inactive 在多少时间内不活动,就会被删除min_uses 必须在 inactive时间内活动N次,才会被缓存valid 检查inactive的时间。

具体的缓存文件fd的来龙去脉值得用一篇文章来详细描述,在这里就暂且不说了,希望最近有时间整理出来。

The End

标题:Nginx的error_log和Access_log分析

地址: https://www.yunhk.xyz/26360.html