Hierarchical design of C language log

Posted by cwncool on Mon, 27 Dec 2021 02:13:16 +0100

Original link: https://blog.csdn.net/dosthing/article/details/82828937

preface

C language is a process oriented programming language. Its program design process is to write a series of functions according to the business requirements of the upper layer, supplemented by if, for, while, switch and other process control statements to realize the processing of various data. Considering the reliable operation of the program, we hope that the execution process of the program can be controlled and understood by the programmer, which is very important in the process of program development and later maintenance of the program. The best way to realize this function is to add log print tracking at the key nodes of the program. A good log printout design can enable programmers to quickly locate the problem of program running errors, whether in program development or later maintenance, and improve programmers' ability to deal with bugs. Generally, in the open method test stage, we hope to output the key information of program operation as much as possible to help repair and improve the bugs in the initial stage of software development. However, after the later software release, we hope to retain only as few key printouts as possible to improve the efficiency of program operation. For this contradictory demand, we can use hierarchical log printing output.

Log classification concept

Referring to the hierarchical log design of some high-level languages such as Java, we can divide the log into five levels: DEBUG, INFO, WARN, ERROR and FATAL ERROR.

DEBUG: it is mainly used for the printout in the program development and testing stage to verify whether the design logic of the program meets the design requirements of the upper application. It can be turned off after the release program has been tested and verified.

INFO: this level of printout is used to tell testers or developers some special information, such as the operation of some key data.

WRAN: This is a warning printout, which is generally used to output warning prints such as user input wrong data. This level of printout is also recommended to be retained after the program is released to facilitate the maintenance and tracking of the later program.

ERROR: the printing of running errors and excellent program design have good fault tolerance performance. They can correct themselves when the program runs errors. For example, in case of data request errors in network programming, we can enable the method of reconnection mechanism to try to solve the problem of program running errors. This level of printing cannot be closed in the released software, otherwise we can't get some feedback from the released software to guide our new program optimization design.

FATAL ERROR: when the program runs into this level of problem, it is difficult to repair. Generally, it is accompanied by the flash back or restart of the program. At this time, the printing of FATAL ERROR is very critical, which can help us locate the cause of the running of the program. The printing of FATAL ERROR cannot be closed at any time.

Log hierarchical design

In C language, the output control of hierarchical log can be realized with the help of powerful macros; The hierarchical configuration of log output can be specified by the running parameters of the program or realized by other means such as configuration file; For the standard control of the format of the output content, the information such as real-time time time, program running file, function and line number are generally added at the beginning of each line to realize rapid query and positioning; The output location of log content can be serial port printing or log information file. It is recommended to use log file to save.

Commonly used #, ## and in C language__ VA_ARGS__ Isomacro introduction

1.#

If you want to include macro parameters in a string, ANSI C allows this. In the replacement part of a class function macro, # symbols are used as a preprocessing operator, which can convert language symbols into strings. For example, if x is a macro parameter, then #x you can convert the parameter name to the corresponding string. This process is called stringing.

//The square of x can be achieved this way
 #incldue <stdio.h>
 #define PSQR(x) printf("the square of" #x "is %d.\n",(x)*(x))

2.##

##Operator can be used in the replacement part of a class function macro. In addition, ## it can also be used for the replacement part of class object macros. This operator combines two language symbols into a single language symbol. For example:

