Nginx的error_log與Access_log區(qū)分
發(fā)表時(shí)間:2023-08-10 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]nginx配置中有關(guān)日志的配置主要是圍繞著下面兩個(gè)指令:1、error_log2、access_log:記錄訪問日志首先要強(qiáng)調(diào)的一點(diǎn)是,如果access日志和error日志都是常量文件名(因?yàn)閍cc...
nginx配置中有關(guān)日志的配置主要是圍繞著下面兩個(gè)指令:
1、error_log
2、access_log:記錄訪問日志
首先要強(qiáng)調(diào)的一點(diǎn)是,如果access日志和error日志都是常量文件名(因?yàn)閍ccess支持變量文件名,后續(xù)會講到),那么nginx進(jìn)程會緩存文件描述符直到進(jìn)程結(jié)束。
什么時(shí)候日志的fd會改變呢?
1)進(jìn)程重啟
2)收到了NGX_REOPEN_SIGNAL信號,會產(chǎn)生新的日志文件
其他情況下,日志的fd不變,所以當(dāng)進(jìn)程運(yùn)行中,刪除了日志文件的話,并不會生成新的日志文件,且日志都會丟失
下面詳細(xì)講一下這兩個(gè)指令的來龍去脈
一:先說error_log:
nginx有兩個(gè)模塊支持error_log指令:
一個(gè)是 ngx_errlog_module ,這個(gè)模塊只有一個(gè)指令,就是error_log ,配置類型為:NGX_MAIN_CONF,回調(diào)函數(shù)為:ngx_error_log;
另一個(gè)是 ngx_http_core_module,這個(gè)模塊中也有指令:error_log ,配置類型為:NGX_HTTP_MAIN_CONF NGX_HTTP_SRV_CONF NGX_HTTP_LOC_CONF,回調(diào)函數(shù)為:ngx_http_core_error_log。
static ngx_command_t ngx_errlog_commands[] = {
{ngx_string("error_log"),
NGX_MAIN_CONF NGX_CONF_1MORE,
ngx_error_log,
0,
0,
NULL},
ngx_null_command
};
static ngx_command_t ngx_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 },
}
這樣會產(chǎn)生幾個(gè)疑問:
1:為什么需要兩個(gè)相同的指令實(shí)現(xiàn)相同的功能。
2:兩個(gè)指令的類型均支持NGX_HTTP_MAIN_CONF ,那么在main中配置的error_log到底使用的是哪一個(gè)
3:兩者的作用關(guān)系。
下面來解釋一下:
nginx在進(jìn)行模塊注冊時(shí),會發(fā)現(xiàn) ngx_errlog_module 模塊是先于 ngx_http_core_module 模塊注冊的 。
在nginx在解析配置文件的時(shí)候 ,見到 error_log,會按照注冊模塊的順序查找指令,這樣,會先找到ngx_errlog_module模塊,如果此時(shí),error_log是在main配置的,那么和ngx_errlog_module模塊error_log的NGX_HTTP_MAIN_CONF match,執(zhí)行ngx_error_log。
如果error_log是在http{}配置的,也會按照注冊模塊的順序查找指令,找到ngx_errlog_module模塊的error_log,發(fā)現(xiàn)type是NGX_HTTP_MAIN_CONF,不match,繼續(xù)往下找,找到ngx_http_core_module的error_log,type是NGX_HTTP_MAIN_CONF NGX_HTTP_SRV_CONF NGX_HTTP_LOC_CONF ,match后執(zhí)行ngx_http_core_error_log。
上面提到了ngx_error_log 和 ngx_http_core_error_log兩個(gè)函數(shù),這兩個(gè)函數(shù)的功能基本一致,但是因?yàn)閮蓚(gè)配置作用域不同,所以配置存儲位置不同:ngx_errlog_module存儲在cycle->new_log,ngx_http_core_module存儲在http core模塊數(shù)據(jù)結(jié)構(gòu)ngx_http_core_loc_conf_s的error_log(在此簡寫成:clcf->error_log)。
clcf->error_log是http模塊中的,其主要記錄和http請求相關(guān)的日志。
cycle->new_log主要記錄如進(jìn)程啟動,event等。
但是主進(jìn)程啟動的時(shí)候,此時(shí)還沒有讀取配置文件,即沒有指定日志打印在哪里。nginx這時(shí)候雖然可以將一些出錯(cuò)內(nèi)容或者結(jié)果輸?shù)綐?biāo)準(zhǔn)輸出,但是如果要記錄一些系統(tǒng)初始化情況,socket監(jiān)聽狀況,還是需要寫到日志文件中去的。在nginx的main函數(shù)中,首先會調(diào)用ngx_log_init 函數(shù),默認(rèn)日志文件為:安裝路徑/logs/error.log,如果這個(gè)文件沒有權(quán)限訪問的話,會直接報(bào)錯(cuò)退出。在mian函數(shù)結(jié)尾處,在ngx_master_process_cycle函數(shù)調(diào)用之前,會close掉這個(gè)日志文件。
如果只在main配置了error_log,http{}中沒有設(shè)置,那么clcf->error_log賦值為clcf->error_log,如下:
static char *
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;
}
}
。。。。。。
}
那為什么不把兩個(gè)指令合并到一起呢。
首先看一下模塊注冊順序:
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多模塊。這些模塊是獨(dú)立于http的,而且需要日志的打印,這些日志肯定是需要記錄的。
則此模塊不可省,那么考慮把ngx_http_core_module的error_log合并過來呢,想想也不行,為什么呢?
想想ngx_errlog_module將error_log信息存在哪里,想想ngx_http_core_module的error_log信息存在哪里。
在調(diào)用ngx_error_log時(shí)候,把針對不同server或者location的error_log信息存儲在哪里。(模塊之間不能深度耦合)
為了兩個(gè)模塊互不影響,這是個(gè)好辦法呀!
二:access_log
接下來看一下access_log,access_log指令是屬于ngx_http_log_module模塊。
ngx_http_log_module有三個(gè)指令:
static ngx_command_t ngx_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
};
每個(gè)block都可以配置上面的指令 ,這些指令對應(yīng)著各個(gè)block配置中的loc[ngx_http_log_module.index](要理解這個(gè),需要知道配置文件的整個(gè)解析過程和block對應(yīng)關(guān)系),即下面這個(gè)數(shù)據(jù)結(jié)構(gòu):
typedef struct {
ngx_array_t *logs; /* array of ngx_http_log_t */
ngx_open_file_cache_t *open_file_cache;
time_t open_file_cache_valid;
ngx_uint_t open_file_cache_min_uses;
ngx_uint_t off; /* unsigned off:1 */
} ngx_http_log_loc_conf_t;
關(guān)于access_log主要有以下幾個(gè)點(diǎn):
1、ngx_http_log_module是屬于nginx狀態(tài)機(jī)最后一個(gè)階段NGX_HTTP_LOG_PHASE的處理模塊,即一個(gè)http請求結(jié)束時(shí)執(zhí)行的,它的任務(wù)就是打印這次request的訪問情況。
2、access_log支持根據(jù)變量指令路徑,如:
access_log logs/'$remote_addr'access.log main;
3、不管是變量路徑還是常量路徑,都將信息放入了 ngx_http_log_loc_conf_t的logs這個(gè)數(shù)組里進(jìn)行管理,logs數(shù)組的元素是ngx_http_log_t。
typedef struct {
ngx_open_file_t *file;
ngx_http_log_script_t *script;
time_t disk_full_time;
time_t error_log_time;
ngx_http_log_fmt_t *format;
} ngx_http_log_t;
當(dāng)是常量的時(shí)候使用file記錄,這個(gè)文件記錄會放入到cycle->open_files
struct ngx_open_file_s {
ngx_fd_t fd;
ngx_str_t name;
u_char *buffer;
u_char *pos;
u_char *last;
};
當(dāng)是變量的時(shí)候使用script記錄
typedef struct {
ngx_array_t *lengths;
ngx_array_t *values;
} ngx_http_log_script_t;
4、不管是error_log還是access_log,nginx都是通過保存文件句柄來進(jìn)行快速寫日志文件的。但是因?yàn)閍ccess_log支持根據(jù)變量指令路徑,如果按照request或者ip來分隔不同的access日志,那么可想而至,若還按照保存文件句柄的方式來寫日志文件,會造成系統(tǒng)fd的大量占用。nginx在此進(jìn)行了優(yōu)化:
1)如果用常量指定acess日志路徑:
access_log logs/access.log main;
那么和error_log一樣,將文件路徑名稱放到cycle->open_files中去,這是個(gè)list,在路徑加入這個(gè)list的時(shí)候會進(jìn)行除重操作的。在所有的模塊初始化完畢,會依次打開這些文件路徑,獲取到fd,以備打印日志使用。
打印日志的時(shí)候調(diào)用函數(shù):ngx_write_fd
2)如果用變量指定acess日志路徑:
使用script標(biāo)記日志文件為變量文件名的。
打印日志的時(shí)候調(diào)用函數(shù):ngx_http_log_script_write
在這個(gè)函數(shù)里,體現(xiàn)出了對緩存fd的管理。這些和指令open_file_log_cache的配置是息息相關(guān)的(后面會詳細(xì)介紹)。
打日志的函數(shù):
static void
ngx_http_log_write(ngx_http_request_t *r, ngx_http_log_t *log, u_char *buf,
size_t len)
{
u_char *name;
time_t now;
ssize_t n;
ngx_err_t err;
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有兩個(gè)指令是管理緩存文件描述符的
一個(gè)就是本文中說到的ngx_http_log_module模塊的open_file_log_cache;
一個(gè)是ngx_http_core_module模塊的 open_file_cache;
前者是只用來管理access變量日志文件。
后者用來管理的就多了,包括:static,index,tryfiles,gzip,mp4,flv,看到了沒,都是靜態(tài)文件哦!
這兩個(gè)指令的handler都調(diào)用了函數(shù) ngx_open_file_cache_init ,這就是用來管理緩存文件描述符的第一步:初始化
ngx_open_file_cache_t *
ngx_open_file_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t inactive)
{
ngx_pool_cleanup_t *cln;
ngx_open_file_cache_t *cache;
cache = ngx_palloc(pool, sizeof(ngx_open_file_cache_t));
if (cache == NULL) {
return NULL;
}
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) {
return NULL;
}
cln->handler = ngx_open_file_cache_cleanup;
cln->data = cache;
return cache;
}
可以看到nginx管理緩存文件描述符,使用了紅黑樹和隊(duì)列,這個(gè)后續(xù)還是作為一篇文章來敘述吧,涉及的內(nèi)容有點(diǎn)多,本文還是以分析日志模塊為主。
6、說一下指令 open_file_log_cache
1)nginx下默認(rèn)這個(gè)指令的配置是:open_file_log_cache off;
也就是說不對access變量日志文件的fd做緩存,每寫一個(gè)文件就打開,然后寫日志。那么這個(gè)文件fd什么時(shí)候關(guān)閉呢。
這就涉及到nginx內(nèi)存管理的cleanup了,cleanup可以注冊,在內(nèi)存池被銷毀的時(shí)候,調(diào)用cleanup鏈表中各個(gè)cleanup的handler(詳細(xì)可以去翻閱nginx內(nèi)存池管理)
而此時(shí)的文件fd就是在request完畢后,銷毀內(nèi)存池的時(shí)候,關(guān)閉fd。
配置open_log_file_cache off; 時(shí)的運(yùn)行
這是獲取access變量文件fd的函數(shù),返回值應(yīng)該是access日志的fd。
ngx_int_t
ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name,
ngx_open_file_info_t *of, ngx_pool_t *pool)
{
time_t now;
uint32_t hash;
ngx_int_t rc;
ngx_file_info_t fi;
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_cache off的話,cache為空
if (cache == NULL) {
......
//添加一個(gè)cleanup
cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));
if (cln == NULL) {
return NGX_ERROR;
}
//打開日志文件,獲得fd rc = 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回調(diào),在銷毀內(nèi)存池的時(shí)候調(diào)用ngx_pool_cleanup_file,函數(shù)內(nèi)采用了close關(guān)閉fd
cln->handler = ngx_pool_cleanup_file;
clnf = cln->data;
clnf->fd = of->fd;
clnf->name = name->data;
clnf->log = pool->log;
}
return rc;
}
......
}
2)如果配置open_file_log_cache的話,支持四種參數(shù):
max = N [ inactive = time ] [ min_uses = N ] [ valid = time ]
max 最大緩存文件描述符數(shù)量
inactive 在多少時(shí)間內(nèi)不活動,就會被刪除
min_uses 必須在 inactive時(shí)間內(nèi)活動N次,才會被緩存
valid 檢查inactive的時(shí)間。
具體的緩存文件fd的來龍去脈值得用一篇文章來詳細(xì)描述,在這里就暫且不說了,希望最近有時(shí)間整理出來。
The End
Microsoft Access在很多地方得到廣泛使用,例如小型企業(yè),大公司的部門。