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),参考资料见这里 ...
随机推荐
- volatile的特性代码验证
一 . 可见性(visibility) volatile关键字修饰的变量,如果值发生了改变,其他线程会立刻获取到,从而避免了出现脏读的情况. public class TestVolatile { p ...
- 什么是P,NP和NPC问题?
P问题,NP问题,NPC问题?这些都是计算机科学领域,关于算法方面的术语.在认识这些术语之前,建议同学们先认真学习一下算法的时间复杂度,因为算法的时间复杂度与P,NP和NPC问题高度相关. 什么是P问 ...
- 如何解决spring boot 项目导入依赖后代码报错问题
如何解决spring boot 项目导入依赖后代码报错问题 2020-08-15 14:17:18 代码截图如图所示(由于本人问题已经解决,没来得及截图,所以在网上找了一张图片)
- 【CF1174D】 Ehab and the Expected XOR Problem - 构造
题面 Given two integers \(n\) and \(x\), construct an array that satisfies the following conditions: · ...
- 碰到 Json_CSRF 怎么办?
前言 在最近挖洞的时候,老是碰到 POST 传参采用 JSON 格式,而不是传统的parameter=value的格式,之前也没接触过,所以也不知道该怎么搞,所以打算学习一下,此文作为一个笔记梳理. ...
- 准确率99.9%的离线IP地址定位库
Ip2region是什么? ip2region - 准确率99.9%的离线IP地址定位库,0.0x毫秒级查询,ip2region.db数据库只有数MB,提供了java,php,c,python,nod ...
- Python3 执行JS出现JSON未定义问题
two = function(e) { var t = e.data; e.url.match(/(https?:)?(\/\/)([^/]*)/) || (e.url = "https:/ ...
- QT下载速度慢的解决方法
在官网的下载速度实在太慢了 找到了一个镜像网站 https://mirrors.tuna.tsinghua.edu.cn/qt/archive/qt/
- java基础-01:dos命令
进入cmd方式 win + r 输入 cmd 随便进入一个文件夹 在导航栏输入cmd,回车即进入命令行 命令 盘符切换:直接输入 E: ,回车即可 查看文件夹下所有目录:dir 切换目录: cd 跨盘 ...
- eclipse git如何切换分支,拉取代码,合并代码,解决冲突等
(如果想看eclipse拉取git项目,移步到我上一篇文章)以下步骤是eclipse运用git的切换分支,拉取合并代码的基本操作: 1.切换远程分支:鼠标右键项目--team--switch to - ...