#include <stdio.h>
#define XNAME(n) x##n
#define PXN(n) printf("x"#n" = %d\n",x##n)
#define WriteLog2(level,format, arg...) \
log_mesg_printf2( __FILE__,__FUNCTION__, __LINE__, level, format, ##arg)// When arg When it is an empty parameter, ## operator "eats" the sign in front of it to solve the compilation problem.
 
int main(void)
 {
     int XNAME(1)=12;//int x1=12;
     PXN(1);//printf("x1 = %d\n", x1);
     return 0;
 }

3. Variable parameter macros... And_ VA_ARGS _

VA_ Args is a variable parameter macro, which is added in the new C99 specification, At present, it seems that only gcc is supported (VC6.0 compiler does not support it). The implementation idea is that the last parameter in the parameter list in the macro definition is an ellipsis (that is, three points). In this way, the predefined macro VA_ARGS can be used in the replacement part to replace the string represented by the ellipsis. For example:

#define PR(...) printf(__VA_ARGS__)
 int main()
 {
     int wt=1,sp=2;
     PR("hello\n");
     PR("weight = %d, shipping = %d",wt,sp);
     return 0;
 }

Implementation example

The sample is very simple. The function is realized by using the idea of log classification and variable parameter macros. When necessary, the whole sample code can control whether to compile log output through compilation options to reduce the code; In addition, in the commissioning stage, the configuration parameters can be used to realize different levels of log printing and output.

/****************************************************************
***Author: lishuangliang		                      ***
***Email: lishuangliang@outlook.com			      ***
***Date: 2018-09-24					      ***	
***Festivsl: Mid-autumn			          ***								
*****************************************************************/
#include <string.h>  
#include <errno.h>  
#include <stdio.h>  
#include <stdint.h>  
#include <stddef.h>  
#include <stdlib.h>  
#include <sys/stat.h>  
#include <sys/types.h> 
#include <stdarg.h>
#include <stdbool.h>
#include <sys/un.h>
#include <sys/param.h>
#include <time.h> 
 
//Use macros to control whether log output is turned on
#ifdef DISABLE_DEBUG
#define real_debug_level 0
#else
#define real_debug_level debug_level
#endif
 
 
//Define log output level
#define 	FATALEER    (1<<0)
#define 	ERROR       (1<<1)
#define 	WRAN        (1<<2)
#define 	INFO      (1<<3)
#define 	DEBUG    (1<<4) 
 
#define WriteLog(level,mesg) log_mesg_printf(__FILE__, __LINE__, __func__, level, mesg)
#define WriteLog2(level,format, arg...) log_mesg_printf2( __FILE__,__FUNCTION__, __LINE__, level, format, ##arg)
 
int debug_level = 0;
 
struct dbg {
    int level;
    const char *mesg;
};
 
static struct dbg debug_level_table[] = {
    {FATALEER, "Config The Log Level as FATALEER"},
    {ERROR, "Config The Log Level as ERROR"},
    {WRAN, "Config The Log Level as WRAN"},
    {INFO, "Config The Log Level as INFO"},
    {DEBUG, "Config The Log Level as DEBUG"}
};
 
void print_debug_usage(void)
{
    struct dbg *p;
 
    fprintf(stderr,
            "  To calculate the debug level, logically 'or'\n"
            "  some of the following values together to get a debug level:\n");
    for (p = debug_level_table;
         p <
         debug_level_table +
         (sizeof (debug_level_table) / sizeof (struct dbg)); p++) {
        fprintf(stderr, "\t%d:\t%s\n", p->level, p->mesg);
    }
    fprintf(stderr, "\n");
}
 
void parse_debug(char *foo)
{
    int i;
    struct dbg *p;
 
    if (!foo)
        return;
    fprintf(stderr, "Before parse_debug, debug_level is: %d\n",
            debug_level);
 
    i = atoi(foo);
    if(i == -1) 
	{
		/* error */
		fprintf(stderr, "Invalid level specified.\n");
		exit(0);
    }    
    for (p = debug_level_table;p < debug_level_table +(sizeof (debug_level_table) / sizeof (struct dbg)); p++)
	{
        if (i > 0) {
            if (i & p->level) {                
                fprintf(stderr, "Enabling %s debug level.\n",p->mesg);
                debug_level |= p->level;
            }
        } 
    }
    fprintf(stderr, "After parse_debug, debug_level is: %d\n",
            debug_level);
}
 
char *get_commonlog_time(void)
{
	char *p;
	char sys_time[64];	
	time_t tnow = time(NULL);	
	struct tm *ptm = localtime(&tnow);
	
	memset(sys_time, 0 ,sizeof(sys_time));
	sprintf(sys_time, "%04d-%02d-%02d %02d:%02d:%02d",ptm->tm_year+1900 ,ptm->tm_mon+1 ,ptm->tm_mday ,ptm->tm_hour ,ptm->tm_min ,ptm->tm_sec);
	//return (char *)sys_time;
	p = sys_time;
	return p;
}
 
void log_mesg_printf(const char *file, int line, const char *func,int level, const char *mesg)
{
    if(real_debug_level & level)
	{
		int errno_save = errno;
		fprintf(stderr, "%s%s:%d (%s) - ", get_commonlog_time(), file, line, func);
		errno = errno_save;
		perror(mesg);
		errno = errno_save;
	}	  
}
 
void log_mesg_printf2(const char *file,const char *func,const int line, int level, char *fmt,...)
{
	if(real_debug_level & level)
	{
		char msg_buf[20*1024];
		va_list ap;
		va_start(ap,fmt);
		sprintf(msg_buf,"[%s  %s:%s:%d] ",get_commonlog_time(),file,func,line);
		vsprintf(msg_buf+strlen(msg_buf),fmt,ap);
		fprintf(stderr,"%s\n",msg_buf);		
		va_end(ap);
	}
}
 
int main(int argc, char* argv[])
{
	
#ifdef DISABLE_DEBUG
	print_debug_usage();
	parse_debug(argv[1]);//Parse log printout level
#endif
	//Sample parsing without variable parameters
	WriteLog(DEBUG,"I want to DEBUG");
	WriteLog(INFO,"I want to INFO");
	WriteLog(WRAN,"I want to WARN");
	WriteLog(ERROR,"I want to ERROR");
	WriteLog(FATALEER,"I want to FATALEER");
	//Sample parsing using variable parameters
	WriteLog2(DEBUG,"I want to %s which level is %d","DEBUG",DEBUG);
	WriteLog2(INFO,"I want to %s which level is %d","INFO",INFO);
	WriteLog2(WRAN,"I want to %s which level is %d","WRAN",WRAN);
	WriteLog2(ERROR,"I want to %s which level is %d","ERROR",ERROR);
	WriteLog2(FATALEER,"I want to %s which level is %d","FATALEER",FATALEER);
	
	return 0;
}

Effect of sample running under linux

summary

The log classification source code has managed the code cloud, and there is no warning during compilation and operation. Click here to download. The quality of log function design affects the development efficiency of the whole program engineering and later operation and maintenance. Hierarchical log design can solve this problem. Through the analysis of hierarchical logs, problems can be quickly located and maintenance procedures can be optimized. Mid Autumn Festival, summarize, sort out, reprint and explain the source.

reference

https://www.cnblogs.com/yulinfeng/archive/2017/10/18/7689388.html

https://blog.csdn.net/bat67/article/details/77542165

https://yq.aliyun.com/articles/399064

Topics: C Back-end Embedded system