前言
Redis原始碼中定義了幾個和日志相關的函式,用于將不同級別的資訊列印到不同的位置(日志檔案或標準輸出,取決于組態檔的設定),這些函式的定義位于 server.h 和server.c 檔案中,包括:
1 void serverLog(int level, const char *fmt, ...); 2 void serverLogRaw(int level, const char *msg); 3 void serverLogFromHandler(int level, const char *msg);
其中, serverLogRaw() 是 serverLog() 的底層實作,差別在于 serverLog() 進行了上層的簡單封裝,以支持可視化字串的列印,而 serverLogRaw() 則只接收完整的字串進行列印,
Redis Logging定義了四種日志等級,從低到高分別為:除錯、詳細、注意、警告,對應的宏定義如下:
1 /* Log levels */ 2 #define LL_DEBUG 0 3 #define LL_VERBOSE 1 4 #define LL_NOTICE 2 5 #define LL_WARNING 3 6 #define LL_RAW (1<<10) /* Modifier to log without timestamp */
serverLog()
serverLog() 函式提供了一個printf-alike的日志列印支持,能夠支持格式化字串,并接收可變引數,
1 void serverLog(int level, const char *fmt, ...) { 2 va_list ap; 3 char msg[LOG_MAX_LEN]; 4 5 if ((level&0xff) < server.verbosity) return; 6 7 va_start(ap, fmt); 8 vsnprintf(msg, sizeof(msg), fmt, ap); 9 va_end(ap); 10 11 serverLogRaw(level,msg); 12 }
函式中對日志列印級別進行了控制,只有給定日志級別不小于服務器設定的級別時,日志才會列印出來,否則,函式提前回傳,
利用了 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 ,日志會列印到相應的日志檔案中,否則,直接輸出至標準輸出,
- 日志列印格式,如果在日志級別中設定了原始位,則只列印原始字串資訊,否則,會在字串首加入行程號、日期等資訊,
1 void serverLogRaw(int level, const char *msg) { 2 // Defined in syslog.h 3 const int syslogLevelMap[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING }; 4 const char *c = ".-*#"; 5 FILE *fp; 6 char buf[64]; 7 int rawmode = (level & LL_RAW); 8 int log_to_stdout = server.logfile[0] == '\0'; 9 10 level &= 0xff; /* clear flags */ 11 if (level < server.verbosity) return; 12 13 fp = log_to_stdout ? stdout : fopen(server.logfile,"a"); 14 if (!fp) return; 15 16 // log without timestamp 17 if (rawmode) { 18 fprintf(fp,"%s",msg); 19 } else { 20 int off; 21 struct timeval tv; 22 int role_char; 23 pid_t pid = getpid(); 24 25 gettimeofday(&tv,NULL); 26 struct tm tm; 27 /* 28 * 自定義的localtime函式 29 * 標準的 localtime 在多執行緒下可能出現死鎖的情況 30 */ 31 nolocks_localtime(&tm,tv.tv_sec,server.timezone,server.daylight_active); 32 off = strftime(buf,sizeof(buf),"%d %b %Y %H:%M:%S.",&tm); 33 snprintf(buf+off,sizeof(buf)-off,"%03d",(int)tv.tv_usec/1000); 34 if (server.sentinel_mode) { 35 role_char = 'X'; /* Sentinel. */ 36 } else if (pid != server.pid) { 37 role_char = 'C'; /* RDB / AOF writing child. */ 38 } else { 39 role_char = (server.masterhost ? 'S':'M'); /* Slave or Master. */ 40 } 41 /* 42 * 依次存放: 43 * pid, X/C/S/M, time, .-*#, msg 44 */ 45 fprintf(fp,"%d:%c %s %c %s\n", 46 (int)getpid(),role_char, buf,c[level],msg); 47 } 48 fflush(fp); 49 50 if (!log_to_stdout) fclose(fp); 51 if (server.syslog_enabled) syslog(syslogLevelMap[level], "%s", msg); 52 }
對于包含時間資訊的日志列印會被加入如下資訊在字串首部:
- 服務器行程號
- X/C/S/M: 體現當前行程的狀態,主行程 / 從行程 / Sentinel / RDB / AOF子行程
- 格式化后的日期
- 表示不同日志等級的字符
- 資訊字串
日志列印實體:
1 2084:C 10 Sep 16:06:35.518 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 2 2084:C 10 Sep 16:06:35.518 # Redis version=4.0.9, bits=64, commit=00000000, modified=0, pid=2084, just started 3 2084:C 10 Sep 16:06: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 4 2084:M 10 Sep 16:06:35.519 # You requested maxclients of 10000 requiring at least 10032 max file descriptors. 5 2084:M 10 Sep 16:06:35.519 # Server can't set maximum open files to 10032 because of OS error: Invalid argument. 6 2084:M 10 Sep 16:06:35.519 # Current maximum open files is 4096. maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'. 7 _._ 8 _.-``__ ''-._ 9 _.-`` `. `_. ''-._ Redis 4.0.9 (00000000/0) 64 bit 10 .-`` .-```. ```\/ _.,_ ''-._ 11 ( ' , .-` | `, ) Running in standalone mode 12 |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379 13 | `-._ `._ / _.-' | PID: 2084 14 `-._ `-._ `-./ _.-' _.-' 15 |`-._`-._ `-.__.-' _.-'_.-'| 16 | `-._`-._ _.-'_.-' | http://redis.io 17 `-._ `-._`-.__.-'_.-' _.-' 18 |`-._`-._ `-.__.-' _.-'_.-'| 19 | `-._`-._ _.-'_.-' | 20 `-._ `-._`-.__.-'_.-' _.-' 21 `-._ `-.__.-' _.-' 22 `-._ _.-' 23 `-.__.-' 24 25 2084:M 10 Sep 16:06:35.523 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. 26 2084:M 10 Sep 16:06:35.523 # Server initialized 27 2084:M 10 Sep 16:06:35.523 # WARNING overcommit_memory is set to 0! 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. 28 2084:M 10 Sep 16:06:35.525 * DB loaded from disk: 0.002 seconds 29 2084:M 10 Sep 16:06:35.525 * Ready to accept connections
nolocks_localtime()
值得一提的是,在對時間進行轉換時,Redis原始碼考慮到標準的 localtime() 在多執行緒下可能出現的死鎖問題,所以自定義了一個不帶鎖的函式 nolocks_localtime() ,用于完成時間格式的轉換,
關于 localtime() 函式多執行緒下可能帶來的死鎖問題,可以參考這篇博文的分析:https://juejin.im/post/6844903775539298318,
參考資料:
localtime函式的死鎖風險 : https://juejin.im/post/6844903775539298318
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/4086.html
標籤:NoSQL
