欢迎登陆真网站,您的到来是我们的荣幸。 登陆 注册 忘记密码? ☆设为首页 △加入收藏
欢迎加入真幸福QQ群
电脑知识: 基础知识 网络技术 操作系统 办公软件 电脑维修 电脑安全 windows7 windows8 windows10 服务器教程 平板电脑 视频播放教程 网络应用 互联网 工具软件 浏览器教程 QQ技巧 输入法教程 影视制作 YY教程 wps教程 word教程 Excel教程 PowerPoint
云南西双版纳特产小花糯玉米真空包装


Ubuntu中保存iptables防火墙规则的例子
360云盘如何获取经验值来升级
Linux系统unzip解压后中文名乱码解决方法
LINUX中RSA认证登录SSH
如何提升360云盘空间
基于corosync+pacemaker的nginx高可用集群安装配置
CS1.5服务器连接常见问题解决
Linux系统分区知识点归纳
360云盘等级有什么用
ubuntu系统下删除软件的教程
Nginx的error_log和Access_log分析
【 来源:网络 】【 点击:1 】 【 发布时间:2017_03_03 08:59:59 】

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

  1、error_log

  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。

  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 },

  }

  这样会产生几个疑问:

  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_CONF match,执行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存储在http core模块数据结构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,如下:

  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;

  }

  }

  。。。。。。

  }

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

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

  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有三个指令:

  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

  };

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

  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;

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

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

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

  access_log logs/'$remote_addr'access.log main;

  3、不管是变量路径还是常量路径,都将信息放入了 ngx_http_log_loc_conf_t的logs这个数组里进行管理,logs数组的元素是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;

  当是常量的时候使用file记录,这个文件记录会放入到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;

  };

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

  typedef struct {

  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.log main;

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

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

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

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

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

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

  打日志的函数:

  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有两个指令是管理缓存文件描述符的

  一个就是本文中说到的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_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管理缓存文件描述符,使用了红黑树和队列,这个后续还是作为一篇文章来叙述吧,涉及的内容有点多,本文还是以分析日志模块为主。

  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_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) {

  ......

  //添加一个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回调,在销毁内存池的时候调用ngx_pool_cleanup_file,函数内采用了close关闭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的话,支持四种参数:

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

  max 最大缓存文件描述符数量

  inactive 在多少时间内不活动,就会被删除

  min_uses 必须在 inactive时间内活动N次,才会被缓存

  valid 检查inactive的时间。

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

  The End

本网站由川南居提供技术支持,fkzxf版权所有 浙ICP备12031891号
淳安分站 淳安分站