用到第三方庫使用printf打印調試信息的時候往往需要重定向日志到文件, 但如果沒有源碼或修改點較多時就比較麻煩. 這里提供兩個重定位方法:
1. 修改值為1的文件描述符
默認printf打印指向標准輸出stdout(fd=1), 最終指向終端. 因此可以關閉值為1的文件描述符再打開另一文件, 之后printf打印就被寫入該文件中. 注意這種方法必須保證關閉fd與打開文件之間不會有其它文件操作(內核分配fd是順序分配的, 如有第三個文件打開則第三個文件的fd被設為1), 且fork進程會保留對應的fd, 即新進程也會操作同一文件, 需要做好互斥(可以通過關閉子進程fd來解決這個問題).
2. 修改printf實現
定義同名同類型的printf並修改其實現. 這里要注意一點, 默認打印不帶參數的字符串時gcc會將printf優化為puts, 解決辦法有兩個: 一是同樣重定義puts, 二是編譯時添加-fno-builtin-printf.
1 #include <string.h> 2 #include <stdlib.h> 3 #include <stdio.h> 4 #include <fcntl.h> 5 #include <stdarg.h> 6 #define REDIRECT 7 #undef REDIRECT 8 #ifdef REDIRECT 9 int fd = -1; 10 #else 11 FILE *fp = NULL; 12 int printf(const char *fmt, ...) 13 { 14 va_list args; 15 int ret = 0; 16 va_start(args, fmt); 17 ret = vfprintf(fp, fmt, args); 18 va_end(args); 19 fflush(fp); 20 return ret; 21 } 22 /* 23 int puts(const char *s) 24 { 25 //add \n here to avoid buffering 26 return printf("%s\n", s); 27 } 28 */ 29 #endif 30 int main() 31 { 32 #ifdef REDIRECT 33 close(1); 34 35 fd = open("./out", O_RDWR | O_CREAT, 0755); 36 if (1 != fd) 37 { 38 fprintf(stderr, "open file fail with %d\n", fd); 39 return -1; 40 } 41 printf("this line shall written into file!\n"); 42 //flush cache, man stdout for detail 43 fsync(fd); 44 #else 45 fp = fopen("./out", "w+"); 46 if (NULL == fp) 47 { 48 fprintf(stderr, "open file fail\n"); 49 return -1; 50 } 51 const char cmd[] = "this line shall written into file!\n"; 52 printf("this line shall written into file!\n"); 53 printf("this line shall written into file! more args %p\n", fp); 54 #endif 55 return 0; 56 }