Redis源码笔记--服务器日志和函数可变参数处理server.c
前言
Redis源码中定义了几个和日志相关的函数,用于将不同级别的信息打印到不同的位置(日志文件或标准输出,取决于配置文件的设置),这些函数的定义位于 server.h 和server.c 文件中,包括:
void serverLog(int level, const char *fmt, ...);
void serverLogRaw(int level, const char *msg);
void serverLogFromHandler(int level, const char *msg);
其中, serverLogRaw() 是 serverLog() 的底层实现,差别在于 serverLog() 进行了上层的简单封装,以支持可视化字符串的打印,而 serverLogRaw() 则只接收完整的字符串进行打印。
Redis Logging定义了四种日志等级,从低到高分别为:调试、详细、注意、警告。对应的宏定义如下:
/* Log levels */
#define LL_DEBUG 0
#define LL_VERBOSE 1
#define LL_NOTICE 2
#define LL_WARNING 3
#define LL_RAW (1<<10) /* Modifier to log without timestamp */
serverLog()
serverLog() 函数提供了一个printf-alike的日志打印支持,能够支持格式化字符串,并接收可变参数。
void serverLog(int level, const char *fmt, ...) {
va_list ap;
char msg[LOG_MAX_LEN];
if ((level&0xff) < server.verbosity) return;
va_start(ap, fmt);
vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
serverLogRaw(level,msg);
}
函数中对日志打印级别进行了控制,只有给定日志级别不小于服务器设置的级别时,日志才会打印出来,否则,函数提前返回。
利用了 va_list 、va_start 、va_end 等函数对可变参数进行了支持,其原理是利用函数参数在栈中的空间排布,栈空间排布和va_list 的操作如下图所示:
int vsnprintf (char * s, size_t n, const char * format, va_list arg) 函数利用可变参数列表来格式化字符串,把字符串保存在 s 指向的空间中。
serverLogRaw()
serverLogRaw()函数是日志打印的底层实现,主要控制了日志打印的如下几个方面:
- 日志级别。 如果给定级别低于服务器设置的server.verbosity 级别,日志不输出,函数直接返回。
- 日志打印位置。如果服务器配置了 server.logfile ,日志会打印到相应的日志文件中。否则,直接输出至标准输出。
- 日志打印格式。如果在日志级别中设置了原始位,则只打印原始字符串信息。否则,会在字符串首加入进程号、日期等信息。
void serverLogRaw(int level, const char *msg) {
// Defined in syslog.h
const int syslogLevelMap[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING };
const char *c = ".-*#";
FILE *fp;
char buf[];
int rawmode = (level & LL_RAW);
int log_to_stdout = server.logfile[] == '\0';
level &= 0xff; /* clear flags */
if (level < server.verbosity) return;
fp = log_to_stdout ? stdout : fopen(server.logfile,"a");
if (!fp) return;
// log without timestamp
if (rawmode) {
fprintf(fp,"%s",msg);
} else {
int off;
struct timeval tv;
int role_char;
pid_t pid = getpid();
gettimeofday(&tv,NULL);
struct tm tm;
/*
* 自定义的localtime函数
* 标准的 localtime 在多线程下可能出现死锁的情况
*/
nolocks_localtime(&tm,tv.tv_sec,server.timezone,server.daylight_active);
off = strftime(buf,sizeof(buf),"%d %b %Y %H:%M:%S.",&tm);
snprintf(buf+off,sizeof(buf)-off,"%03d",(int)tv.tv_usec/);
if (server.sentinel_mode) {
role_char = 'X'; /* Sentinel. */
} else if (pid != server.pid) {
role_char = 'C'; /* RDB / AOF writing child. */
} else {
role_char = (server.masterhost ? 'S':'M'); /* Slave or Master. */
}
/*
* 依次存放:
* pid, X/C/S/M, time, .-*#, msg
*/
fprintf(fp,"%d:%c %s %c %s\n",
(int)getpid(),role_char, buf,c[level],msg);
}
fflush(fp);
if (!log_to_stdout) fclose(fp);
if (server.syslog_enabled) syslog(syslogLevelMap[level], "%s", msg);
}
对于包含时间信息的日志打印会被加入如下信息在字符串首部:
- 服务器进程号
- X/C/S/M: 体现当前进程的状态,主进程 / 从进程 / Sentinel / RDB / AOF子进程
- 格式化后的日期
- 表示不同日志等级的字符
- 信息字符串
日志打印实例:
:C Sep ::35.518 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
:C Sep ::35.518 # Redis version=4.0., bits=, commit=, modified=, pid=, just started
:C Sep ::35.518 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
:M Sep ::35.519 # You requested maxclients of requiring at least max file descriptors.
:M Sep ::35.519 # Server can't set maximum open files to 10032 because of OS error: Invalid argument.
:M Sep ::35.519 # Current maximum open files is . maxclients has been reduced to to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'.
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 4.0. (/) bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in standalone mode
|`-._`-...-` __...-.``-._|'` _.-'| Port:
| `-._ `._ / _.-' | PID: 2084
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-' :M Sep ::35.523 # WARNING: The TCP backlog setting of cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of .
:M Sep ::35.523 # Server initialized
:M Sep ::35.523 # WARNING overcommit_memory is set to ! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
:M Sep ::35.525 * DB loaded from disk: 0.002 seconds
:M Sep ::35.525 * Ready to accept connections
nolocks_localtime()
值得一提的是,在对时间进行转换时,Redis源码考虑到标准的 localtime() 在多线程下可能出现的死锁问题,所以自定义了一个不带锁的函数 nolocks_localtime() ,用于完成时间格式的转换。
关于 localtime() 函数多线程下可能带来的死锁问题,可以参考这篇博文的分析:https://juejin.im/post/6844903775539298318。
参考资料:
localtime函数的死锁风险 : https://juejin.im/post/6844903775539298318
Redis源码笔记--服务器日志和函数可变参数处理server.c的更多相关文章
- redis源码笔记(一) —— 从redis的启动到command的分发
本作品采用知识共享署名 4.0 国际许可协议进行许可.转载联系作者并保留声明头部与原文链接https://luzeshu.com/blog/redis1 本博客同步在http://www.cnblog ...
- Redis源码分析:serverCron - redis源码笔记
[redis源码分析]http://blog.csdn.net/column/details/redis-source.html Redis源代码重要目录 dict.c:也是很重要的两个文件,主要 ...
- Redis 源码简洁剖析 07 - main 函数启动
前言 问题 阶段 1:基本初始化 阶段 2:检查哨兵模式,执行 RDB 或 AOF 检测 阶段 3:运行参数解析 阶段 4:初始化 server 资源管理 初始化数据库 创建事件驱动框架 阶段 5:执 ...
- Redis源码笔记-初步
目录 目录 1 1. 前言 2 2. 名词 2 3. dict.c 2 3.1. siphash算法 2 3.2. 核心函数 3 3.3. 核心宏 3 3.4. 核心结构体 3 3.4.1. dict ...
- redis源码笔记-内存管理zmalloc.c
redis的内存分配主要就是对malloc和free进行了一层简单的封装.具体的实现在zmalloc.h和zmalloc.c中.本文将对redis的内存管理相关几个比较重要的函数做逐一的介绍 参考: ...
- redis源码笔记 - redis-cli.c
这份代码是redis的client接口,其和server端的交互使用了deps目录下的hiredis c库,同时,在这部分代码中,应用了linenoise库完成类似history命令查询.自动补全等终 ...
- Redis 源码简洁剖析 09 - Reactor 模型
Reactor 模型 事件驱动框架 Redis 如何实现 Reactor 模型 事件的数据结构:aeFileEvent 主循环:aeMain 函数 事件捕获与分发:aeProcessEvents 函数 ...
- 曹工说Redis源码(4)-- 通过redis server源码来理解 listen 函数中的 backlog 参数
文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis,而怎么才能懂,光看是不够的,建议跟着下面的这一篇,把环境搭建起来,后续可以自己阅读源码,或者跟着我这边一起阅读.由于 ...
- Redis源码阅读笔记(1)——简单动态字符串sds实现原理
首先,sds即simple dynamic string,redis实现这个的时候使用了一个技巧,并且C99将其收录为标准,即柔性数组成员(flexible array member),参考资料见这里 ...
随机推荐
- Android 内部存储读写介绍
内部存储读写 内容介绍 Android系统允许应用程序创建仅能够自身访问的私有文件,文件保存在设备的内部存储器上,在Linux系统下的/data/data//files目录中 Android系统不仅支 ...
- 解决@ResponseBody不能和 <mvc:annotation-driven>同时使用的问题
我们都知道使用Springmvc的ajax很强大只要三步就可以实现: 1.引入jackson的maven到pom文件: <dependency> <groupId>com.fa ...
- Ubuntu 18.04 安装、配置和美化
本文章修改自inkss的博文 为什么要修改原文? 本来我是想自己写这么一篇的,但是没想到在百度一搜,居然一篇好文.我之前没看到,真是可惜. 这篇文章主要是帮助我记录一些东西,如题. 原文虽然不错,但是 ...
- 41. The Security Namespace
41.1 Web Application Security网络应用安全 41.1.1 <debug> 启用spring安全调试基础架构.这将提供人类可读的(多行)调试信息来监控进入安全过滤 ...
- 动态数组java实现
数组是一种顺序存储的线性表,所有元素的内存地址是连续的. 动态数组相对于一般数组的优势是可以灵活地添加或删除元素.而一般数组则受限于固定的内存空间.只能有限的添加元素 动态数组(Dynamic Arr ...
- Hadoop的源码编译
目录 正文 1.准备阶段 使用root登录Centos,并且要求能够正常连接网络.配置清单如下: (1)hadoop-2.7.2-src.tar.gz (2)jdk-8u144-linux-x64.t ...
- 使用分区挂载 ftp 目录
ftp挂载分区上去后无法识别的问题困扰了我好几天,今天有时间把它研究了一下,拿出来与大家分享一下. ftp目录挂载分区前与挂载分区后的区别[root@localhost ~]# ls -Zd /var ...
- Excel数据分析 --数据透析表
数据透析表主要用于各种数据总汇,对各项数据指标进行分类统计 实例分析 如下所示:是一份销售流水数据,有时间,地区,销售员,商品名称,数量,单价和金额几个字段,如下所示: 现在针对不同的数据汇总需求,可 ...
- 给你的Swagger文档换套附魔皮肤吧
前言 相信无论是前端或是后端的程序员对Swagger都不怎么陌生,没有用过应该也听说过 Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格的 Web 服务. 简 ...
- 神舟zx6-ct5da装黑苹果Macos 10.15.6记录
可能是一时脑子抽风,突然就想体验一把mac系统.以前就了解过,给非苹果电脑装macos叫黑苹果,emmmmm.好吧,给我的神船也整一个. 看了很多个视频,整理一下装黑苹果过程.本人电脑系统是win10 ...