Write a daemon on Windows (4) log remaining, windows daemon
Write a daemon on Windows (4) logs remaining
This time I will talk about other log-related things.
I. vaformat
The C ++ log interface generally has two forms: Stream Input and printf.
I use the printf format because the stream input is not easy to control the format.
The printf format requires the log interface to support the variable length parameter. Instead of directly supporting the variable length parameter in the log implementation class, I only accept one string parameter. For details, see article 1.
Why?
This is the case if you want to become an indefinite parameter.
bool log_string(const LOG_LEVEL level, const char* file, const int line, const char *s, ...);
In every log_xxx variant, the set of code _ vsnprintf_s will be written, and it will be exactly the same (I don't know whether _ VA_ARGS _ macros can be passed ), this is obviously a bad practice.
I put the processing of the variable length parameter in the macro definition, like:
#define ErrorLog(s, ...) _Log(LOG_ERROR, __FILE__, __LINE__, vaformat(MAX_LOG_BUFFER, s, __VA_ARGS__))
Vaformat is used to process parameters with an indefinite length:
std::string vaformat(const size_t max_size, const char* msg, ...);std::wstring vaformat(const size_t max_size, const wchar_t* wmsg, ...);
Because you do not know the length after formatting, you must specify the maximum length. If the length after formatting is greater than the maximum length, it is truncated. There is also a small trick in vaformat. When the specified max_size is smaller than 1024, use the stack space. Otherwise, apply for heap memory, which is from std :: string implementation-SSO short string optimization.
Let's take a look at the vaformat implementation. The two versions of the Code are basically the same. This is certainly not good, but I don't know how to merge them. This is a todo. Similar problems are listed below.
Ii. CLastErrorFormat
This is used to solve the LastErrorCode record mentioned in the first article.
Its main function is to convert the error code into a text description. A proper structure can also save the call of GetLastError:
class CLastErrorFormat : public boost::noncopyable{public: CLastErrorFormat() : m_code(GetLastError()) { } CLastErrorFormat(const DWORD code) : m_code(code) { } ~CLastErrorFormat() { }public: const DWORD code() const { return m_code; } const std::string& str() { //... } const std::wstring& wstr() { //... }private: //...};
Corresponding interfaces in the log implementation class:
bool log_last_error(const LOG_LEVEL level, const char* file, const int line, CLastErrorFormat& e, const std::string& prefix);
Accept a CLastErrorFormat reference, and record the error code and its corresponding description when logging: xxx, error code: 999, error msg: yyy
The final log interface has two versions: one accepts one CLastErrorFormat parameter, and the other saves the need for self-constructing within the function.
Iii. str_encode
It is impossible for me to remember the wide string in the log file and the narrow string for a while, so I cannot read it. Considering the size of the log file, I finally decided, logs are encoded according to the narrow string SystemCurrentCodePage (GB2312 on Windows in Simplified Chinese version), so I need to convert the wide string to a narrow string.
Windows provides two APIs for encoding and conversion: MultiByteToWideChar and WideCharToMultiByte. These two APIs must be called twice for secure conversion. I encapsulated it a little and made it into two functions:
std::wstring multistr2widestr(const unsigned int from_code_page, const std::string& s);std::string widestr2multistr(const unsigned int to_code_page, const std::wstring& ws, const char *default_char = NULL);
Note: The code page of SystemCurrentCodePage is CP_ACP.
In this way, the width string is always slower, so I use narrow strings in my code.
Iv. any_lexical_cast
In the code, data type conversion is always inevitable, especially converting numbers into strings. To make it simple, I use boost's lexical_cast. Although we all say this product is inefficient, because the C ++ stream is used, I still use it by sticking to the "correct first and then optimization" principle.
However, there are two inconveniences for using this item:
1. If the conversion fails, an exception is thrown.
2. When converting bool to string, it is 0 or 1, not true or false.
To solve these two problems, I made another encapsulation:
1. Fill is the default value when the conversion fails. The caller must provide the default value.
2. Special conversion between bool and string
This is any_lexical_cast:
template<typename Target, typename Source>Target any_lexical_cast(const Source& src, const Target& fail_value){ Target value = fail_value; try { value = boost::lexical_cast<Target>(src); } catch (boost::bad_lexical_cast&) { value = fail_value; } return value;}template<>bool any_lexical_cast<bool, std::string>(const std::string& src, const bool& fail_value);template<>bool any_lexical_cast<bool, std::wstring>(const std::wstring& src, const bool& fail_value);template<>std::string any_lexical_cast<std::string, bool>(const bool& src, const std::string&);template<>std::wstring any_lexical_cast<std::wstring, bool>(const bool& src, const std::wstring&);
For specific implementation, see the source code.
5. CSelfPath
The log initialization interface usually needs to provide a path parameter to specify the log storage path. I added a default path for it: When an empty string is passed, the log file is placed in the log directory of the Application Path. If the log directory does not exist, it is created first.
The path where the application is located can be stored in the log module, but the path will not change once the application is started, therefore, CSelfPath is a singleton class.
CSelfPath only calls GetModuleFileNameA In the constructor to obtain the path and split it into directories, file names, and other parts.
6. CLoggerImpl and Logger
There are many things in the log implementation class that I don't want the caller to see, such as private members, and the interfaces of the log implementation class are not easy to use. Therefore, I introduced an indirect layer Logger between the log implementation class and the caller. Its main function is to hide the log implementation class and make the interface more user-friendly ". Besides this, I also gave it some other functions: control the log output level. Logger is not a class.
7. Disable 3rd party library warning
When I used boost's algorithm about string, I found that the compiler would generate a large warning. This is from the use of std: copy in the boost library, but I clearly know that the boost library code is correct. There are many and annoying warnings. Is there a safe way to eliminate them?
Certainly:
#pragma warning(push)#pragma warning(disable:4996)#include <boost/algorithm/string.hpp>#pragma warning(pop)
The above code is saved as a head file: boost_algorithm_string.h. When boost/algorithm/string. hpp is to be included in the future, it will be replaced by boost_algorithm_string.h.
Source code: https://git.oschina.net/mkdym/DaemonSvc.git (main) & https://github.com/mkdym/DaemonSvc.git (to improve the Force Grid ).
Sunday, January 1, November 1, 2015