As one master once said, "Even simple programs have bugs." ", so when the error of the program can be accurate positioning problem is to ensure the quality of software is the key, and how to accurately locate the problem." This requires a program error when possible to spit out more information, the best situation is to locate the problem to specific files and statements and then stack information ..., so that can help programmers quickly positioning problems, this is the ultimate goal of software testability.
We usually use some test methods, such as using Assert in the code, printing some program execution in the screen or control, and using debug to directly trace the program's statement, But these simple tests are a bit of a little retroactive to the hidden bugs in those programs, especially for some server-level programs, they all need to run for a long time, sometimes out of error and allow to stop immediately, which requires the program must save the error message for later to analyze it, There are a lot of logs inside Windows systems that not only allow developers to locate problems, they can also help users better use the system. In fact, all the programs have some of the same requirements for testability, so we can extract these requirements to abstract into a separate system (class), so that all programs can provide a unified testing interface.
2. Some key points of design log class
1) can be saved into a separate file, as the above analysis, some programs to perform a long time, must be the program to save the execution of information, and the program is closed after the test information can not be eliminated with the program, so there is no better than to save this information on the disk a good way.
2 The method of printing the log must be reentrant, this does not need more explanation, ask now which software is not multithreading, in order to let the thread between the printed information does not interfere with each other, our printing method must support multiple threads.
3 Set the level of printing requirements, level of granularity due to the need, generally include, error information, alarm information and general information three categories, so that users can, according to the need, through the log configuration file to select the need to print information, rather than a brain to all the information printed out, The focus is not prominent but is not conducive to the positioning of the problem.
4 the printing interface to design flexible, you can let the user free to print any information. In some cases printing is also to print the stack information at the time, which may be string, plastic or floating-point type, and so require the printed interface to use a similar printf method to achieve.
5 The printed content should be in a uniform format, generally including:
Time Level [File/function] [Information content]
3. Simple Code Implementation
Through the above introduction, we will implement it through the code, this is just a simple implementation, and even the code is not complete, intended to explain the problem, the reader can be based on their own to improve.
/* MyLog.h Header file */#ifndef mylog_h #define MYLOG_H/* Log alarm level/typedef enum {Level_none =
0,/* Do not output log * * Level_error, level_warning, Level_info, Level_butt} level_type; extern const Char log_config_file[];
The profile name of the log file is const int max_log_length = 1024;
const int max_file_name_length = 255;
/x different levels of the corresponding string x/const char LOG_ERROR[][10] = {"", "ERROR", "Warning", "Info"};
/* Config item in configuration file/const char log_config_section[] = "LOG"; const char log_config_leveltype[] = "LevelType"; Level of print Log const char log_config_filename[] = "FILENAME";
Log file name/* Simplify the Call Log interface macro/#define Write_log clog::getinstance ()->writelog Class Cmylog { public:cmylog* getinstance (); Gets an instance of the log class void Writelog (Level_type level, const char* szTitle, const char* format,...);
Log print interface, variable protected:cmylog ();
~cmylog ();
virtual void Printlog (const char* szmsg, Level_type ilevel); Print output information void getdatestring (char* szdate); Obtains the date, realizes omits the void gettimestring (char* sztime);
Obtain the time, realizes omits Private:level_type M_ilevel; Char M_szfilename[max_file_name_length]; Output log file name critical_section M_cs; function's reentrant control FILE *m_file;
Log file} #endif/* MyLog.cpp Original FILE * * * * * #include "MyLog.h" #include "string.h" #include "stdio.h"
Cmylog::cmylog () {/* initialization variable/m_ilevel = Level_none;
InitializeCriticalSection (&m_cs);
/* Read configuration file */char szconfigfile[max_file_name_length] = {0};
strcpy (Szconfigfile, log_config_file);
M_ilevel = Getprivateprofileint (log_config_section, Log_config_leveltype, M_ilevel, Szconfigfile);
Read the LevelType entry in the. ini file, please refer to MSDN char Szfilename[max_file_name_length + 1]; GetprivateProfileString (Log_config_section, Log_config_filename, "", szFileName, Max_file_name_length, Szconfigfile);
Read the filename item in the. ini file, refer to the MSDN strcpy (M_szfilename, szFileName);
strcat (M_szfilename, log_file_name);
} cmylog::~cmylog () {if (NULL!= m_file) {fclose (m_file);
M_file = NULL;
} deletecriticalsection (&m_cs);
} cmylog* cmylog::getinstance () {static cmylog log;
Return &log;
} void Cmylog::writelog (Level_type level, const char* szTitle, const char* format,...)
{/* Input check */if (NULL = format) {return;
}/* Level control */if (Levels > M_ilevel) {return;
}/* Composition to the output string/char Szmsg[max_log_length + 1];
/* Date Time * * strcpy (szmsg, "["); Gettimestring (&szmsg[strlen (szmsg)));
strcat (szmsg, "]");
/* Level/* STRCAT (szmsg, "[");
if (Level >= Level_butt | | level <= level_none) {return;
else {strcat (szmsg, Log_error[level]);
} strcat (szmsg, "]");
/* Title */if (NULL!= szTitle) {strcat (szmsg, "[");
strcat (szmsg, szTitle);
strcat (szmsg, "]:");
}/* Content/* va_list ARGP;
Va_strat (Argp,format);
_vsnprintf (&szmsg[strlen (szmsg)), Max_log_length-strlen (szmsg), format, ARGP);
Va_end (ARGP);
* * Output Log/Printlog (szmsg, level); } void Cmylog::P rintlog (const char* szmsg, Level_type ilevel) {/* Reentrant control of/* Function/Entercriticalsectio
N (&m_cs);
/* Output to File */if (ilevel <= m_ilevel) {if (NULL = = m_file) {m_file = fopen (M_szfilename, "A +");
} if (NULL = = m_file) {ASSERT (false);
else {fprintf (m_file, "%s\n", szmsg);
Fflush (M_file)//To write the contents of the buffer immediately to disk} leavecriticalsection (&m_cs); }
4. How to use Log in code
First of all, to the above log header file and the original file into the project to compile, and in the path of the executable program to have an. ini configuration file, and then to define the global variables, and finally as long as the code needs to be printed where the use of Write_log macros on it, as follows:
/* Log.ini configuration file * *
[log]
LevelType = ERROR //Log print level control
filename = Log.txt//log filename/
* Print with log in your own code * /
... ...
#include "MyLog.h"
const char log_config_file[] = "Log.ini";//The profile name of the log file
...
int Mysocket::sendto (SOCKET sock, const char* szbuffer, DWORD dwsize)
{
if (socket_error = Send (sock, szbuffer , dwsize, 0)
{
write_log (level_error, "Mysocket::sendto", "Send%s failed! GetLastError:%d ",
Szbuffer, WSAGetLastError ());
return-1;
}
return SUCCESS;
}
In the above code, you can implement log printing, in the course of the execution of the socket if send failed, you will print a message in the Log.txt file to help developers locate the problem, as follows:
[ 23:44:12][error][mysocket::sendto]send Hello Word failed! getlasterror:10060
5. Later improvements
This simple log class simply presents a way to print the log for a program, Readers are also required to continue to improve, for example, if the program execution time is relatively long, log printing information will be very much, followed by the log file will be very large, not conducive to analysis, so you need to specify the size of log file; Frequent printing of strings in code can also have a certain effect on the performance of the program, So you can encode a number of fixed strings, printing only need to print the encoding, of course, can also develop a specific to the log file analysis software, so as to improve the efficiency of the problem positioning.
Current research on code testability has matured in the industry, and interested readers can refer to the appropriate log printing method for Aces.