├── .gitignore
├── AsyncIPC
├── AsyncIPC.vcxproj
├── AsyncIPC.vcxproj.filters
├── ReadMe.txt
├── async_ipc.cpp
├── async_ipc.h
├── dllmain.cpp
├── log
│ ├── easylogging++.cc
│ └── easylogging++.h
├── named_pipe_async.cpp
├── named_pipe_async.h
├── named_pipe_define.h
├── named_pipe_impl.cpp
├── named_pipe_impl.h
├── named_pipe_overlapped.cpp
├── named_pipe_overlapped.h
├── overlapped_pool.h
├── stdafx.cpp
├── stdafx.h
└── targetver.h
├── AsyncIPCClient.sln
├── AsyncIPCClient
├── AsyncIPCClient.h
├── AsyncIPCClient.ico
├── AsyncIPCClient.rc
├── AsyncIPCClient.vcxproj
├── AsyncIPCClient.vcxproj.filters
├── ReadMe.txt
├── Resource.h
├── main.cpp
├── small.ico
├── stdafx.cpp
├── stdafx.h
└── targetver.h
├── AsyncIPCServer
├── AsyncIPCServer.h
├── AsyncIPCServer.ico
├── AsyncIPCServer.rc
├── AsyncIPCServer.vcxproj
├── AsyncIPCServer.vcxproj.filters
├── ReadMe.txt
├── Resource.h
├── main.cpp
├── small.ico
├── stdafx.cpp
├── stdafx.h
└── targetver.h
├── LICENSE
├── README.md
└── img
├── connect.png
├── disconnect1.png
├── disconnect2.png
└── msg.png
/.gitignore:
--------------------------------------------------------------------------------
1 | # Prerequisites
2 | *.d
3 |
4 | # Compiled Object files
5 | *.slo
6 | *.lo
7 | *.o
8 | *.obj
9 |
10 | # Precompiled Headers
11 | *.gch
12 | *.pch
13 |
14 | # Compiled Dynamic libraries
15 | *.so
16 | *.dylib
17 | *.dll
18 |
19 | # Fortran module files
20 | *.mod
21 | *.smod
22 |
23 | # Compiled Static libraries
24 | *.lai
25 | *.la
26 | *.a
27 | *.lib
28 |
29 | # Executables
30 | *.exe
31 | *.out
32 | *.app
33 |
--------------------------------------------------------------------------------
/AsyncIPC/AsyncIPC.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 |
14 | {1A79C766-1CF7-4393-A31D-93E3B728FD1E}
15 | Win32Proj
16 | AsyncIPC
17 |
18 |
19 |
20 | DynamicLibrary
21 | true
22 | v120
23 | Unicode
24 |
25 |
26 | DynamicLibrary
27 | false
28 | v120
29 | true
30 | Unicode
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | true
44 |
45 |
46 | false
47 |
48 |
49 |
50 | Use
51 | Level3
52 | Disabled
53 | WIN32;_DEBUG;_WINDOWS;_USRDLL;ASYNCIPC_EXPORTS;%(PreprocessorDefinitions)
54 | true
55 |
56 |
57 | Windows
58 | true
59 |
60 |
61 |
62 |
63 | Level3
64 | Use
65 | MaxSpeed
66 | true
67 | true
68 | WIN32;NDEBUG;_WINDOWS;_USRDLL;ASYNCIPC_EXPORTS;%(PreprocessorDefinitions)
69 | true
70 |
71 |
72 | Windows
73 | true
74 | true
75 | true
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | false
97 |
98 |
99 | false
100 |
101 |
102 |
103 |
104 | NotUsing
105 | NotUsing
106 |
107 |
108 |
109 |
110 | Create
111 | Create
112 |
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/AsyncIPC/AsyncIPC.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 | {9b34da97-d645-4f6a-9d75-d0c789aee650}
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | 头文件
26 |
27 |
28 | 头文件
29 |
30 |
31 | log
32 |
33 |
34 | 头文件
35 |
36 |
37 | 头文件
38 |
39 |
40 | 头文件
41 |
42 |
43 | 头文件
44 |
45 |
46 | 头文件
47 |
48 |
49 | 头文件
50 |
51 |
52 |
53 |
54 | 源文件
55 |
56 |
57 | 源文件
58 |
59 |
60 | log
61 |
62 |
63 | 源文件
64 |
65 |
66 | 源文件
67 |
68 |
69 | 源文件
70 |
71 |
72 | 源文件
73 |
74 |
75 |
--------------------------------------------------------------------------------
/AsyncIPC/ReadMe.txt:
--------------------------------------------------------------------------------
1 | ========================================================================
2 | 动态链接库:AsyncIPC 项目概述
3 | ========================================================================
4 |
5 | 应用程序向导已为您创建了此 AsyncIPC DLL。
6 |
7 | 本文件概要介绍组成 AsyncIPC 应用程序的每个文件的内容。
8 |
9 |
10 | AsyncIPC.vcxproj
11 | 这是使用应用程序向导生成的 VC++ 项目的主项目文件,其中包含生成该文件的 Visual C++ 的版本信息,以及有关使用应用程序向导选择的平台、配置和项目功能的信息。
12 |
13 | AsyncIPC.vcxproj.filters
14 | 这是使用“应用程序向导”生成的 VC++ 项目筛选器文件。它包含有关项目文件与筛选器之间的关联信息。在 IDE 中,通过这种关联,在特定节点下以分组形式显示具有相似扩展名的文件。例如,“.cpp”文件与“源文件”筛选器关联。
15 |
16 | AsyncIPC.cpp
17 | 这是主 DLL 源文件。
18 |
19 | 此 DLL 在创建时不导出任何符号。因此,生成时不会产生 .lib 文件。如果希望此项目成为其他某个项目的项目依赖项,则需要添加代码以从 DLL 导出某些符号,以便产生一个导出库,或者,也可以在项目“属性页”对话框中的“链接器”文件夹中,将“常规”属性页上的“忽略输入库”属性设置为“是”。
20 |
21 | /////////////////////////////////////////////////////////////////////////////
22 | 其他标准文件:
23 |
24 | StdAfx.h, StdAfx.cpp
25 | 这些文件用于生成名为 AsyncIPC.pch 的预编译头 (PCH) 文件和名为 StdAfx.obj 的预编译类型文件。
26 |
27 | /////////////////////////////////////////////////////////////////////////////
28 | 其他注释:
29 |
30 | 应用程序向导使用“TODO:”注释来指示应添加或自定义的源代码部分。
31 |
32 | /////////////////////////////////////////////////////////////////////////////
33 |
--------------------------------------------------------------------------------
/AsyncIPC/async_ipc.cpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPC/async_ipc.cpp
--------------------------------------------------------------------------------
/AsyncIPC/async_ipc.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPC/async_ipc.h
--------------------------------------------------------------------------------
/AsyncIPC/dllmain.cpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPC/dllmain.cpp
--------------------------------------------------------------------------------
/AsyncIPC/log/easylogging++.cc:
--------------------------------------------------------------------------------
1 | //
2 | // Bismillah ar-Rahmaan ar-Raheem
3 | //
4 | // Easylogging++ v9.96.7
5 | // Cross-platform logging library for C++ applications
6 | //
7 | // Copyright (c) 2012-2018 Zuhd Web Services
8 | // Copyright (c) 2012-2018 @abumusamq
9 | //
10 | // This library is released under the MIT Licence.
11 | // https://github.com/zuhd-org/easyloggingpp/blob/master/LICENSE
12 | //
13 | // https://zuhd.org
14 | // http://muflihun.com
15 | //
16 |
17 | #include "easylogging++.h"
18 |
19 | #if defined(AUTO_INITIALIZE_EASYLOGGINGPP)
20 | INITIALIZE_EASYLOGGINGPP
21 | #endif
22 |
23 | namespace el {
24 |
25 | // el::base
26 | namespace base {
27 | // el::base::consts
28 | namespace consts {
29 |
30 | // Level log values - These are values that are replaced in place of %level format specifier
31 | // Extra spaces after format specifiers are only for readability purposes in log files
32 | static const base::type::char_t* kInfoLevelLogValue = ELPP_LITERAL("INFO");
33 | static const base::type::char_t* kDebugLevelLogValue = ELPP_LITERAL("DEBUG");
34 | static const base::type::char_t* kWarningLevelLogValue = ELPP_LITERAL("WARNING");
35 | static const base::type::char_t* kErrorLevelLogValue = ELPP_LITERAL("ERROR");
36 | static const base::type::char_t* kFatalLevelLogValue = ELPP_LITERAL("FATAL");
37 | static const base::type::char_t* kVerboseLevelLogValue =
38 | ELPP_LITERAL("VERBOSE"); // will become VERBOSE-x where x = verbose level
39 | static const base::type::char_t* kTraceLevelLogValue = ELPP_LITERAL("TRACE");
40 | static const base::type::char_t* kInfoLevelShortLogValue = ELPP_LITERAL("I");
41 | static const base::type::char_t* kDebugLevelShortLogValue = ELPP_LITERAL("D");
42 | static const base::type::char_t* kWarningLevelShortLogValue = ELPP_LITERAL("W");
43 | static const base::type::char_t* kErrorLevelShortLogValue = ELPP_LITERAL("E");
44 | static const base::type::char_t* kFatalLevelShortLogValue = ELPP_LITERAL("F");
45 | static const base::type::char_t* kVerboseLevelShortLogValue = ELPP_LITERAL("V");
46 | static const base::type::char_t* kTraceLevelShortLogValue = ELPP_LITERAL("T");
47 | // Format specifiers - These are used to define log format
48 | static const base::type::char_t* kAppNameFormatSpecifier = ELPP_LITERAL("%app");
49 | static const base::type::char_t* kLoggerIdFormatSpecifier = ELPP_LITERAL("%logger");
50 | static const base::type::char_t* kThreadIdFormatSpecifier = ELPP_LITERAL("%thread");
51 | static const base::type::char_t* kSeverityLevelFormatSpecifier = ELPP_LITERAL("%level");
52 | static const base::type::char_t* kSeverityLevelShortFormatSpecifier = ELPP_LITERAL("%levshort");
53 | static const base::type::char_t* kDateTimeFormatSpecifier = ELPP_LITERAL("%datetime");
54 | static const base::type::char_t* kLogFileFormatSpecifier = ELPP_LITERAL("%file");
55 | static const base::type::char_t* kLogFileBaseFormatSpecifier = ELPP_LITERAL("%fbase");
56 | static const base::type::char_t* kLogLineFormatSpecifier = ELPP_LITERAL("%line");
57 | static const base::type::char_t* kLogLocationFormatSpecifier = ELPP_LITERAL("%loc");
58 | static const base::type::char_t* kLogFunctionFormatSpecifier = ELPP_LITERAL("%func");
59 | static const base::type::char_t* kCurrentUserFormatSpecifier = ELPP_LITERAL("%user");
60 | static const base::type::char_t* kCurrentHostFormatSpecifier = ELPP_LITERAL("%host");
61 | static const base::type::char_t* kMessageFormatSpecifier = ELPP_LITERAL("%msg");
62 | static const base::type::char_t* kVerboseLevelFormatSpecifier = ELPP_LITERAL("%vlevel");
63 | static const char* kDateTimeFormatSpecifierForFilename = "%datetime";
64 | // Date/time
65 | static const char* kDays[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
66 | static const char* kDaysAbbrev[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
67 | static const char* kMonths[12] = { "January", "February", "March", "Apri", "May", "June", "July", "August",
68 | "September", "October", "November", "December"
69 | };
70 | static const char* kMonthsAbbrev[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
71 | static const char* kDefaultDateTimeFormat = "%Y-%M-%d %H:%m:%s,%g";
72 | static const char* kDefaultDateTimeFormatInFilename = "%Y-%M-%d_%H-%m";
73 | static const int kYearBase = 1900;
74 | static const char* kAm = "AM";
75 | static const char* kPm = "PM";
76 | // Miscellaneous constants
77 |
78 | static const char* kNullPointer = "nullptr";
79 | #if ELPP_VARIADIC_TEMPLATES_SUPPORTED
80 | #endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED
81 | static const base::type::VerboseLevel kMaxVerboseLevel = 9;
82 | static const char* kUnknownUser = "user";
83 | static const char* kUnknownHost = "unknown-host";
84 |
85 |
86 | //---------------- DEFAULT LOG FILE -----------------------
87 |
88 | #if defined(ELPP_NO_DEFAULT_LOG_FILE)
89 | # if ELPP_OS_UNIX
90 | static const char* kDefaultLogFile = "/dev/null";
91 | # elif ELPP_OS_WINDOWS
92 | static const char* kDefaultLogFile = "nul";
93 | # endif // ELPP_OS_UNIX
94 | #elif defined(ELPP_DEFAULT_LOG_FILE)
95 | static const char* kDefaultLogFile = ELPP_DEFAULT_LOG_FILE;
96 | #else
97 | static const char* kDefaultLogFile = "myeasylog.log";
98 | #endif // defined(ELPP_NO_DEFAULT_LOG_FILE)
99 |
100 |
101 | #if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
102 | static const char* kDefaultLogFileParam = "--default-log-file";
103 | #endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
104 | #if defined(ELPP_LOGGING_FLAGS_FROM_ARG)
105 | static const char* kLoggingFlagsParam = "--logging-flags";
106 | #endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG)
107 | static const char* kValidLoggerIdSymbols =
108 | "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._";
109 | static const char* kConfigurationComment = "##";
110 | static const char* kConfigurationLevel = "*";
111 | static const char* kConfigurationLoggerId = "--";
112 | }
113 | // el::base::utils
114 | namespace utils {
115 |
116 | /// @brief Aborts application due with user-defined status
117 | static void abort(int status, const std::string& reason) {
118 | // Both status and reason params are there for debugging with tools like gdb etc
119 | ELPP_UNUSED(status);
120 | ELPP_UNUSED(reason);
121 | #if defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG)
122 | // Ignore msvc critical error dialog - break instead (on debug mode)
123 | _asm int 3
124 | #else
125 | ::abort();
126 | #endif // defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG)
127 | }
128 |
129 | } // namespace utils
130 | } // namespace base
131 |
132 | // el
133 |
134 | // LevelHelper
135 |
136 | const char* LevelHelper::convertToString(Level level) {
137 | // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet.
138 | if (level == Level::Global) return "GLOBAL";
139 | if (level == Level::Debug) return "DEBUG";
140 | if (level == Level::Info) return "INFO";
141 | if (level == Level::Warning) return "WARNING";
142 | if (level == Level::Error) return "ERROR";
143 | if (level == Level::Fatal) return "FATAL";
144 | if (level == Level::Verbose) return "VERBOSE";
145 | if (level == Level::Trace) return "TRACE";
146 | return "UNKNOWN";
147 | }
148 |
149 | struct StringToLevelItem {
150 | const char* levelString;
151 | Level level;
152 | };
153 |
154 | static struct StringToLevelItem stringToLevelMap[] = {
155 | { "global", Level::Global },
156 | { "debug", Level::Debug },
157 | { "info", Level::Info },
158 | { "warning", Level::Warning },
159 | { "error", Level::Error },
160 | { "fatal", Level::Fatal },
161 | { "verbose", Level::Verbose },
162 | { "trace", Level::Trace }
163 | };
164 |
165 | Level LevelHelper::convertFromString(const char* levelStr) {
166 | for (auto& item : stringToLevelMap) {
167 | if (base::utils::Str::cStringCaseEq(levelStr, item.levelString)) {
168 | return item.level;
169 | }
170 | }
171 | return Level::Unknown;
172 | }
173 |
174 | void LevelHelper::forEachLevel(base::type::EnumType* startIndex, const std::function& fn) {
175 | base::type::EnumType lIndexMax = LevelHelper::kMaxValid;
176 | do {
177 | if (fn()) {
178 | break;
179 | }
180 | *startIndex = static_cast(*startIndex << 1);
181 | } while (*startIndex <= lIndexMax);
182 | }
183 |
184 | // ConfigurationTypeHelper
185 |
186 | const char* ConfigurationTypeHelper::convertToString(ConfigurationType configurationType) {
187 | // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet.
188 | if (configurationType == ConfigurationType::Enabled) return "ENABLED";
189 | if (configurationType == ConfigurationType::Filename) return "FILENAME";
190 | if (configurationType == ConfigurationType::Format) return "FORMAT";
191 | if (configurationType == ConfigurationType::ToFile) return "TO_FILE";
192 | if (configurationType == ConfigurationType::ToStandardOutput) return "TO_STANDARD_OUTPUT";
193 | if (configurationType == ConfigurationType::SubsecondPrecision) return "SUBSECOND_PRECISION";
194 | if (configurationType == ConfigurationType::PerformanceTracking) return "PERFORMANCE_TRACKING";
195 | if (configurationType == ConfigurationType::MaxLogFileSize) return "MAX_LOG_FILE_SIZE";
196 | if (configurationType == ConfigurationType::LogFlushThreshold) return "LOG_FLUSH_THRESHOLD";
197 | return "UNKNOWN";
198 | }
199 |
200 | struct ConfigurationStringToTypeItem {
201 | const char* configString;
202 | ConfigurationType configType;
203 | };
204 |
205 | static struct ConfigurationStringToTypeItem configStringToTypeMap[] = {
206 | { "enabled", ConfigurationType::Enabled },
207 | { "to_file", ConfigurationType::ToFile },
208 | { "to_standard_output", ConfigurationType::ToStandardOutput },
209 | { "format", ConfigurationType::Format },
210 | { "filename", ConfigurationType::Filename },
211 | { "subsecond_precision", ConfigurationType::SubsecondPrecision },
212 | { "milliseconds_width", ConfigurationType::MillisecondsWidth },
213 | { "performance_tracking", ConfigurationType::PerformanceTracking },
214 | { "max_log_file_size", ConfigurationType::MaxLogFileSize },
215 | { "log_flush_threshold", ConfigurationType::LogFlushThreshold },
216 | };
217 |
218 | ConfigurationType ConfigurationTypeHelper::convertFromString(const char* configStr) {
219 | for (auto& item : configStringToTypeMap) {
220 | if (base::utils::Str::cStringCaseEq(configStr, item.configString)) {
221 | return item.configType;
222 | }
223 | }
224 | return ConfigurationType::Unknown;
225 | }
226 |
227 | void ConfigurationTypeHelper::forEachConfigType(base::type::EnumType* startIndex, const std::function& fn) {
228 | base::type::EnumType cIndexMax = ConfigurationTypeHelper::kMaxValid;
229 | do {
230 | if (fn()) {
231 | break;
232 | }
233 | *startIndex = static_cast(*startIndex << 1);
234 | } while (*startIndex <= cIndexMax);
235 | }
236 |
237 | // Configuration
238 |
239 | Configuration::Configuration(const Configuration& c) :
240 | m_level(c.m_level),
241 | m_configurationType(c.m_configurationType),
242 | m_value(c.m_value) {
243 | }
244 |
245 | Configuration& Configuration::operator=(const Configuration& c) {
246 | if (&c != this) {
247 | m_level = c.m_level;
248 | m_configurationType = c.m_configurationType;
249 | m_value = c.m_value;
250 | }
251 | return *this;
252 | }
253 |
254 | /// @brief Full constructor used to sets value of configuration
255 | Configuration::Configuration(Level level, ConfigurationType configurationType, const std::string& value) :
256 | m_level(level),
257 | m_configurationType(configurationType),
258 | m_value(value) {
259 | }
260 |
261 | void Configuration::log(el::base::type::ostream_t& os) const {
262 | os << LevelHelper::convertToString(m_level)
263 | << ELPP_LITERAL(" ") << ConfigurationTypeHelper::convertToString(m_configurationType)
264 | << ELPP_LITERAL(" = ") << m_value.c_str();
265 | }
266 |
267 | /// @brief Used to find configuration from configuration (pointers) repository. Avoid using it.
268 | Configuration::Predicate::Predicate(Level level, ConfigurationType configurationType) :
269 | m_level(level),
270 | m_configurationType(configurationType) {
271 | }
272 |
273 | bool Configuration::Predicate::operator()(const Configuration* conf) const {
274 | return ((conf != nullptr) && (conf->level() == m_level) && (conf->configurationType() == m_configurationType));
275 | }
276 |
277 | // Configurations
278 |
279 | Configurations::Configurations(void) :
280 | m_configurationFile(std::string()),
281 | m_isFromFile(false) {
282 | }
283 |
284 | Configurations::Configurations(const std::string& configurationFile, bool useDefaultsForRemaining,
285 | Configurations* base) :
286 | m_configurationFile(configurationFile),
287 | m_isFromFile(false) {
288 | parseFromFile(configurationFile, base);
289 | if (useDefaultsForRemaining) {
290 | setRemainingToDefault();
291 | }
292 | }
293 |
294 | bool Configurations::parseFromFile(const std::string& configurationFile, Configurations* base) {
295 | // We initial assertion with true because if we have assertion diabled, we want to pass this
296 | // check and if assertion is enabled we will have values re-assigned any way.
297 | bool assertionPassed = true;
298 | ELPP_ASSERT((assertionPassed = base::utils::File::pathExists(configurationFile.c_str(), true)) == true,
299 | "Configuration file [" << configurationFile << "] does not exist!");
300 | if (!assertionPassed) {
301 | return false;
302 | }
303 | bool success = Parser::parseFromFile(configurationFile, this, base);
304 | m_isFromFile = success;
305 | return success;
306 | }
307 |
308 | bool Configurations::parseFromText(const std::string& configurationsString, Configurations* base) {
309 | bool success = Parser::parseFromText(configurationsString, this, base);
310 | if (success) {
311 | m_isFromFile = false;
312 | }
313 | return success;
314 | }
315 |
316 | void Configurations::setFromBase(Configurations* base) {
317 | if (base == nullptr || base == this) {
318 | return;
319 | }
320 | base::threading::ScopedLock scopedLock(base->lock());
321 | for (Configuration*& conf : base->list()) {
322 | set(conf);
323 | }
324 | }
325 |
326 | bool Configurations::hasConfiguration(ConfigurationType configurationType) {
327 | base::type::EnumType lIndex = LevelHelper::kMinValid;
328 | bool result = false;
329 | LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
330 | if (hasConfiguration(LevelHelper::castFromInt(lIndex), configurationType)) {
331 | result = true;
332 | }
333 | return result;
334 | });
335 | return result;
336 | }
337 |
338 | bool Configurations::hasConfiguration(Level level, ConfigurationType configurationType) {
339 | base::threading::ScopedLock scopedLock(lock());
340 | #if ELPP_COMPILER_INTEL
341 | // We cant specify template types here, Intel C++ throws compilation error
342 | // "error: type name is not allowed"
343 | return RegistryWithPred::get(level, configurationType) != nullptr;
344 | #else
345 | return RegistryWithPred::get(level, configurationType) != nullptr;
346 | #endif // ELPP_COMPILER_INTEL
347 | }
348 |
349 | void Configurations::set(Level level, ConfigurationType configurationType, const std::string& value) {
350 | base::threading::ScopedLock scopedLock(lock());
351 | unsafeSet(level, configurationType, value); // This is not unsafe anymore as we have locked mutex
352 | if (level == Level::Global) {
353 | unsafeSetGlobally(configurationType, value, false); // Again this is not unsafe either
354 | }
355 | }
356 |
357 | void Configurations::set(Configuration* conf) {
358 | if (conf == nullptr) {
359 | return;
360 | }
361 | set(conf->level(), conf->configurationType(), conf->value());
362 | }
363 |
364 | void Configurations::setToDefault(void) {
365 | setGlobally(ConfigurationType::Enabled, std::string("true"), true);
366 | setGlobally(ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile), true);
367 | #if defined(ELPP_NO_LOG_TO_FILE)
368 | setGlobally(ConfigurationType::ToFile, std::string("false"), true);
369 | #else
370 | setGlobally(ConfigurationType::ToFile, std::string("true"), true);
371 | #endif // defined(ELPP_NO_LOG_TO_FILE)
372 | setGlobally(ConfigurationType::ToStandardOutput, std::string("true"), true);
373 | setGlobally(ConfigurationType::SubsecondPrecision, std::string("3"), true);
374 | setGlobally(ConfigurationType::PerformanceTracking, std::string("true"), true);
375 | setGlobally(ConfigurationType::MaxLogFileSize, std::string("0"), true);
376 | setGlobally(ConfigurationType::LogFlushThreshold, std::string("0"), true);
377 |
378 | setGlobally(ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"), true);
379 | set(Level::Debug, ConfigurationType::Format,
380 | std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg"));
381 | // INFO and WARNING are set to default by Level::Global
382 | set(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
383 | set(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
384 | set(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg"));
385 | set(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg"));
386 | }
387 |
388 | void Configurations::setRemainingToDefault(void) {
389 | base::threading::ScopedLock scopedLock(lock());
390 | #if defined(ELPP_NO_LOG_TO_FILE)
391 | unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("false"));
392 | #else
393 | unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("true"));
394 | #endif // defined(ELPP_NO_LOG_TO_FILE)
395 | unsafeSetIfNotExist(Level::Global, ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile));
396 | unsafeSetIfNotExist(Level::Global, ConfigurationType::ToStandardOutput, std::string("true"));
397 | unsafeSetIfNotExist(Level::Global, ConfigurationType::SubsecondPrecision, std::string("3"));
398 | unsafeSetIfNotExist(Level::Global, ConfigurationType::PerformanceTracking, std::string("true"));
399 | unsafeSetIfNotExist(Level::Global, ConfigurationType::MaxLogFileSize, std::string("0"));
400 | unsafeSetIfNotExist(Level::Global, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
401 | unsafeSetIfNotExist(Level::Debug, ConfigurationType::Format,
402 | std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg"));
403 | // INFO and WARNING are set to default by Level::Global
404 | unsafeSetIfNotExist(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
405 | unsafeSetIfNotExist(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
406 | unsafeSetIfNotExist(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg"));
407 | unsafeSetIfNotExist(Level::Trace, ConfigurationType::Format,
408 | std::string("%datetime %level [%logger] [%func] [%loc] %msg"));
409 | }
410 |
411 | bool Configurations::Parser::parseFromFile(const std::string& configurationFile, Configurations* sender,
412 | Configurations* base) {
413 | sender->setFromBase(base);
414 | std::ifstream fileStream_(configurationFile.c_str(), std::ifstream::in);
415 | ELPP_ASSERT(fileStream_.is_open(), "Unable to open configuration file [" << configurationFile << "] for parsing.");
416 | bool parsedSuccessfully = false;
417 | std::string line = std::string();
418 | Level currLevel = Level::Unknown;
419 | std::string currConfigStr = std::string();
420 | std::string currLevelStr = std::string();
421 | while (fileStream_.good()) {
422 | std::getline(fileStream_, line);
423 | parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender);
424 | ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line);
425 | }
426 | return parsedSuccessfully;
427 | }
428 |
429 | bool Configurations::Parser::parseFromText(const std::string& configurationsString, Configurations* sender,
430 | Configurations* base) {
431 | sender->setFromBase(base);
432 | bool parsedSuccessfully = false;
433 | std::stringstream ss(configurationsString);
434 | std::string line = std::string();
435 | Level currLevel = Level::Unknown;
436 | std::string currConfigStr = std::string();
437 | std::string currLevelStr = std::string();
438 | while (std::getline(ss, line)) {
439 | parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender);
440 | ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line);
441 | }
442 | return parsedSuccessfully;
443 | }
444 |
445 | void Configurations::Parser::ignoreComments(std::string* line) {
446 | std::size_t foundAt = 0;
447 | std::size_t quotesStart = line->find("\"");
448 | std::size_t quotesEnd = std::string::npos;
449 | if (quotesStart != std::string::npos) {
450 | quotesEnd = line->find("\"", quotesStart + 1);
451 | while (quotesEnd != std::string::npos && line->at(quotesEnd - 1) == '\\') {
452 | // Do not erase slash yet - we will erase it in parseLine(..) while loop
453 | quotesEnd = line->find("\"", quotesEnd + 2);
454 | }
455 | }
456 | if ((foundAt = line->find(base::consts::kConfigurationComment)) != std::string::npos) {
457 | if (foundAt < quotesEnd) {
458 | foundAt = line->find(base::consts::kConfigurationComment, quotesEnd + 1);
459 | }
460 | *line = line->substr(0, foundAt);
461 | }
462 | }
463 |
464 | bool Configurations::Parser::isLevel(const std::string& line) {
465 | return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLevel));
466 | }
467 |
468 | bool Configurations::Parser::isComment(const std::string& line) {
469 | return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationComment));
470 | }
471 |
472 | bool Configurations::Parser::isConfig(const std::string& line) {
473 | std::size_t assignment = line.find('=');
474 | return line != "" &&
475 | ((line[0] >= 'A' && line[0] <= 'Z') || (line[0] >= 'a' && line[0] <= 'z')) &&
476 | (assignment != std::string::npos) &&
477 | (line.size() > assignment);
478 | }
479 |
480 | bool Configurations::Parser::parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr,
481 | Level* currLevel,
482 | Configurations* conf) {
483 | ConfigurationType currConfig = ConfigurationType::Unknown;
484 | std::string currValue = std::string();
485 | *line = base::utils::Str::trim(*line);
486 | if (isComment(*line)) return true;
487 | ignoreComments(line);
488 | *line = base::utils::Str::trim(*line);
489 | if (line->empty()) {
490 | // Comment ignored
491 | return true;
492 | }
493 | if (isLevel(*line)) {
494 | if (line->size() <= 2) {
495 | return true;
496 | }
497 | *currLevelStr = line->substr(1, line->size() - 2);
498 | *currLevelStr = base::utils::Str::toUpper(*currLevelStr);
499 | *currLevelStr = base::utils::Str::trim(*currLevelStr);
500 | *currLevel = LevelHelper::convertFromString(currLevelStr->c_str());
501 | return true;
502 | }
503 | if (isConfig(*line)) {
504 | std::size_t assignment = line->find('=');
505 | *currConfigStr = line->substr(0, assignment);
506 | *currConfigStr = base::utils::Str::toUpper(*currConfigStr);
507 | *currConfigStr = base::utils::Str::trim(*currConfigStr);
508 | currConfig = ConfigurationTypeHelper::convertFromString(currConfigStr->c_str());
509 | currValue = line->substr(assignment + 1);
510 | currValue = base::utils::Str::trim(currValue);
511 | std::size_t quotesStart = currValue.find("\"", 0);
512 | std::size_t quotesEnd = std::string::npos;
513 | if (quotesStart != std::string::npos) {
514 | quotesEnd = currValue.find("\"", quotesStart + 1);
515 | while (quotesEnd != std::string::npos && currValue.at(quotesEnd - 1) == '\\') {
516 | currValue = currValue.erase(quotesEnd - 1, 1);
517 | quotesEnd = currValue.find("\"", quotesEnd + 2);
518 | }
519 | }
520 | if (quotesStart != std::string::npos && quotesEnd != std::string::npos) {
521 | // Quote provided - check and strip if valid
522 | ELPP_ASSERT((quotesStart < quotesEnd), "Configuration error - No ending quote found in ["
523 | << currConfigStr << "]");
524 | ELPP_ASSERT((quotesStart + 1 != quotesEnd), "Empty configuration value for [" << currConfigStr << "]");
525 | if ((quotesStart != quotesEnd) && (quotesStart + 1 != quotesEnd)) {
526 | // Explicit check in case if assertion is disabled
527 | currValue = currValue.substr(quotesStart + 1, quotesEnd - 1);
528 | }
529 | }
530 | }
531 | ELPP_ASSERT(*currLevel != Level::Unknown, "Unrecognized severity level [" << *currLevelStr << "]");
532 | ELPP_ASSERT(currConfig != ConfigurationType::Unknown, "Unrecognized configuration [" << *currConfigStr << "]");
533 | if (*currLevel == Level::Unknown || currConfig == ConfigurationType::Unknown) {
534 | return false; // unrecognizable level or config
535 | }
536 | conf->set(*currLevel, currConfig, currValue);
537 | return true;
538 | }
539 |
540 | void Configurations::unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value) {
541 | Configuration* conf = RegistryWithPred::get(level, configurationType);
542 | if (conf == nullptr) {
543 | unsafeSet(level, configurationType, value);
544 | }
545 | }
546 |
547 | void Configurations::unsafeSet(Level level, ConfigurationType configurationType, const std::string& value) {
548 | Configuration* conf = RegistryWithPred::get(level, configurationType);
549 | if (conf == nullptr) {
550 | registerNew(new Configuration(level, configurationType, value));
551 | } else {
552 | conf->setValue(value);
553 | }
554 | if (level == Level::Global) {
555 | unsafeSetGlobally(configurationType, value, false);
556 | }
557 | }
558 |
559 | void Configurations::setGlobally(ConfigurationType configurationType, const std::string& value,
560 | bool includeGlobalLevel) {
561 | if (includeGlobalLevel) {
562 | set(Level::Global, configurationType, value);
563 | }
564 | base::type::EnumType lIndex = LevelHelper::kMinValid;
565 | LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
566 | set(LevelHelper::castFromInt(lIndex), configurationType, value);
567 | return false; // Do not break lambda function yet as we need to set all levels regardless
568 | });
569 | }
570 |
571 | void Configurations::unsafeSetGlobally(ConfigurationType configurationType, const std::string& value,
572 | bool includeGlobalLevel) {
573 | if (includeGlobalLevel) {
574 | unsafeSet(Level::Global, configurationType, value);
575 | }
576 | base::type::EnumType lIndex = LevelHelper::kMinValid;
577 | LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
578 | unsafeSet(LevelHelper::castFromInt(lIndex), configurationType, value);
579 | return false; // Do not break lambda function yet as we need to set all levels regardless
580 | });
581 | }
582 |
583 | // LogBuilder
584 |
585 | void LogBuilder::convertToColoredOutput(base::type::string_t* logLine, Level level) {
586 | if (!m_termSupportsColor) return;
587 | const base::type::char_t* resetColor = ELPP_LITERAL("\x1b[0m");
588 | if (level == Level::Error || level == Level::Fatal)
589 | *logLine = ELPP_LITERAL("\x1b[31m") + *logLine + resetColor;
590 | else if (level == Level::Warning)
591 | *logLine = ELPP_LITERAL("\x1b[33m") + *logLine + resetColor;
592 | else if (level == Level::Debug)
593 | *logLine = ELPP_LITERAL("\x1b[32m") + *logLine + resetColor;
594 | else if (level == Level::Info)
595 | *logLine = ELPP_LITERAL("\x1b[36m") + *logLine + resetColor;
596 | else if (level == Level::Trace)
597 | *logLine = ELPP_LITERAL("\x1b[35m") + *logLine + resetColor;
598 | }
599 |
600 | // Logger
601 |
602 | Logger::Logger(const std::string& id, base::LogStreamsReferenceMap* logStreamsReference) :
603 | m_id(id),
604 | m_typedConfigurations(nullptr),
605 | m_parentApplicationName(std::string()),
606 | m_isConfigured(false),
607 | m_logStreamsReference(logStreamsReference) {
608 | initUnflushedCount();
609 | }
610 |
611 | Logger::Logger(const std::string& id, const Configurations& configurations,
612 | base::LogStreamsReferenceMap* logStreamsReference) :
613 | m_id(id),
614 | m_typedConfigurations(nullptr),
615 | m_parentApplicationName(std::string()),
616 | m_isConfigured(false),
617 | m_logStreamsReference(logStreamsReference) {
618 | initUnflushedCount();
619 | configure(configurations);
620 | }
621 |
622 | Logger::Logger(const Logger& logger) {
623 | base::utils::safeDelete(m_typedConfigurations);
624 | m_id = logger.m_id;
625 | m_typedConfigurations = logger.m_typedConfigurations;
626 | m_parentApplicationName = logger.m_parentApplicationName;
627 | m_isConfigured = logger.m_isConfigured;
628 | m_configurations = logger.m_configurations;
629 | m_unflushedCount = logger.m_unflushedCount;
630 | m_logStreamsReference = logger.m_logStreamsReference;
631 | }
632 |
633 | Logger& Logger::operator=(const Logger& logger) {
634 | if (&logger != this) {
635 | base::utils::safeDelete(m_typedConfigurations);
636 | m_id = logger.m_id;
637 | m_typedConfigurations = logger.m_typedConfigurations;
638 | m_parentApplicationName = logger.m_parentApplicationName;
639 | m_isConfigured = logger.m_isConfigured;
640 | m_configurations = logger.m_configurations;
641 | m_unflushedCount = logger.m_unflushedCount;
642 | m_logStreamsReference = logger.m_logStreamsReference;
643 | }
644 | return *this;
645 | }
646 |
647 | void Logger::configure(const Configurations& configurations) {
648 | m_isConfigured = false; // we set it to false in case if we fail
649 | initUnflushedCount();
650 | if (m_typedConfigurations != nullptr) {
651 | Configurations* c = const_cast(m_typedConfigurations->configurations());
652 | if (c->hasConfiguration(Level::Global, ConfigurationType::Filename)) {
653 | flush();
654 | }
655 | }
656 | base::threading::ScopedLock scopedLock(lock());
657 | if (m_configurations != configurations) {
658 | m_configurations.setFromBase(const_cast(&configurations));
659 | }
660 | base::utils::safeDelete(m_typedConfigurations);
661 | m_typedConfigurations = new base::TypedConfigurations(&m_configurations, m_logStreamsReference);
662 | resolveLoggerFormatSpec();
663 | m_isConfigured = true;
664 | }
665 |
666 | void Logger::reconfigure(void) {
667 | ELPP_INTERNAL_INFO(1, "Reconfiguring logger [" << m_id << "]");
668 | configure(m_configurations);
669 | }
670 |
671 | bool Logger::isValidId(const std::string& id) {
672 | for (std::string::const_iterator it = id.begin(); it != id.end(); ++it) {
673 | if (!base::utils::Str::contains(base::consts::kValidLoggerIdSymbols, *it)) {
674 | return false;
675 | }
676 | }
677 | return true;
678 | }
679 |
680 | void Logger::flush(void) {
681 | ELPP_INTERNAL_INFO(3, "Flushing logger [" << m_id << "] all levels");
682 | base::threading::ScopedLock scopedLock(lock());
683 | base::type::EnumType lIndex = LevelHelper::kMinValid;
684 | LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
685 | flush(LevelHelper::castFromInt(lIndex), nullptr);
686 | return false;
687 | });
688 | }
689 |
690 | void Logger::flush(Level level, base::type::fstream_t* fs) {
691 | if (fs == nullptr && m_typedConfigurations->toFile(level)) {
692 | fs = m_typedConfigurations->fileStream(level);
693 | }
694 | if (fs != nullptr) {
695 | fs->flush();
696 | std::unordered_map::iterator iter = m_unflushedCount.find(level);
697 | if (iter != m_unflushedCount.end()) {
698 | iter->second = 0;
699 | }
700 | Helpers::validateFileRolling(this, level);
701 | }
702 | }
703 |
704 | void Logger::initUnflushedCount(void) {
705 | m_unflushedCount.clear();
706 | base::type::EnumType lIndex = LevelHelper::kMinValid;
707 | LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
708 | m_unflushedCount.insert(std::make_pair(LevelHelper::castFromInt(lIndex), 0));
709 | return false;
710 | });
711 | }
712 |
713 | void Logger::resolveLoggerFormatSpec(void) const {
714 | base::type::EnumType lIndex = LevelHelper::kMinValid;
715 | LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
716 | base::LogFormat* logFormat =
717 | const_cast(&m_typedConfigurations->logFormat(LevelHelper::castFromInt(lIndex)));
718 | base::utils::Str::replaceFirstWithEscape(logFormat->m_format, base::consts::kLoggerIdFormatSpecifier, m_id);
719 | return false;
720 | });
721 | }
722 |
723 | // el::base
724 | namespace base {
725 |
726 | // el::base::utils
727 | namespace utils {
728 |
729 | // File
730 |
731 | base::type::fstream_t* File::newFileStream(const std::string& filename) {
732 | base::type::fstream_t *fs = new base::type::fstream_t(filename.c_str(),
733 | base::type::fstream_t::out
734 | #if !defined(ELPP_FRESH_LOG_FILE)
735 | | base::type::fstream_t::app
736 | #endif
737 | );
738 | #if defined(ELPP_UNICODE)
739 | std::locale elppUnicodeLocale("");
740 | # if ELPP_OS_WINDOWS
741 | std::locale elppUnicodeLocaleWindows(elppUnicodeLocale, new std::codecvt_utf8_utf16);
742 | elppUnicodeLocale = elppUnicodeLocaleWindows;
743 | # endif // ELPP_OS_WINDOWS
744 | fs->imbue(elppUnicodeLocale);
745 | #endif // defined(ELPP_UNICODE)
746 | if (fs->is_open()) {
747 | fs->flush();
748 | } else {
749 | base::utils::safeDelete(fs);
750 | ELPP_INTERNAL_ERROR("Bad file [" << filename << "]", true);
751 | }
752 | return fs;
753 | }
754 |
755 | std::size_t File::getSizeOfFile(base::type::fstream_t* fs) {
756 | if (fs == nullptr) {
757 | return 0;
758 | }
759 | // Since the file stream is appended to or truncated, the current
760 | // offset is the file size.
761 | std::size_t size = static_cast(fs->tellg());
762 | return size;
763 | }
764 |
765 | bool File::pathExists(const char* path, bool considerFile) {
766 | if (path == nullptr) {
767 | return false;
768 | }
769 | #if ELPP_OS_UNIX
770 | ELPP_UNUSED(considerFile);
771 | struct stat st;
772 | return (stat(path, &st) == 0);
773 | #elif ELPP_OS_WINDOWS
774 | DWORD fileType = GetFileAttributesA(path);
775 | if (fileType == INVALID_FILE_ATTRIBUTES) {
776 | return false;
777 | }
778 | return considerFile ? true : ((fileType & FILE_ATTRIBUTE_DIRECTORY) == 0 ? false : true);
779 | #endif // ELPP_OS_UNIX
780 | }
781 |
782 | bool File::createPath(const std::string& path) {
783 | if (path.empty()) {
784 | return false;
785 | }
786 | if (base::utils::File::pathExists(path.c_str())) {
787 | return true;
788 | }
789 | int status = -1;
790 |
791 | char* currPath = const_cast(path.c_str());
792 | std::string builtPath = std::string();
793 | #if ELPP_OS_UNIX
794 | if (path[0] == '/') {
795 | builtPath = "/";
796 | }
797 | currPath = STRTOK(currPath, base::consts::kFilePathSeperator, 0);
798 | #elif ELPP_OS_WINDOWS
799 | // Use secure functions API
800 | char* nextTok_ = nullptr;
801 | currPath = STRTOK(currPath, base::consts::kFilePathSeperator, &nextTok_);
802 | ELPP_UNUSED(nextTok_);
803 | #endif // ELPP_OS_UNIX
804 | while (currPath != nullptr) {
805 | builtPath.append(currPath);
806 | builtPath.append(base::consts::kFilePathSeperator);
807 | #if ELPP_OS_UNIX
808 | status = mkdir(builtPath.c_str(), ELPP_LOG_PERMS);
809 | currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, 0);
810 | #elif ELPP_OS_WINDOWS
811 | status = _mkdir(builtPath.c_str());
812 | currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, &nextTok_);
813 | #endif // ELPP_OS_UNIX
814 | }
815 | if (status == -1) {
816 | ELPP_INTERNAL_ERROR("Error while creating path [" << path << "]", true);
817 | return false;
818 | }
819 | return true;
820 | }
821 |
822 | std::string File::extractPathFromFilename(const std::string& fullPath, const char* separator) {
823 | if ((fullPath == "") || (fullPath.find(separator) == std::string::npos)) {
824 | return fullPath;
825 | }
826 | std::size_t lastSlashAt = fullPath.find_last_of(separator);
827 | if (lastSlashAt == 0) {
828 | return std::string(separator);
829 | }
830 | return fullPath.substr(0, lastSlashAt + 1);
831 | }
832 |
833 | void File::buildStrippedFilename(const char* filename, char buff[], std::size_t limit) {
834 | std::size_t sizeOfFilename = strlen(filename);
835 | if (sizeOfFilename >= limit) {
836 | filename += (sizeOfFilename - limit);
837 | if (filename[0] != '.' && filename[1] != '.') { // prepend if not already
838 | filename += 3; // 3 = '..'
839 | STRCAT(buff, "..", limit);
840 | }
841 | }
842 | STRCAT(buff, filename, limit);
843 | }
844 |
845 | void File::buildBaseFilename(const std::string& fullPath, char buff[], std::size_t limit, const char* separator) {
846 | const char *filename = fullPath.c_str();
847 | std::size_t lastSlashAt = fullPath.find_last_of(separator);
848 | filename += lastSlashAt ? lastSlashAt+1 : 0;
849 | std::size_t sizeOfFilename = strlen(filename);
850 | if (sizeOfFilename >= limit) {
851 | filename += (sizeOfFilename - limit);
852 | if (filename[0] != '.' && filename[1] != '.') { // prepend if not already
853 | filename += 3; // 3 = '..'
854 | STRCAT(buff, "..", limit);
855 | }
856 | }
857 | STRCAT(buff, filename, limit);
858 | }
859 |
860 | // Str
861 |
862 | bool Str::wildCardMatch(const char* str, const char* pattern) {
863 | while (*pattern) {
864 | switch (*pattern) {
865 | case '?':
866 | if (!*str)
867 | return false;
868 | ++str;
869 | ++pattern;
870 | break;
871 | case '*':
872 | if (wildCardMatch(str, pattern + 1))
873 | return true;
874 | if (*str && wildCardMatch(str + 1, pattern))
875 | return true;
876 | return false;
877 | default:
878 | if (*str++ != *pattern++)
879 | return false;
880 | break;
881 | }
882 | }
883 | return !*str && !*pattern;
884 | }
885 |
886 | std::string& Str::ltrim(std::string& str) {
887 | str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](char c) {
888 | return !std::isspace(c);
889 | } ));
890 | return str;
891 | }
892 |
893 | std::string& Str::rtrim(std::string& str) {
894 | str.erase(std::find_if(str.rbegin(), str.rend(), [](char c) {
895 | return !std::isspace(c);
896 | }).base(), str.end());
897 | return str;
898 | }
899 |
900 | std::string& Str::trim(std::string& str) {
901 | return ltrim(rtrim(str));
902 | }
903 |
904 | bool Str::startsWith(const std::string& str, const std::string& start) {
905 | return (str.length() >= start.length()) && (str.compare(0, start.length(), start) == 0);
906 | }
907 |
908 | bool Str::endsWith(const std::string& str, const std::string& end) {
909 | return (str.length() >= end.length()) && (str.compare(str.length() - end.length(), end.length(), end) == 0);
910 | }
911 |
912 | std::string& Str::replaceAll(std::string& str, char replaceWhat, char replaceWith) {
913 | std::replace(str.begin(), str.end(), replaceWhat, replaceWith);
914 | return str;
915 | }
916 |
917 | std::string& Str::replaceAll(std::string& str, const std::string& replaceWhat,
918 | const std::string& replaceWith) {
919 | if (replaceWhat == replaceWith)
920 | return str;
921 | std::size_t foundAt = std::string::npos;
922 | while ((foundAt = str.find(replaceWhat, foundAt + 1)) != std::string::npos) {
923 | str.replace(foundAt, replaceWhat.length(), replaceWith);
924 | }
925 | return str;
926 | }
927 |
928 | void Str::replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat,
929 | const base::type::string_t& replaceWith) {
930 | std::size_t foundAt = base::type::string_t::npos;
931 | while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) {
932 | if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) {
933 | str.erase(foundAt - 1, 1);
934 | ++foundAt;
935 | } else {
936 | str.replace(foundAt, replaceWhat.length(), replaceWith);
937 | return;
938 | }
939 | }
940 | }
941 | #if defined(ELPP_UNICODE)
942 | void Str::replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat,
943 | const std::string& replaceWith) {
944 | replaceFirstWithEscape(str, replaceWhat, base::type::string_t(replaceWith.begin(), replaceWith.end()));
945 | }
946 | #endif // defined(ELPP_UNICODE)
947 |
948 | std::string& Str::toUpper(std::string& str) {
949 | std::transform(str.begin(), str.end(), str.begin(),
950 | [](char c) {
951 | return static_cast(::toupper(c));
952 | });
953 | return str;
954 | }
955 |
956 | bool Str::cStringEq(const char* s1, const char* s2) {
957 | if (s1 == nullptr && s2 == nullptr) return true;
958 | if (s1 == nullptr || s2 == nullptr) return false;
959 | return strcmp(s1, s2) == 0;
960 | }
961 |
962 | bool Str::cStringCaseEq(const char* s1, const char* s2) {
963 | if (s1 == nullptr && s2 == nullptr) return true;
964 | if (s1 == nullptr || s2 == nullptr) return false;
965 |
966 | // With thanks to cygwin for this code
967 | int d = 0;
968 |
969 | while (true) {
970 | const int c1 = toupper(*s1++);
971 | const int c2 = toupper(*s2++);
972 |
973 | if (((d = c1 - c2) != 0) || (c2 == '\0')) {
974 | break;
975 | }
976 | }
977 |
978 | return d == 0;
979 | }
980 |
981 | bool Str::contains(const char* str, char c) {
982 | for (; *str; ++str) {
983 | if (*str == c)
984 | return true;
985 | }
986 | return false;
987 | }
988 |
989 | char* Str::convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded) {
990 | char localBuff[10] = "";
991 | char* p = localBuff + sizeof(localBuff) - 2;
992 | if (n > 0) {
993 | for (; n > 0 && p > localBuff && len > 0; n /= 10, --len)
994 | *--p = static_cast(n % 10 + '0');
995 | } else {
996 | *--p = '0';
997 | --len;
998 | }
999 | if (zeroPadded)
1000 | while (p > localBuff && len-- > 0) *--p = static_cast('0');
1001 | return addToBuff(p, buf, bufLim);
1002 | }
1003 |
1004 | char* Str::addToBuff(const char* str, char* buf, const char* bufLim) {
1005 | while ((buf < bufLim) && ((*buf = *str++) != '\0'))
1006 | ++buf;
1007 | return buf;
1008 | }
1009 |
1010 | char* Str::clearBuff(char buff[], std::size_t lim) {
1011 | STRCPY(buff, "", lim);
1012 | ELPP_UNUSED(lim); // For *nix we dont have anything using lim in above STRCPY macro
1013 | return buff;
1014 | }
1015 |
1016 | /// @brief Converst wchar* to char*
1017 | /// NOTE: Need to free return value after use!
1018 | char* Str::wcharPtrToCharPtr(const wchar_t* line) {
1019 | std::size_t len_ = wcslen(line) + 1;
1020 | char* buff_ = static_cast(malloc(len_ + 1));
1021 | # if ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS)
1022 | std::wcstombs(buff_, line, len_);
1023 | # elif ELPP_OS_WINDOWS
1024 | std::size_t convCount_ = 0;
1025 | mbstate_t mbState_;
1026 | ::memset(static_cast(&mbState_), 0, sizeof(mbState_));
1027 | wcsrtombs_s(&convCount_, buff_, len_, &line, len_, &mbState_);
1028 | # endif // ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS)
1029 | return buff_;
1030 | }
1031 |
1032 | // OS
1033 |
1034 | #if ELPP_OS_WINDOWS
1035 | /// @brief Gets environment variables for Windows based OS.
1036 | /// We are not using getenv(const char*)
because of CRT deprecation
1037 | /// @param varname Variable name to get environment variable value for
1038 | /// @return If variable exist the value of it otherwise nullptr
1039 | const char* OS::getWindowsEnvironmentVariable(const char* varname) {
1040 | const DWORD bufferLen = 50;
1041 | static char buffer[bufferLen];
1042 | if (GetEnvironmentVariableA(varname, buffer, bufferLen)) {
1043 | return buffer;
1044 | }
1045 | return nullptr;
1046 | }
1047 | #endif // ELPP_OS_WINDOWS
1048 | #if ELPP_OS_ANDROID
1049 | std::string OS::getProperty(const char* prop) {
1050 | char propVal[PROP_VALUE_MAX + 1];
1051 | int ret = __system_property_get(prop, propVal);
1052 | return ret == 0 ? std::string() : std::string(propVal);
1053 | }
1054 |
1055 | std::string OS::getDeviceName(void) {
1056 | std::stringstream ss;
1057 | std::string manufacturer = getProperty("ro.product.manufacturer");
1058 | std::string model = getProperty("ro.product.model");
1059 | if (manufacturer.empty() || model.empty()) {
1060 | return std::string();
1061 | }
1062 | ss << manufacturer << "-" << model;
1063 | return ss.str();
1064 | }
1065 | #endif // ELPP_OS_ANDROID
1066 |
1067 | const std::string OS::getBashOutput(const char* command) {
1068 | #if (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN)
1069 | if (command == nullptr) {
1070 | return std::string();
1071 | }
1072 | FILE* proc = nullptr;
1073 | if ((proc = popen(command, "r")) == nullptr) {
1074 | ELPP_INTERNAL_ERROR("\nUnable to run command [" << command << "]", true);
1075 | return std::string();
1076 | }
1077 | char hBuff[4096];
1078 | if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) {
1079 | pclose(proc);
1080 | const std::size_t buffLen = strlen(hBuff);
1081 | if (buffLen > 0 && hBuff[buffLen - 1] == '\n') {
1082 | hBuff[buffLen - 1] = '\0';
1083 | }
1084 | return std::string(hBuff);
1085 | } else {
1086 | pclose(proc);
1087 | }
1088 | return std::string();
1089 | #else
1090 | ELPP_UNUSED(command);
1091 | return std::string();
1092 | #endif // (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN)
1093 | }
1094 |
1095 | std::string OS::getEnvironmentVariable(const char* variableName, const char* defaultVal,
1096 | const char* alternativeBashCommand) {
1097 | #if ELPP_OS_UNIX
1098 | const char* val = getenv(variableName);
1099 | #elif ELPP_OS_WINDOWS
1100 | const char* val = getWindowsEnvironmentVariable(variableName);
1101 | #endif // ELPP_OS_UNIX
1102 | if ((val == nullptr) || ((strcmp(val, "") == 0))) {
1103 | #if ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH)
1104 | // Try harder on unix-based systems
1105 | std::string valBash = base::utils::OS::getBashOutput(alternativeBashCommand);
1106 | if (valBash.empty()) {
1107 | return std::string(defaultVal);
1108 | } else {
1109 | return valBash;
1110 | }
1111 | #elif ELPP_OS_WINDOWS || ELPP_OS_UNIX
1112 | ELPP_UNUSED(alternativeBashCommand);
1113 | return std::string(defaultVal);
1114 | #endif // ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH)
1115 | }
1116 | return std::string(val);
1117 | }
1118 |
1119 | std::string OS::currentUser(void) {
1120 | #if ELPP_OS_UNIX && !ELPP_OS_ANDROID
1121 | return getEnvironmentVariable("USER", base::consts::kUnknownUser, "whoami");
1122 | #elif ELPP_OS_WINDOWS
1123 | return getEnvironmentVariable("USERNAME", base::consts::kUnknownUser);
1124 | #elif ELPP_OS_ANDROID
1125 | ELPP_UNUSED(base::consts::kUnknownUser);
1126 | return std::string("android");
1127 | #else
1128 | return std::string();
1129 | #endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID
1130 | }
1131 |
1132 | std::string OS::currentHost(void) {
1133 | #if ELPP_OS_UNIX && !ELPP_OS_ANDROID
1134 | return getEnvironmentVariable("HOSTNAME", base::consts::kUnknownHost, "hostname");
1135 | #elif ELPP_OS_WINDOWS
1136 | return getEnvironmentVariable("COMPUTERNAME", base::consts::kUnknownHost);
1137 | #elif ELPP_OS_ANDROID
1138 | ELPP_UNUSED(base::consts::kUnknownHost);
1139 | return getDeviceName();
1140 | #else
1141 | return std::string();
1142 | #endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID
1143 | }
1144 |
1145 | bool OS::termSupportsColor(void) {
1146 | std::string term = getEnvironmentVariable("TERM", "");
1147 | return term == "xterm" || term == "xterm-color" || term == "xterm-256color"
1148 | || term == "screen" || term == "linux" || term == "cygwin"
1149 | || term == "screen-256color";
1150 | }
1151 |
1152 | // DateTime
1153 |
1154 | void DateTime::gettimeofday(struct timeval* tv) {
1155 | #if ELPP_OS_WINDOWS
1156 | if (tv != nullptr) {
1157 | # if ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS)
1158 | const unsigned __int64 delta_ = 11644473600000000Ui64;
1159 | # else
1160 | const unsigned __int64 delta_ = 11644473600000000ULL;
1161 | # endif // ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS)
1162 | const double secOffSet = 0.000001;
1163 | const unsigned long usecOffSet = 1000000;
1164 | FILETIME fileTime;
1165 | GetSystemTimeAsFileTime(&fileTime);
1166 | unsigned __int64 present = 0;
1167 | present |= fileTime.dwHighDateTime;
1168 | present = present << 32;
1169 | present |= fileTime.dwLowDateTime;
1170 | present /= 10; // mic-sec
1171 | // Subtract the difference
1172 | present -= delta_;
1173 | tv->tv_sec = static_cast(present * secOffSet);
1174 | tv->tv_usec = static_cast(present % usecOffSet);
1175 | }
1176 | #else
1177 | ::gettimeofday(tv, nullptr);
1178 | #endif // ELPP_OS_WINDOWS
1179 | }
1180 |
1181 | std::string DateTime::getDateTime(const char* format, const base::SubsecondPrecision* ssPrec) {
1182 | struct timeval currTime;
1183 | gettimeofday(&currTime);
1184 | return timevalToString(currTime, format, ssPrec);
1185 | }
1186 |
1187 | std::string DateTime::timevalToString(struct timeval tval, const char* format,
1188 | const el::base::SubsecondPrecision* ssPrec) {
1189 | struct ::tm timeInfo;
1190 | buildTimeInfo(&tval, &timeInfo);
1191 | const int kBuffSize = 30;
1192 | char buff_[kBuffSize] = "";
1193 | parseFormat(buff_, kBuffSize, format, &timeInfo, static_cast(tval.tv_usec / ssPrec->m_offset),
1194 | ssPrec);
1195 | return std::string(buff_);
1196 | }
1197 |
1198 | base::type::string_t DateTime::formatTime(unsigned long long time, base::TimestampUnit timestampUnit) {
1199 | base::type::EnumType start = static_cast(timestampUnit);
1200 | const base::type::char_t* unit = base::consts::kTimeFormats[start].unit;
1201 | for (base::type::EnumType i = start; i < base::consts::kTimeFormatsCount - 1; ++i) {
1202 | if (time <= base::consts::kTimeFormats[i].value) {
1203 | break;
1204 | }
1205 | if (base::consts::kTimeFormats[i].value == 1000.0f && time / 1000.0f < 1.9f) {
1206 | break;
1207 | }
1208 | time /= static_cast(base::consts::kTimeFormats[i].value);
1209 | unit = base::consts::kTimeFormats[i + 1].unit;
1210 | }
1211 | base::type::stringstream_t ss;
1212 | ss << time << " " << unit;
1213 | return ss.str();
1214 | }
1215 |
1216 | unsigned long long DateTime::getTimeDifference(const struct timeval& endTime, const struct timeval& startTime,
1217 | base::TimestampUnit timestampUnit) {
1218 | if (timestampUnit == base::TimestampUnit::Microsecond) {
1219 | return static_cast(static_cast(1000000 * endTime.tv_sec + endTime.tv_usec) -
1220 | static_cast(1000000 * startTime.tv_sec + startTime.tv_usec));
1221 | }
1222 | // milliseconds
1223 | auto conv = [](const struct timeval& tim) {
1224 | return static_cast((tim.tv_sec * 1000) + (tim.tv_usec / 1000));
1225 | };
1226 | return static_cast(conv(endTime) - conv(startTime));
1227 | }
1228 |
1229 | struct ::tm* DateTime::buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo) {
1230 | #if ELPP_OS_UNIX
1231 | time_t rawTime = currTime->tv_sec;
1232 | ::elpptime_r(&rawTime, timeInfo);
1233 | return timeInfo;
1234 | #else
1235 | # if ELPP_COMPILER_MSVC
1236 | ELPP_UNUSED(currTime);
1237 | time_t t;
1238 | # if defined(_USE_32BIT_TIME_T)
1239 | _time32(&t);
1240 | # else
1241 | _time64(&t);
1242 | # endif
1243 | elpptime_s(timeInfo, &t);
1244 | return timeInfo;
1245 | # else
1246 | // For any other compilers that don't have CRT warnings issue e.g, MinGW or TDM GCC- we use different method
1247 | time_t rawTime = currTime->tv_sec;
1248 | struct tm* tmInf = elpptime(&rawTime);
1249 | *timeInfo = *tmInf;
1250 | return timeInfo;
1251 | # endif // ELPP_COMPILER_MSVC
1252 | #endif // ELPP_OS_UNIX
1253 | }
1254 |
1255 | char* DateTime::parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo,
1256 | std::size_t msec, const base::SubsecondPrecision* ssPrec) {
1257 | const char* bufLim = buf + bufSz;
1258 | for (; *format; ++format) {
1259 | if (*format == base::consts::kFormatSpecifierChar) {
1260 | switch (*++format) {
1261 | case base::consts::kFormatSpecifierChar: // Escape
1262 | break;
1263 | case '\0': // End
1264 | --format;
1265 | break;
1266 | case 'd': // Day
1267 | buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mday, 2, buf, bufLim);
1268 | continue;
1269 | case 'a': // Day of week (short)
1270 | buf = base::utils::Str::addToBuff(base::consts::kDaysAbbrev[tInfo->tm_wday], buf, bufLim);
1271 | continue;
1272 | case 'A': // Day of week (long)
1273 | buf = base::utils::Str::addToBuff(base::consts::kDays[tInfo->tm_wday], buf, bufLim);
1274 | continue;
1275 | case 'M': // month
1276 | buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mon + 1, 2, buf, bufLim);
1277 | continue;
1278 | case 'b': // month (short)
1279 | buf = base::utils::Str::addToBuff(base::consts::kMonthsAbbrev[tInfo->tm_mon], buf, bufLim);
1280 | continue;
1281 | case 'B': // month (long)
1282 | buf = base::utils::Str::addToBuff(base::consts::kMonths[tInfo->tm_mon], buf, bufLim);
1283 | continue;
1284 | case 'y': // year (two digits)
1285 | buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 2, buf, bufLim);
1286 | continue;
1287 | case 'Y': // year (four digits)
1288 | buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 4, buf, bufLim);
1289 | continue;
1290 | case 'h': // hour (12-hour)
1291 | buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour % 12, 2, buf, bufLim);
1292 | continue;
1293 | case 'H': // hour (24-hour)
1294 | buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour, 2, buf, bufLim);
1295 | continue;
1296 | case 'm': // minute
1297 | buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_min, 2, buf, bufLim);
1298 | continue;
1299 | case 's': // second
1300 | buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_sec, 2, buf, bufLim);
1301 | continue;
1302 | case 'z': // subsecond part
1303 | case 'g':
1304 | buf = base::utils::Str::convertAndAddToBuff(msec, ssPrec->m_width, buf, bufLim);
1305 | continue;
1306 | case 'F': // AM/PM
1307 | buf = base::utils::Str::addToBuff((tInfo->tm_hour >= 12) ? base::consts::kPm : base::consts::kAm, buf, bufLim);
1308 | continue;
1309 | default:
1310 | continue;
1311 | }
1312 | }
1313 | if (buf == bufLim) break;
1314 | *buf++ = *format;
1315 | }
1316 | return buf;
1317 | }
1318 |
1319 | // CommandLineArgs
1320 |
1321 | void CommandLineArgs::setArgs(int argc, char** argv) {
1322 | m_params.clear();
1323 | m_paramsWithValue.clear();
1324 | if (argc == 0 || argv == nullptr) {
1325 | return;
1326 | }
1327 | m_argc = argc;
1328 | m_argv = argv;
1329 | for (int i = 1; i < m_argc; ++i) {
1330 | const char* v = (strstr(m_argv[i], "="));
1331 | if (v != nullptr && strlen(v) > 0) {
1332 | std::string key = std::string(m_argv[i]);
1333 | key = key.substr(0, key.find_first_of('='));
1334 | if (hasParamWithValue(key.c_str())) {
1335 | ELPP_INTERNAL_INFO(1, "Skipping [" << key << "] arg since it already has value ["
1336 | << getParamValue(key.c_str()) << "]");
1337 | } else {
1338 | m_paramsWithValue.insert(std::make_pair(key, std::string(v + 1)));
1339 | }
1340 | }
1341 | if (v == nullptr) {
1342 | if (hasParam(m_argv[i])) {
1343 | ELPP_INTERNAL_INFO(1, "Skipping [" << m_argv[i] << "] arg since it already exists");
1344 | } else {
1345 | m_params.push_back(std::string(m_argv[i]));
1346 | }
1347 | }
1348 | }
1349 | }
1350 |
1351 | bool CommandLineArgs::hasParamWithValue(const char* paramKey) const {
1352 | return m_paramsWithValue.find(std::string(paramKey)) != m_paramsWithValue.end();
1353 | }
1354 |
1355 | const char* CommandLineArgs::getParamValue(const char* paramKey) const {
1356 | std::unordered_map::const_iterator iter = m_paramsWithValue.find(std::string(paramKey));
1357 | return iter != m_paramsWithValue.end() ? iter->second.c_str() : "";
1358 | }
1359 |
1360 | bool CommandLineArgs::hasParam(const char* paramKey) const {
1361 | return std::find(m_params.begin(), m_params.end(), std::string(paramKey)) != m_params.end();
1362 | }
1363 |
1364 | bool CommandLineArgs::empty(void) const {
1365 | return m_params.empty() && m_paramsWithValue.empty();
1366 | }
1367 |
1368 | std::size_t CommandLineArgs::size(void) const {
1369 | return m_params.size() + m_paramsWithValue.size();
1370 | }
1371 |
1372 | base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c) {
1373 | for (int i = 1; i < c.m_argc; ++i) {
1374 | os << ELPP_LITERAL("[") << c.m_argv[i] << ELPP_LITERAL("]");
1375 | if (i < c.m_argc - 1) {
1376 | os << ELPP_LITERAL(" ");
1377 | }
1378 | }
1379 | return os;
1380 | }
1381 |
1382 | } // namespace utils
1383 |
1384 | // el::base::threading
1385 | namespace threading {
1386 |
1387 | #if ELPP_THREADING_ENABLED
1388 | # if ELPP_USE_STD_THREADING
1389 | # if ELPP_ASYNC_LOGGING
1390 | static void msleep(int ms) {
1391 | // Only when async logging enabled - this is because async is strict on compiler
1392 | # if defined(ELPP_NO_SLEEP_FOR)
1393 | usleep(ms * 1000);
1394 | # else
1395 | std::this_thread::sleep_for(std::chrono::milliseconds(ms));
1396 | # endif // defined(ELPP_NO_SLEEP_FOR)
1397 | }
1398 | # endif // ELPP_ASYNC_LOGGING
1399 | # endif // !ELPP_USE_STD_THREADING
1400 | #endif // ELPP_THREADING_ENABLED
1401 |
1402 | } // namespace threading
1403 |
1404 | // el::base
1405 |
1406 | // SubsecondPrecision
1407 |
1408 | void SubsecondPrecision::init(int width) {
1409 | if (width < 1 || width > 6) {
1410 | width = base::consts::kDefaultSubsecondPrecision;
1411 | }
1412 | m_width = width;
1413 | switch (m_width) {
1414 | case 3:
1415 | m_offset = 1000;
1416 | break;
1417 | case 4:
1418 | m_offset = 100;
1419 | break;
1420 | case 5:
1421 | m_offset = 10;
1422 | break;
1423 | case 6:
1424 | m_offset = 1;
1425 | break;
1426 | default:
1427 | m_offset = 1000;
1428 | break;
1429 | }
1430 | }
1431 |
1432 | // LogFormat
1433 |
1434 | LogFormat::LogFormat(void) :
1435 | m_level(Level::Unknown),
1436 | m_userFormat(base::type::string_t()),
1437 | m_format(base::type::string_t()),
1438 | m_dateTimeFormat(std::string()),
1439 | m_flags(0x0),
1440 | m_currentUser(base::utils::OS::currentUser()),
1441 | m_currentHost(base::utils::OS::currentHost()) {
1442 | }
1443 |
1444 | LogFormat::LogFormat(Level level, const base::type::string_t& format)
1445 | : m_level(level), m_userFormat(format), m_currentUser(base::utils::OS::currentUser()),
1446 | m_currentHost(base::utils::OS::currentHost()) {
1447 | parseFromFormat(m_userFormat);
1448 | }
1449 |
1450 | LogFormat::LogFormat(const LogFormat& logFormat):
1451 | m_level(logFormat.m_level),
1452 | m_userFormat(logFormat.m_userFormat),
1453 | m_format(logFormat.m_format),
1454 | m_dateTimeFormat(logFormat.m_dateTimeFormat),
1455 | m_flags(logFormat.m_flags),
1456 | m_currentUser(logFormat.m_currentUser),
1457 | m_currentHost(logFormat.m_currentHost) {
1458 | }
1459 |
1460 | LogFormat::LogFormat(LogFormat&& logFormat) {
1461 | m_level = std::move(logFormat.m_level);
1462 | m_userFormat = std::move(logFormat.m_userFormat);
1463 | m_format = std::move(logFormat.m_format);
1464 | m_dateTimeFormat = std::move(logFormat.m_dateTimeFormat);
1465 | m_flags = std::move(logFormat.m_flags);
1466 | m_currentUser = std::move(logFormat.m_currentUser);
1467 | m_currentHost = std::move(logFormat.m_currentHost);
1468 | }
1469 |
1470 | LogFormat& LogFormat::operator=(const LogFormat& logFormat) {
1471 | if (&logFormat != this) {
1472 | m_level = logFormat.m_level;
1473 | m_userFormat = logFormat.m_userFormat;
1474 | m_dateTimeFormat = logFormat.m_dateTimeFormat;
1475 | m_flags = logFormat.m_flags;
1476 | m_currentUser = logFormat.m_currentUser;
1477 | m_currentHost = logFormat.m_currentHost;
1478 | }
1479 | return *this;
1480 | }
1481 |
1482 | bool LogFormat::operator==(const LogFormat& other) {
1483 | return m_level == other.m_level && m_userFormat == other.m_userFormat && m_format == other.m_format &&
1484 | m_dateTimeFormat == other.m_dateTimeFormat && m_flags == other.m_flags;
1485 | }
1486 |
1487 | /// @brief Updates format to be used while logging.
1488 | /// @param userFormat User provided format
1489 | void LogFormat::parseFromFormat(const base::type::string_t& userFormat) {
1490 | // We make copy because we will be changing the format
1491 | // i.e, removing user provided date format from original format
1492 | // and then storing it.
1493 | base::type::string_t formatCopy = userFormat;
1494 | m_flags = 0x0;
1495 | auto conditionalAddFlag = [&](const base::type::char_t* specifier, base::FormatFlags flag) {
1496 | std::size_t foundAt = base::type::string_t::npos;
1497 | while ((foundAt = formatCopy.find(specifier, foundAt + 1)) != base::type::string_t::npos) {
1498 | if (foundAt > 0 && formatCopy[foundAt - 1] == base::consts::kFormatSpecifierChar) {
1499 | if (hasFlag(flag)) {
1500 | // If we already have flag we remove the escape chars so that '%%' is turned to '%'
1501 | // even after specifier resolution - this is because we only replaceFirst specifier
1502 | formatCopy.erase(foundAt - 1, 1);
1503 | ++foundAt;
1504 | }
1505 | } else {
1506 | if (!hasFlag(flag)) addFlag(flag);
1507 | }
1508 | }
1509 | };
1510 | conditionalAddFlag(base::consts::kAppNameFormatSpecifier, base::FormatFlags::AppName);
1511 | conditionalAddFlag(base::consts::kSeverityLevelFormatSpecifier, base::FormatFlags::Level);
1512 | conditionalAddFlag(base::consts::kSeverityLevelShortFormatSpecifier, base::FormatFlags::LevelShort);
1513 | conditionalAddFlag(base::consts::kLoggerIdFormatSpecifier, base::FormatFlags::LoggerId);
1514 | conditionalAddFlag(base::consts::kThreadIdFormatSpecifier, base::FormatFlags::ThreadId);
1515 | conditionalAddFlag(base::consts::kLogFileFormatSpecifier, base::FormatFlags::File);
1516 | conditionalAddFlag(base::consts::kLogFileBaseFormatSpecifier, base::FormatFlags::FileBase);
1517 | conditionalAddFlag(base::consts::kLogLineFormatSpecifier, base::FormatFlags::Line);
1518 | conditionalAddFlag(base::consts::kLogLocationFormatSpecifier, base::FormatFlags::Location);
1519 | conditionalAddFlag(base::consts::kLogFunctionFormatSpecifier, base::FormatFlags::Function);
1520 | conditionalAddFlag(base::consts::kCurrentUserFormatSpecifier, base::FormatFlags::User);
1521 | conditionalAddFlag(base::consts::kCurrentHostFormatSpecifier, base::FormatFlags::Host);
1522 | conditionalAddFlag(base::consts::kMessageFormatSpecifier, base::FormatFlags::LogMessage);
1523 | conditionalAddFlag(base::consts::kVerboseLevelFormatSpecifier, base::FormatFlags::VerboseLevel);
1524 | // For date/time we need to extract user's date format first
1525 | std::size_t dateIndex = std::string::npos;
1526 | if ((dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier)) != std::string::npos) {
1527 | while (dateIndex > 0 && formatCopy[dateIndex - 1] == base::consts::kFormatSpecifierChar) {
1528 | dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier, dateIndex + 1);
1529 | }
1530 | if (dateIndex != std::string::npos) {
1531 | addFlag(base::FormatFlags::DateTime);
1532 | updateDateFormat(dateIndex, formatCopy);
1533 | }
1534 | }
1535 | m_format = formatCopy;
1536 | updateFormatSpec();
1537 | }
1538 |
1539 | void LogFormat::updateDateFormat(std::size_t index, base::type::string_t& currFormat) {
1540 | if (hasFlag(base::FormatFlags::DateTime)) {
1541 | index += ELPP_STRLEN(base::consts::kDateTimeFormatSpecifier);
1542 | }
1543 | const base::type::char_t* ptr = currFormat.c_str() + index;
1544 | if ((currFormat.size() > index) && (ptr[0] == '{')) {
1545 | // User has provided format for date/time
1546 | ++ptr;
1547 | int count = 1; // Start by 1 in order to remove starting brace
1548 | std::stringstream ss;
1549 | for (; *ptr; ++ptr, ++count) {
1550 | if (*ptr == '}') {
1551 | ++count; // In order to remove ending brace
1552 | break;
1553 | }
1554 | ss << static_cast(*ptr);
1555 | }
1556 | currFormat.erase(index, count);
1557 | m_dateTimeFormat = ss.str();
1558 | } else {
1559 | // No format provided, use default
1560 | if (hasFlag(base::FormatFlags::DateTime)) {
1561 | m_dateTimeFormat = std::string(base::consts::kDefaultDateTimeFormat);
1562 | }
1563 | }
1564 | }
1565 |
1566 | void LogFormat::updateFormatSpec(void) {
1567 | // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet.
1568 | if (m_level == Level::Debug) {
1569 | base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
1570 | base::consts::kDebugLevelLogValue);
1571 | base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
1572 | base::consts::kDebugLevelShortLogValue);
1573 | } else if (m_level == Level::Info) {
1574 | base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
1575 | base::consts::kInfoLevelLogValue);
1576 | base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
1577 | base::consts::kInfoLevelShortLogValue);
1578 | } else if (m_level == Level::Warning) {
1579 | base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
1580 | base::consts::kWarningLevelLogValue);
1581 | base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
1582 | base::consts::kWarningLevelShortLogValue);
1583 | } else if (m_level == Level::Error) {
1584 | base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
1585 | base::consts::kErrorLevelLogValue);
1586 | base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
1587 | base::consts::kErrorLevelShortLogValue);
1588 | } else if (m_level == Level::Fatal) {
1589 | base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
1590 | base::consts::kFatalLevelLogValue);
1591 | base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
1592 | base::consts::kFatalLevelShortLogValue);
1593 | } else if (m_level == Level::Verbose) {
1594 | base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
1595 | base::consts::kVerboseLevelLogValue);
1596 | base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
1597 | base::consts::kVerboseLevelShortLogValue);
1598 | } else if (m_level == Level::Trace) {
1599 | base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
1600 | base::consts::kTraceLevelLogValue);
1601 | base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
1602 | base::consts::kTraceLevelShortLogValue);
1603 | }
1604 | if (hasFlag(base::FormatFlags::User)) {
1605 | base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentUserFormatSpecifier,
1606 | m_currentUser);
1607 | }
1608 | if (hasFlag(base::FormatFlags::Host)) {
1609 | base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentHostFormatSpecifier,
1610 | m_currentHost);
1611 | }
1612 | // Ignore Level::Global and Level::Unknown
1613 | }
1614 |
1615 | // TypedConfigurations
1616 |
1617 | TypedConfigurations::TypedConfigurations(Configurations* configurations,
1618 | base::LogStreamsReferenceMap* logStreamsReference) {
1619 | m_configurations = configurations;
1620 | m_logStreamsReference = logStreamsReference;
1621 | build(m_configurations);
1622 | }
1623 |
1624 | TypedConfigurations::TypedConfigurations(const TypedConfigurations& other) {
1625 | this->m_configurations = other.m_configurations;
1626 | this->m_logStreamsReference = other.m_logStreamsReference;
1627 | build(m_configurations);
1628 | }
1629 |
1630 | bool TypedConfigurations::enabled(Level level) {
1631 | return getConfigByVal(level, &m_enabledMap, "enabled");
1632 | }
1633 |
1634 | bool TypedConfigurations::toFile(Level level) {
1635 | return getConfigByVal(level, &m_toFileMap, "toFile");
1636 | }
1637 |
1638 | const std::string& TypedConfigurations::filename(Level level) {
1639 | return getConfigByRef(level, &m_filenameMap, "filename");
1640 | }
1641 |
1642 | bool TypedConfigurations::toStandardOutput(Level level) {
1643 | return getConfigByVal(level, &m_toStandardOutputMap, "toStandardOutput");
1644 | }
1645 |
1646 | const base::LogFormat& TypedConfigurations::logFormat(Level level) {
1647 | return getConfigByRef(level, &m_logFormatMap, "logFormat");
1648 | }
1649 |
1650 | const base::SubsecondPrecision& TypedConfigurations::subsecondPrecision(Level level) {
1651 | return getConfigByRef(level, &m_subsecondPrecisionMap, "subsecondPrecision");
1652 | }
1653 |
1654 | const base::MillisecondsWidth& TypedConfigurations::millisecondsWidth(Level level) {
1655 | return getConfigByRef(level, &m_subsecondPrecisionMap, "millisecondsWidth");
1656 | }
1657 |
1658 | bool TypedConfigurations::performanceTracking(Level level) {
1659 | return getConfigByVal(level, &m_performanceTrackingMap, "performanceTracking");
1660 | }
1661 |
1662 | base::type::fstream_t* TypedConfigurations::fileStream(Level level) {
1663 | return getConfigByRef(level, &m_fileStreamMap, "fileStream").get();
1664 | }
1665 |
1666 | std::size_t TypedConfigurations::maxLogFileSize(Level level) {
1667 | return getConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize");
1668 | }
1669 |
1670 | std::size_t TypedConfigurations::logFlushThreshold(Level level) {
1671 | return getConfigByVal(level, &m_logFlushThresholdMap, "logFlushThreshold");
1672 | }
1673 |
1674 | void TypedConfigurations::build(Configurations* configurations) {
1675 | base::threading::ScopedLock scopedLock(lock());
1676 | auto getBool = [] (std::string boolStr) -> bool { // Pass by value for trimming
1677 | base::utils::Str::trim(boolStr);
1678 | return (boolStr == "TRUE" || boolStr == "true" || boolStr == "1");
1679 | };
1680 | std::vector withFileSizeLimit;
1681 | for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) {
1682 | Configuration* conf = *it;
1683 | // We cannot use switch on strong enums because Intel C++ dont support them yet
1684 | if (conf->configurationType() == ConfigurationType::Enabled) {
1685 | setValue(conf->level(), getBool(conf->value()), &m_enabledMap);
1686 | } else if (conf->configurationType() == ConfigurationType::ToFile) {
1687 | setValue(conf->level(), getBool(conf->value()), &m_toFileMap);
1688 | } else if (conf->configurationType() == ConfigurationType::ToStandardOutput) {
1689 | setValue(conf->level(), getBool(conf->value()), &m_toStandardOutputMap);
1690 | } else if (conf->configurationType() == ConfigurationType::Filename) {
1691 | // We do not yet configure filename but we will configure in another
1692 | // loop. This is because if file cannot be created, we will force ToFile
1693 | // to be false. Because configuring logger is not necessarily performance
1694 | // sensative operation, we can live with another loop; (by the way this loop
1695 | // is not very heavy either)
1696 | } else if (conf->configurationType() == ConfigurationType::Format) {
1697 | setValue(conf->level(), base::LogFormat(conf->level(),
1698 | base::type::string_t(conf->value().begin(), conf->value().end())), &m_logFormatMap);
1699 | } else if (conf->configurationType() == ConfigurationType::SubsecondPrecision) {
1700 | setValue(Level::Global,
1701 | base::SubsecondPrecision(static_cast(getULong(conf->value()))), &m_subsecondPrecisionMap);
1702 | } else if (conf->configurationType() == ConfigurationType::PerformanceTracking) {
1703 | setValue(Level::Global, getBool(conf->value()), &m_performanceTrackingMap);
1704 | } else if (conf->configurationType() == ConfigurationType::MaxLogFileSize) {
1705 | auto v = getULong(conf->value());
1706 | setValue(conf->level(), static_cast(v), &m_maxLogFileSizeMap);
1707 | if (v != 0) {
1708 | withFileSizeLimit.push_back(conf);
1709 | }
1710 | } else if (conf->configurationType() == ConfigurationType::LogFlushThreshold) {
1711 | setValue(conf->level(), static_cast(getULong(conf->value())), &m_logFlushThresholdMap);
1712 | }
1713 | }
1714 | // As mentioned earlier, we will now set filename configuration in separate loop to deal with non-existent files
1715 | for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) {
1716 | Configuration* conf = *it;
1717 | if (conf->configurationType() == ConfigurationType::Filename) {
1718 | insertFile(conf->level(), conf->value());
1719 | }
1720 | }
1721 | for (std::vector::iterator conf = withFileSizeLimit.begin();
1722 | conf != withFileSizeLimit.end(); ++conf) {
1723 | // This is not unsafe as mutex is locked in currect scope
1724 | unsafeValidateFileRolling((*conf)->level(), base::defaultPreRollOutCallback);
1725 | }
1726 | }
1727 |
1728 | unsigned long TypedConfigurations::getULong(std::string confVal) {
1729 | bool valid = true;
1730 | base::utils::Str::trim(confVal);
1731 | valid = !confVal.empty() && std::find_if(confVal.begin(), confVal.end(),
1732 | [](char c) {
1733 | return !base::utils::Str::isDigit(c);
1734 | }) == confVal.end();
1735 | if (!valid) {
1736 | valid = false;
1737 | ELPP_ASSERT(valid, "Configuration value not a valid integer [" << confVal << "]");
1738 | return 0;
1739 | }
1740 | return atol(confVal.c_str());
1741 | }
1742 |
1743 | std::string TypedConfigurations::resolveFilename(const std::string& filename) {
1744 | std::string resultingFilename = filename;
1745 | std::size_t dateIndex = std::string::npos;
1746 | std::string dateTimeFormatSpecifierStr = std::string(base::consts::kDateTimeFormatSpecifierForFilename);
1747 | if ((dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str())) != std::string::npos) {
1748 | while (dateIndex > 0 && resultingFilename[dateIndex - 1] == base::consts::kFormatSpecifierChar) {
1749 | dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str(), dateIndex + 1);
1750 | }
1751 | if (dateIndex != std::string::npos) {
1752 | const char* ptr = resultingFilename.c_str() + dateIndex;
1753 | // Goto end of specifier
1754 | ptr += dateTimeFormatSpecifierStr.size();
1755 | std::string fmt;
1756 | if ((resultingFilename.size() > dateIndex) && (ptr[0] == '{')) {
1757 | // User has provided format for date/time
1758 | ++ptr;
1759 | int count = 1; // Start by 1 in order to remove starting brace
1760 | std::stringstream ss;
1761 | for (; *ptr; ++ptr, ++count) {
1762 | if (*ptr == '}') {
1763 | ++count; // In order to remove ending brace
1764 | break;
1765 | }
1766 | ss << *ptr;
1767 | }
1768 | resultingFilename.erase(dateIndex + dateTimeFormatSpecifierStr.size(), count);
1769 | fmt = ss.str();
1770 | } else {
1771 | fmt = std::string(base::consts::kDefaultDateTimeFormatInFilename);
1772 | }
1773 | base::SubsecondPrecision ssPrec(3);
1774 | std::string now = base::utils::DateTime::getDateTime(fmt.c_str(), &ssPrec);
1775 | base::utils::Str::replaceAll(now, '/', '-'); // Replace path element since we are dealing with filename
1776 | base::utils::Str::replaceAll(resultingFilename, dateTimeFormatSpecifierStr, now);
1777 | }
1778 | }
1779 | return resultingFilename;
1780 | }
1781 |
1782 | void TypedConfigurations::insertFile(Level level, const std::string& fullFilename) {
1783 | std::string resolvedFilename = resolveFilename(fullFilename);
1784 | if (resolvedFilename.empty()) {
1785 | std::cerr << "Could not load empty file for logging, please re-check your configurations for level ["
1786 | << LevelHelper::convertToString(level) << "]";
1787 | }
1788 | std::string filePath = base::utils::File::extractPathFromFilename(resolvedFilename, base::consts::kFilePathSeperator);
1789 | if (filePath.size() < resolvedFilename.size()) {
1790 | base::utils::File::createPath(filePath);
1791 | }
1792 | auto create = [&](Level level) {
1793 | base::LogStreamsReferenceMap::iterator filestreamIter = m_logStreamsReference->find(resolvedFilename);
1794 | base::type::fstream_t* fs = nullptr;
1795 | if (filestreamIter == m_logStreamsReference->end()) {
1796 | // We need a completely new stream, nothing to share with
1797 | fs = base::utils::File::newFileStream(resolvedFilename);
1798 | m_filenameMap.insert(std::make_pair(level, resolvedFilename));
1799 | m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(fs)));
1800 | m_logStreamsReference->insert(std::make_pair(resolvedFilename, base::FileStreamPtr(m_fileStreamMap.at(level))));
1801 | } else {
1802 | // Woops! we have an existing one, share it!
1803 | m_filenameMap.insert(std::make_pair(level, filestreamIter->first));
1804 | m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(filestreamIter->second)));
1805 | fs = filestreamIter->second.get();
1806 | }
1807 | if (fs == nullptr) {
1808 | // We display bad file error from newFileStream()
1809 | ELPP_INTERNAL_ERROR("Setting [TO_FILE] of ["
1810 | << LevelHelper::convertToString(level) << "] to FALSE", false);
1811 | setValue(level, false, &m_toFileMap);
1812 | }
1813 | };
1814 | // If we dont have file conf for any level, create it for Level::Global first
1815 | // otherwise create for specified level
1816 | create(m_filenameMap.empty() && m_fileStreamMap.empty() ? Level::Global : level);
1817 | }
1818 |
1819 | bool TypedConfigurations::unsafeValidateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback) {
1820 | base::type::fstream_t* fs = unsafeGetConfigByRef(level, &m_fileStreamMap, "fileStream").get();
1821 | if (fs == nullptr) {
1822 | return true;
1823 | }
1824 | std::size_t maxLogFileSize = unsafeGetConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize");
1825 | std::size_t currFileSize = base::utils::File::getSizeOfFile(fs);
1826 | if (maxLogFileSize != 0 && currFileSize >= maxLogFileSize) {
1827 | std::string fname = unsafeGetConfigByRef(level, &m_filenameMap, "filename");
1828 | ELPP_INTERNAL_INFO(1, "Truncating log file [" << fname << "] as a result of configurations for level ["
1829 | << LevelHelper::convertToString(level) << "]");
1830 | fs->close();
1831 | preRollOutCallback(fname.c_str(), currFileSize);
1832 | fs->open(fname, std::fstream::out | std::fstream::trunc);
1833 | return true;
1834 | }
1835 | return false;
1836 | }
1837 |
1838 | // RegisteredHitCounters
1839 |
1840 | bool RegisteredHitCounters::validateEveryN(const char* filename, base::type::LineNumber lineNumber, std::size_t n) {
1841 | base::threading::ScopedLock scopedLock(lock());
1842 | base::HitCounter* counter = get(filename, lineNumber);
1843 | if (counter == nullptr) {
1844 | registerNew(counter = new base::HitCounter(filename, lineNumber));
1845 | }
1846 | counter->validateHitCounts(n);
1847 | bool result = (n >= 1 && counter->hitCounts() != 0 && counter->hitCounts() % n == 0);
1848 | return result;
1849 | }
1850 |
1851 | /// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one
1852 | /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned
1853 | bool RegisteredHitCounters::validateAfterN(const char* filename, base::type::LineNumber lineNumber, std::size_t n) {
1854 | base::threading::ScopedLock scopedLock(lock());
1855 | base::HitCounter* counter = get(filename, lineNumber);
1856 | if (counter == nullptr) {
1857 | registerNew(counter = new base::HitCounter(filename, lineNumber));
1858 | }
1859 | // Do not use validateHitCounts here since we do not want to reset counter here
1860 | // Note the >= instead of > because we are incrementing
1861 | // after this check
1862 | if (counter->hitCounts() >= n)
1863 | return true;
1864 | counter->increment();
1865 | return false;
1866 | }
1867 |
1868 | /// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one
1869 | /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned
1870 | bool RegisteredHitCounters::validateNTimes(const char* filename, base::type::LineNumber lineNumber, std::size_t n) {
1871 | base::threading::ScopedLock scopedLock(lock());
1872 | base::HitCounter* counter = get(filename, lineNumber);
1873 | if (counter == nullptr) {
1874 | registerNew(counter = new base::HitCounter(filename, lineNumber));
1875 | }
1876 | counter->increment();
1877 | // Do not use validateHitCounts here since we do not want to reset counter here
1878 | if (counter->hitCounts() <= n)
1879 | return true;
1880 | return false;
1881 | }
1882 |
1883 | // RegisteredLoggers
1884 |
1885 | RegisteredLoggers::RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder) :
1886 | m_defaultLogBuilder(defaultLogBuilder) {
1887 | m_defaultConfigurations.setToDefault();
1888 | }
1889 |
1890 | Logger* RegisteredLoggers::get(const std::string& id, bool forceCreation) {
1891 | base::threading::ScopedLock scopedLock(lock());
1892 | Logger* logger_ = base::utils::Registry::get(id);
1893 | if (logger_ == nullptr && forceCreation) {
1894 | bool validId = Logger::isValidId(id);
1895 | if (!validId) {
1896 | ELPP_ASSERT(validId, "Invalid logger ID [" << id << "]. Not registering this logger.");
1897 | return nullptr;
1898 | }
1899 | logger_ = new Logger(id, m_defaultConfigurations, &m_logStreamsReference);
1900 | logger_->m_logBuilder = m_defaultLogBuilder;
1901 | registerNew(id, logger_);
1902 | LoggerRegistrationCallback* callback = nullptr;
1903 | for (const std::pair& h
1904 | : m_loggerRegistrationCallbacks) {
1905 | callback = h.second.get();
1906 | if (callback != nullptr && callback->enabled()) {
1907 | callback->handle(logger_);
1908 | }
1909 | }
1910 | }
1911 | return logger_;
1912 | }
1913 |
1914 | bool RegisteredLoggers::remove(const std::string& id) {
1915 | if (id == base::consts::kDefaultLoggerId) {
1916 | return false;
1917 | }
1918 | // get has internal lock
1919 | Logger* logger = base::utils::Registry::get(id);
1920 | if (logger != nullptr) {
1921 | // unregister has internal lock
1922 | unregister(logger);
1923 | }
1924 | return true;
1925 | }
1926 |
1927 | void RegisteredLoggers::unsafeFlushAll(void) {
1928 | ELPP_INTERNAL_INFO(1, "Flushing all log files");
1929 | for (base::LogStreamsReferenceMap::iterator it = m_logStreamsReference.begin();
1930 | it != m_logStreamsReference.end(); ++it) {
1931 | if (it->second.get() == nullptr) continue;
1932 | it->second->flush();
1933 | }
1934 | }
1935 |
1936 | // VRegistry
1937 |
1938 | VRegistry::VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags) : m_level(level), m_pFlags(pFlags) {
1939 | }
1940 |
1941 | /// @brief Sets verbose level. Accepted range is 0-9
1942 | void VRegistry::setLevel(base::type::VerboseLevel level) {
1943 | base::threading::ScopedLock scopedLock(lock());
1944 | if (level > 9)
1945 | m_level = base::consts::kMaxVerboseLevel;
1946 | else
1947 | m_level = level;
1948 | }
1949 |
1950 | void VRegistry::setModules(const char* modules) {
1951 | base::threading::ScopedLock scopedLock(lock());
1952 | auto addSuffix = [](std::stringstream& ss, const char* sfx, const char* prev) {
1953 | if (prev != nullptr && base::utils::Str::endsWith(ss.str(), std::string(prev))) {
1954 | std::string chr(ss.str().substr(0, ss.str().size() - strlen(prev)));
1955 | ss.str(std::string(""));
1956 | ss << chr;
1957 | }
1958 | if (base::utils::Str::endsWith(ss.str(), std::string(sfx))) {
1959 | std::string chr(ss.str().substr(0, ss.str().size() - strlen(sfx)));
1960 | ss.str(std::string(""));
1961 | ss << chr;
1962 | }
1963 | ss << sfx;
1964 | };
1965 | auto insert = [&](std::stringstream& ss, base::type::VerboseLevel level) {
1966 | if (!base::utils::hasFlag(LoggingFlag::DisableVModulesExtensions, *m_pFlags)) {
1967 | addSuffix(ss, ".h", nullptr);
1968 | m_modules.insert(std::make_pair(ss.str(), level));
1969 | addSuffix(ss, ".c", ".h");
1970 | m_modules.insert(std::make_pair(ss.str(), level));
1971 | addSuffix(ss, ".cpp", ".c");
1972 | m_modules.insert(std::make_pair(ss.str(), level));
1973 | addSuffix(ss, ".cc", ".cpp");
1974 | m_modules.insert(std::make_pair(ss.str(), level));
1975 | addSuffix(ss, ".cxx", ".cc");
1976 | m_modules.insert(std::make_pair(ss.str(), level));
1977 | addSuffix(ss, ".-inl.h", ".cxx");
1978 | m_modules.insert(std::make_pair(ss.str(), level));
1979 | addSuffix(ss, ".hxx", ".-inl.h");
1980 | m_modules.insert(std::make_pair(ss.str(), level));
1981 | addSuffix(ss, ".hpp", ".hxx");
1982 | m_modules.insert(std::make_pair(ss.str(), level));
1983 | addSuffix(ss, ".hh", ".hpp");
1984 | }
1985 | m_modules.insert(std::make_pair(ss.str(), level));
1986 | };
1987 | bool isMod = true;
1988 | bool isLevel = false;
1989 | std::stringstream ss;
1990 | int level = -1;
1991 | for (; *modules; ++modules) {
1992 | switch (*modules) {
1993 | case '=':
1994 | isLevel = true;
1995 | isMod = false;
1996 | break;
1997 | case ',':
1998 | isLevel = false;
1999 | isMod = true;
2000 | if (!ss.str().empty() && level != -1) {
2001 | insert(ss, static_cast(level));
2002 | ss.str(std::string(""));
2003 | level = -1;
2004 | }
2005 | break;
2006 | default:
2007 | if (isMod) {
2008 | ss << *modules;
2009 | } else if (isLevel) {
2010 | if (isdigit(*modules)) {
2011 | level = static_cast(*modules) - 48;
2012 | }
2013 | }
2014 | break;
2015 | }
2016 | }
2017 | if (!ss.str().empty() && level != -1) {
2018 | insert(ss, static_cast(level));
2019 | }
2020 | }
2021 |
2022 | bool VRegistry::allowed(base::type::VerboseLevel vlevel, const char* file) {
2023 | base::threading::ScopedLock scopedLock(lock());
2024 | if (m_modules.empty() || file == nullptr) {
2025 | return vlevel <= m_level;
2026 | } else {
2027 | char baseFilename[base::consts::kSourceFilenameMaxLength] = "";
2028 | base::utils::File::buildBaseFilename(file, baseFilename);
2029 | std::unordered_map::iterator it = m_modules.begin();
2030 | for (; it != m_modules.end(); ++it) {
2031 | if (base::utils::Str::wildCardMatch(baseFilename, it->first.c_str())) {
2032 | return vlevel <= it->second;
2033 | }
2034 | }
2035 | if (base::utils::hasFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified, *m_pFlags)) {
2036 | return true;
2037 | }
2038 | return false;
2039 | }
2040 | }
2041 |
2042 | void VRegistry::setFromArgs(const base::utils::CommandLineArgs* commandLineArgs) {
2043 | if (commandLineArgs->hasParam("-v") || commandLineArgs->hasParam("--verbose") ||
2044 | commandLineArgs->hasParam("-V") || commandLineArgs->hasParam("--VERBOSE")) {
2045 | setLevel(base::consts::kMaxVerboseLevel);
2046 | } else if (commandLineArgs->hasParamWithValue("--v")) {
2047 | setLevel(static_cast(atoi(commandLineArgs->getParamValue("--v"))));
2048 | } else if (commandLineArgs->hasParamWithValue("--V")) {
2049 | setLevel(static_cast(atoi(commandLineArgs->getParamValue("--V"))));
2050 | } else if ((commandLineArgs->hasParamWithValue("-vmodule")) && vModulesEnabled()) {
2051 | setModules(commandLineArgs->getParamValue("-vmodule"));
2052 | } else if (commandLineArgs->hasParamWithValue("-VMODULE") && vModulesEnabled()) {
2053 | setModules(commandLineArgs->getParamValue("-VMODULE"));
2054 | }
2055 | }
2056 |
2057 | #if !defined(ELPP_DEFAULT_LOGGING_FLAGS)
2058 | # define ELPP_DEFAULT_LOGGING_FLAGS 0x0
2059 | #endif // !defined(ELPP_DEFAULT_LOGGING_FLAGS)
2060 | // Storage
2061 | #if ELPP_ASYNC_LOGGING
2062 | Storage::Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) :
2063 | #else
2064 | Storage::Storage(const LogBuilderPtr& defaultLogBuilder) :
2065 | #endif // ELPP_ASYNC_LOGGING
2066 | m_registeredHitCounters(new base::RegisteredHitCounters()),
2067 | m_registeredLoggers(new base::RegisteredLoggers(defaultLogBuilder)),
2068 | m_flags(ELPP_DEFAULT_LOGGING_FLAGS),
2069 | m_vRegistry(new base::VRegistry(0, &m_flags)),
2070 |
2071 | #if ELPP_ASYNC_LOGGING
2072 | m_asyncLogQueue(new base::AsyncLogQueue()),
2073 | m_asyncDispatchWorker(asyncDispatchWorker),
2074 | #endif // ELPP_ASYNC_LOGGING
2075 |
2076 | m_preRollOutCallback(base::defaultPreRollOutCallback) {
2077 | // Register default logger
2078 | m_registeredLoggers->get(std::string(base::consts::kDefaultLoggerId));
2079 | // We register default logger anyway (worse case it's not going to register) just in case
2080 | m_registeredLoggers->get("default");
2081 |
2082 | #if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
2083 | // Register performance logger and reconfigure format
2084 | Logger* performanceLogger = m_registeredLoggers->get(std::string(base::consts::kPerformanceLoggerId));
2085 | m_registeredLoggers->get("performance");
2086 | performanceLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%datetime %level %msg"));
2087 | performanceLogger->reconfigure();
2088 | #endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
2089 |
2090 | #if defined(ELPP_SYSLOG)
2091 | // Register syslog logger and reconfigure format
2092 | Logger* sysLogLogger = m_registeredLoggers->get(std::string(base::consts::kSysLogLoggerId));
2093 | sysLogLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%level: %msg"));
2094 | sysLogLogger->reconfigure();
2095 | #endif // defined(ELPP_SYSLOG)
2096 | addFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified);
2097 | #if ELPP_ASYNC_LOGGING
2098 | installLogDispatchCallback(std::string("AsyncLogDispatchCallback"));
2099 | #else
2100 | installLogDispatchCallback(std::string("DefaultLogDispatchCallback"));
2101 | #endif // ELPP_ASYNC_LOGGING
2102 | #if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
2103 | installPerformanceTrackingCallback
2104 | (std::string("DefaultPerformanceTrackingCallback"));
2105 | #endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
2106 | ELPP_INTERNAL_INFO(1, "Easylogging++ has been initialized");
2107 | #if ELPP_ASYNC_LOGGING
2108 | m_asyncDispatchWorker->start();
2109 | #endif // ELPP_ASYNC_LOGGING
2110 | }
2111 |
2112 | Storage::~Storage(void) {
2113 | ELPP_INTERNAL_INFO(4, "Destroying storage");
2114 | #if ELPP_ASYNC_LOGGING
2115 | ELPP_INTERNAL_INFO(5, "Replacing log dispatch callback to synchronous");
2116 | uninstallLogDispatchCallback(std::string("AsyncLogDispatchCallback"));
2117 | installLogDispatchCallback(std::string("DefaultLogDispatchCallback"));
2118 | ELPP_INTERNAL_INFO(5, "Destroying asyncDispatchWorker");
2119 | base::utils::safeDelete(m_asyncDispatchWorker);
2120 | ELPP_INTERNAL_INFO(5, "Destroying asyncLogQueue");
2121 | base::utils::safeDelete(m_asyncLogQueue);
2122 | #endif // ELPP_ASYNC_LOGGING
2123 | ELPP_INTERNAL_INFO(5, "Destroying registeredHitCounters");
2124 | base::utils::safeDelete(m_registeredHitCounters);
2125 | ELPP_INTERNAL_INFO(5, "Destroying registeredLoggers");
2126 | base::utils::safeDelete(m_registeredLoggers);
2127 | ELPP_INTERNAL_INFO(5, "Destroying vRegistry");
2128 | base::utils::safeDelete(m_vRegistry);
2129 | }
2130 |
2131 | bool Storage::hasCustomFormatSpecifier(const char* formatSpecifier) {
2132 | base::threading::ScopedLock scopedLock(customFormatSpecifiersLock());
2133 | return std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(),
2134 | formatSpecifier) != m_customFormatSpecifiers.end();
2135 | }
2136 |
2137 | void Storage::installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) {
2138 | if (hasCustomFormatSpecifier(customFormatSpecifier.formatSpecifier())) {
2139 | return;
2140 | }
2141 | base::threading::ScopedLock scopedLock(customFormatSpecifiersLock());
2142 | m_customFormatSpecifiers.push_back(customFormatSpecifier);
2143 | }
2144 |
2145 | bool Storage::uninstallCustomFormatSpecifier(const char* formatSpecifier) {
2146 | base::threading::ScopedLock scopedLock(customFormatSpecifiersLock());
2147 | std::vector::iterator it = std::find(m_customFormatSpecifiers.begin(),
2148 | m_customFormatSpecifiers.end(), formatSpecifier);
2149 | if (it != m_customFormatSpecifiers.end() && strcmp(formatSpecifier, it->formatSpecifier()) == 0) {
2150 | m_customFormatSpecifiers.erase(it);
2151 | return true;
2152 | }
2153 | return false;
2154 | }
2155 |
2156 | void Storage::setApplicationArguments(int argc, char** argv) {
2157 | m_commandLineArgs.setArgs(argc, argv);
2158 | m_vRegistry->setFromArgs(commandLineArgs());
2159 | // default log file
2160 | #if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
2161 | if (m_commandLineArgs.hasParamWithValue(base::consts::kDefaultLogFileParam)) {
2162 | Configurations c;
2163 | c.setGlobally(ConfigurationType::Filename,
2164 | std::string(m_commandLineArgs.getParamValue(base::consts::kDefaultLogFileParam)));
2165 | registeredLoggers()->setDefaultConfigurations(c);
2166 | for (base::RegisteredLoggers::iterator it = registeredLoggers()->begin();
2167 | it != registeredLoggers()->end(); ++it) {
2168 | it->second->configure(c);
2169 | }
2170 | }
2171 | #endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
2172 | #if defined(ELPP_LOGGING_FLAGS_FROM_ARG)
2173 | if (m_commandLineArgs.hasParamWithValue(base::consts::kLoggingFlagsParam)) {
2174 | int userInput = atoi(m_commandLineArgs.getParamValue(base::consts::kLoggingFlagsParam));
2175 | if (ELPP_DEFAULT_LOGGING_FLAGS == 0x0) {
2176 | m_flags = userInput;
2177 | } else {
2178 | base::utils::addFlag(userInput, &m_flags);
2179 | }
2180 | }
2181 | #endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG)
2182 | }
2183 |
2184 | } // namespace base
2185 |
2186 | // LogDispatchCallback
2187 | void LogDispatchCallback::handle(const LogDispatchData* data) {
2188 | #if defined(ELPP_THREAD_SAFE)
2189 | base::threading::ScopedLock scopedLock(m_fileLocksMapLock);
2190 | std::string filename = data->logMessage()->logger()->typedConfigurations()->filename(data->logMessage()->level());
2191 | auto lock = m_fileLocks.find(filename);
2192 | if (lock == m_fileLocks.end()) {
2193 | m_fileLocks.emplace(std::make_pair(filename, std::unique_ptr(new base::threading::Mutex)));
2194 | }
2195 | #endif
2196 | }
2197 |
2198 | base::threading::Mutex& LogDispatchCallback::fileHandle(const LogDispatchData* data) {
2199 | auto it = m_fileLocks.find(data->logMessage()->logger()->typedConfigurations()->filename(data->logMessage()->level()));
2200 | return *(it->second.get());
2201 | }
2202 |
2203 | namespace base {
2204 | // DefaultLogDispatchCallback
2205 |
2206 | void DefaultLogDispatchCallback::handle(const LogDispatchData* data) {
2207 | #if defined(ELPP_THREAD_SAFE)
2208 | LogDispatchCallback::handle(data);
2209 | base::threading::ScopedLock scopedLock(fileHandle(data));
2210 | #endif
2211 | m_data = data;
2212 | dispatch(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(),
2213 | m_data->dispatchAction() == base::DispatchAction::NormalLog));
2214 | }
2215 |
2216 | void DefaultLogDispatchCallback::dispatch(base::type::string_t&& logLine) {
2217 | if (m_data->dispatchAction() == base::DispatchAction::NormalLog) {
2218 | if (m_data->logMessage()->logger()->m_typedConfigurations->toFile(m_data->logMessage()->level())) {
2219 | base::type::fstream_t* fs = m_data->logMessage()->logger()->m_typedConfigurations->fileStream(
2220 | m_data->logMessage()->level());
2221 | if (fs != nullptr) {
2222 | fs->write(logLine.c_str(), logLine.size());
2223 | if (fs->fail()) {
2224 | ELPP_INTERNAL_ERROR("Unable to write log to file ["
2225 | << m_data->logMessage()->logger()->m_typedConfigurations->filename(m_data->logMessage()->level()) << "].\n"
2226 | << "Few possible reasons (could be something else):\n" << " * Permission denied\n"
2227 | << " * Disk full\n" << " * Disk is not writable", true);
2228 | } else {
2229 | if (ELPP->hasFlag(LoggingFlag::ImmediateFlush)
2230 | || (m_data->logMessage()->logger()->isFlushNeeded(m_data->logMessage()->level()))) {
2231 | m_data->logMessage()->logger()->flush(m_data->logMessage()->level(), fs);
2232 | }
2233 | }
2234 | } else {
2235 | ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(m_data->logMessage()->level()) << "] "
2236 | << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: "
2237 | << m_data->logMessage()->logger()->id() << "]", false);
2238 | }
2239 | }
2240 | if (m_data->logMessage()->logger()->m_typedConfigurations->toStandardOutput(m_data->logMessage()->level())) {
2241 | if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput))
2242 | m_data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, m_data->logMessage()->level());
2243 | ELPP_COUT << ELPP_COUT_LINE(logLine);
2244 | }
2245 | }
2246 | #if defined(ELPP_SYSLOG)
2247 | else if (m_data->dispatchAction() == base::DispatchAction::SysLog) {
2248 | // Determine syslog priority
2249 | int sysLogPriority = 0;
2250 | if (m_data->logMessage()->level() == Level::Fatal)
2251 | sysLogPriority = LOG_EMERG;
2252 | else if (m_data->logMessage()->level() == Level::Error)
2253 | sysLogPriority = LOG_ERR;
2254 | else if (m_data->logMessage()->level() == Level::Warning)
2255 | sysLogPriority = LOG_WARNING;
2256 | else if (m_data->logMessage()->level() == Level::Info)
2257 | sysLogPriority = LOG_INFO;
2258 | else if (m_data->logMessage()->level() == Level::Debug)
2259 | sysLogPriority = LOG_DEBUG;
2260 | else
2261 | sysLogPriority = LOG_NOTICE;
2262 | # if defined(ELPP_UNICODE)
2263 | char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str());
2264 | syslog(sysLogPriority, "%s", line);
2265 | free(line);
2266 | # else
2267 | syslog(sysLogPriority, "%s", logLine.c_str());
2268 | # endif
2269 | }
2270 | #endif // defined(ELPP_SYSLOG)
2271 | }
2272 |
2273 | #if ELPP_ASYNC_LOGGING
2274 |
2275 | // AsyncLogDispatchCallback
2276 |
2277 | void AsyncLogDispatchCallback::handle(const LogDispatchData* data) {
2278 | base::type::string_t logLine = data->logMessage()->logger()->logBuilder()->build(data->logMessage(),
2279 | data->dispatchAction() == base::DispatchAction::NormalLog);
2280 | if (data->dispatchAction() == base::DispatchAction::NormalLog
2281 | && data->logMessage()->logger()->typedConfigurations()->toStandardOutput(data->logMessage()->level())) {
2282 | if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput))
2283 | data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, data->logMessage()->level());
2284 | ELPP_COUT << ELPP_COUT_LINE(logLine);
2285 | }
2286 | // Save resources and only queue if we want to write to file otherwise just ignore handler
2287 | if (data->logMessage()->logger()->typedConfigurations()->toFile(data->logMessage()->level())) {
2288 | ELPP->asyncLogQueue()->push(AsyncLogItem(*(data->logMessage()), *data, logLine));
2289 | }
2290 | }
2291 |
2292 | // AsyncDispatchWorker
2293 | AsyncDispatchWorker::AsyncDispatchWorker() {
2294 | setContinueRunning(false);
2295 | }
2296 |
2297 | AsyncDispatchWorker::~AsyncDispatchWorker() {
2298 | setContinueRunning(false);
2299 | ELPP_INTERNAL_INFO(6, "Stopping dispatch worker - Cleaning log queue");
2300 | clean();
2301 | ELPP_INTERNAL_INFO(6, "Log queue cleaned");
2302 | }
2303 |
2304 | bool AsyncDispatchWorker::clean(void) {
2305 | std::mutex m;
2306 | std::unique_lock lk(m);
2307 | cv.wait(lk, [] { return !ELPP->asyncLogQueue()->empty(); });
2308 | emptyQueue();
2309 | lk.unlock();
2310 | cv.notify_one();
2311 | return ELPP->asyncLogQueue()->empty();
2312 | }
2313 |
2314 | void AsyncDispatchWorker::emptyQueue(void) {
2315 | while (!ELPP->asyncLogQueue()->empty()) {
2316 | AsyncLogItem data = ELPP->asyncLogQueue()->next();
2317 | handle(&data);
2318 | base::threading::msleep(100);
2319 | }
2320 | }
2321 |
2322 | void AsyncDispatchWorker::start(void) {
2323 | base::threading::msleep(5000); // 5s (why?)
2324 | setContinueRunning(true);
2325 | std::thread t1(&AsyncDispatchWorker::run, this);
2326 | t1.join();
2327 | }
2328 |
2329 | void AsyncDispatchWorker::handle(AsyncLogItem* logItem) {
2330 | LogDispatchData* data = logItem->data();
2331 | LogMessage* logMessage = logItem->logMessage();
2332 | Logger* logger = logMessage->logger();
2333 | base::TypedConfigurations* conf = logger->typedConfigurations();
2334 | base::type::string_t logLine = logItem->logLine();
2335 | if (data->dispatchAction() == base::DispatchAction::NormalLog) {
2336 | if (conf->toFile(logMessage->level())) {
2337 | base::type::fstream_t* fs = conf->fileStream(logMessage->level());
2338 | if (fs != nullptr) {
2339 | fs->write(logLine.c_str(), logLine.size());
2340 | if (fs->fail()) {
2341 | ELPP_INTERNAL_ERROR("Unable to write log to file ["
2342 | << conf->filename(logMessage->level()) << "].\n"
2343 | << "Few possible reasons (could be something else):\n" << " * Permission denied\n"
2344 | << " * Disk full\n" << " * Disk is not writable", true);
2345 | } else {
2346 | if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) || (logger->isFlushNeeded(logMessage->level()))) {
2347 | logger->flush(logMessage->level(), fs);
2348 | }
2349 | }
2350 | } else {
2351 | ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(logMessage->level()) << "] "
2352 | << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " << logger->id() << "]", false);
2353 | }
2354 | }
2355 | }
2356 | # if defined(ELPP_SYSLOG)
2357 | else if (data->dispatchAction() == base::DispatchAction::SysLog) {
2358 | // Determine syslog priority
2359 | int sysLogPriority = 0;
2360 | if (logMessage->level() == Level::Fatal)
2361 | sysLogPriority = LOG_EMERG;
2362 | else if (logMessage->level() == Level::Error)
2363 | sysLogPriority = LOG_ERR;
2364 | else if (logMessage->level() == Level::Warning)
2365 | sysLogPriority = LOG_WARNING;
2366 | else if (logMessage->level() == Level::Info)
2367 | sysLogPriority = LOG_INFO;
2368 | else if (logMessage->level() == Level::Debug)
2369 | sysLogPriority = LOG_DEBUG;
2370 | else
2371 | sysLogPriority = LOG_NOTICE;
2372 | # if defined(ELPP_UNICODE)
2373 | char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str());
2374 | syslog(sysLogPriority, "%s", line);
2375 | free(line);
2376 | # else
2377 | syslog(sysLogPriority, "%s", logLine.c_str());
2378 | # endif
2379 | }
2380 | # endif // defined(ELPP_SYSLOG)
2381 | }
2382 |
2383 | void AsyncDispatchWorker::run(void) {
2384 | while (continueRunning()) {
2385 | emptyQueue();
2386 | base::threading::msleep(10); // 10ms
2387 | }
2388 | }
2389 | #endif // ELPP_ASYNC_LOGGING
2390 |
2391 | // DefaultLogBuilder
2392 |
2393 | base::type::string_t DefaultLogBuilder::build(const LogMessage* logMessage, bool appendNewLine) const {
2394 | base::TypedConfigurations* tc = logMessage->logger()->typedConfigurations();
2395 | const base::LogFormat* logFormat = &tc->logFormat(logMessage->level());
2396 | base::type::string_t logLine = logFormat->format();
2397 | char buff[base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength] = "";
2398 | const char* bufLim = buff + sizeof(buff);
2399 | if (logFormat->hasFlag(base::FormatFlags::AppName)) {
2400 | // App name
2401 | base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kAppNameFormatSpecifier,
2402 | logMessage->logger()->parentApplicationName());
2403 | }
2404 | if (logFormat->hasFlag(base::FormatFlags::ThreadId)) {
2405 | // Thread ID
2406 | base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kThreadIdFormatSpecifier,
2407 | ELPP->getThreadName(base::threading::getCurrentThreadId()));
2408 | }
2409 | if (logFormat->hasFlag(base::FormatFlags::DateTime)) {
2410 | // DateTime
2411 | base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kDateTimeFormatSpecifier,
2412 | base::utils::DateTime::getDateTime(logFormat->dateTimeFormat().c_str(),
2413 | &tc->subsecondPrecision(logMessage->level())));
2414 | }
2415 | if (logFormat->hasFlag(base::FormatFlags::Function)) {
2416 | // Function
2417 | base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFunctionFormatSpecifier, logMessage->func());
2418 | }
2419 | if (logFormat->hasFlag(base::FormatFlags::File)) {
2420 | // File
2421 | base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength);
2422 | base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff);
2423 | base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileFormatSpecifier, std::string(buff));
2424 | }
2425 | if (logFormat->hasFlag(base::FormatFlags::FileBase)) {
2426 | // FileBase
2427 | base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength);
2428 | base::utils::File::buildBaseFilename(logMessage->file(), buff);
2429 | base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileBaseFormatSpecifier, std::string(buff));
2430 | }
2431 | if (logFormat->hasFlag(base::FormatFlags::Line)) {
2432 | // Line
2433 | char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceLineMaxLength);
2434 | buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), base::consts::kSourceLineMaxLength, buf, bufLim, false);
2435 | base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLineFormatSpecifier, std::string(buff));
2436 | }
2437 | if (logFormat->hasFlag(base::FormatFlags::Location)) {
2438 | // Location
2439 | char* buf = base::utils::Str::clearBuff(buff,
2440 | base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength);
2441 | base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff);
2442 | buf = base::utils::Str::addToBuff(buff, buf, bufLim);
2443 | buf = base::utils::Str::addToBuff(":", buf, bufLim);
2444 | buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), base::consts::kSourceLineMaxLength, buf, bufLim,
2445 | false);
2446 | base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLocationFormatSpecifier, std::string(buff));
2447 | }
2448 | if (logMessage->level() == Level::Verbose && logFormat->hasFlag(base::FormatFlags::VerboseLevel)) {
2449 | // Verbose level
2450 | char* buf = base::utils::Str::clearBuff(buff, 1);
2451 | buf = base::utils::Str::convertAndAddToBuff(logMessage->verboseLevel(), 1, buf, bufLim, false);
2452 | base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kVerboseLevelFormatSpecifier, std::string(buff));
2453 | }
2454 | if (logFormat->hasFlag(base::FormatFlags::LogMessage)) {
2455 | // Log message
2456 | base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kMessageFormatSpecifier, logMessage->message());
2457 | }
2458 | #if !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS)
2459 | el::base::threading::ScopedLock lock_(ELPP->customFormatSpecifiersLock());
2460 | ELPP_UNUSED(lock_);
2461 | for (std::vector::const_iterator it = ELPP->customFormatSpecifiers()->begin();
2462 | it != ELPP->customFormatSpecifiers()->end(); ++it) {
2463 | std::string fs(it->formatSpecifier());
2464 | base::type::string_t wcsFormatSpecifier(fs.begin(), fs.end());
2465 | base::utils::Str::replaceFirstWithEscape(logLine, wcsFormatSpecifier, it->resolver()(logMessage));
2466 | }
2467 | #endif // !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS)
2468 | if (appendNewLine) logLine += ELPP_LITERAL("\n");
2469 | return logLine;
2470 | }
2471 |
2472 | // LogDispatcher
2473 |
2474 | void LogDispatcher::dispatch(void) {
2475 | if (m_proceed && m_dispatchAction == base::DispatchAction::None) {
2476 | m_proceed = false;
2477 | }
2478 | if (!m_proceed) {
2479 | return;
2480 | }
2481 | #ifndef ELPP_NO_GLOBAL_LOCK
2482 | // see https://github.com/muflihun/easyloggingpp/issues/580
2483 | // global lock is turned off by default unless
2484 | // ELPP_NO_GLOBAL_LOCK is defined
2485 | base::threading::ScopedLock scopedLock(ELPP->lock());
2486 | #endif
2487 | base::TypedConfigurations* tc = m_logMessage->logger()->m_typedConfigurations;
2488 | if (ELPP->hasFlag(LoggingFlag::StrictLogFileSizeCheck)) {
2489 | tc->validateFileRolling(m_logMessage->level(), ELPP->preRollOutCallback());
2490 | }
2491 | LogDispatchCallback* callback = nullptr;
2492 | LogDispatchData data;
2493 | for (const std::pair& h
2494 | : ELPP->m_logDispatchCallbacks) {
2495 | callback = h.second.get();
2496 | if (callback != nullptr && callback->enabled()) {
2497 | data.setLogMessage(m_logMessage);
2498 | data.setDispatchAction(m_dispatchAction);
2499 | callback->handle(&data);
2500 | }
2501 | }
2502 | }
2503 |
2504 | // MessageBuilder
2505 |
2506 | void MessageBuilder::initialize(Logger* logger) {
2507 | m_logger = logger;
2508 | m_containerLogSeperator = ELPP->hasFlag(LoggingFlag::NewLineForContainer) ?
2509 | ELPP_LITERAL("\n ") : ELPP_LITERAL(", ");
2510 | }
2511 |
2512 | MessageBuilder& MessageBuilder::operator<<(const wchar_t* msg) {
2513 | if (msg == nullptr) {
2514 | m_logger->stream() << base::consts::kNullPointer;
2515 | return *this;
2516 | }
2517 | # if defined(ELPP_UNICODE)
2518 | m_logger->stream() << msg;
2519 | # else
2520 | char* buff_ = base::utils::Str::wcharPtrToCharPtr(msg);
2521 | m_logger->stream() << buff_;
2522 | free(buff_);
2523 | # endif
2524 | if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) {
2525 | m_logger->stream() << " ";
2526 | }
2527 | return *this;
2528 | }
2529 |
2530 | // Writer
2531 |
2532 | Writer& Writer::construct(Logger* logger, bool needLock) {
2533 | m_logger = logger;
2534 | initializeLogger(logger->id(), false, needLock);
2535 | m_messageBuilder.initialize(m_logger);
2536 | return *this;
2537 | }
2538 |
2539 | Writer& Writer::construct(int count, const char* loggerIds, ...) {
2540 | if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) {
2541 | va_list loggersList;
2542 | va_start(loggersList, loggerIds);
2543 | const char* id = loggerIds;
2544 | m_loggerIds.reserve(count);
2545 | for (int i = 0; i < count; ++i) {
2546 | m_loggerIds.push_back(std::string(id));
2547 | id = va_arg(loggersList, const char*);
2548 | }
2549 | va_end(loggersList);
2550 | initializeLogger(m_loggerIds.at(0));
2551 | } else {
2552 | initializeLogger(std::string(loggerIds));
2553 | }
2554 | m_messageBuilder.initialize(m_logger);
2555 | return *this;
2556 | }
2557 |
2558 | void Writer::initializeLogger(const std::string& loggerId, bool lookup, bool needLock) {
2559 | if (lookup) {
2560 | m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically));
2561 | }
2562 | if (m_logger == nullptr) {
2563 | {
2564 | if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) {
2565 | // Somehow default logger has been unregistered. Not good! Register again
2566 | ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId));
2567 | }
2568 | }
2569 | Writer(Level::Debug, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId)
2570 | << "Logger [" << loggerId << "] is not registered yet!";
2571 | m_proceed = false;
2572 | } else {
2573 | if (needLock) {
2574 | m_logger->acquireLock(); // This should not be unlocked by checking m_proceed because
2575 | // m_proceed can be changed by lines below
2576 | }
2577 | if (ELPP->hasFlag(LoggingFlag::HierarchicalLogging)) {
2578 | m_proceed = m_level == Level::Verbose ? m_logger->enabled(m_level) :
2579 | LevelHelper::castToInt(m_level) >= LevelHelper::castToInt(ELPP->m_loggingLevel);
2580 | } else {
2581 | m_proceed = m_logger->enabled(m_level);
2582 | }
2583 | }
2584 | }
2585 |
2586 | void Writer::processDispatch() {
2587 | #if ELPP_LOGGING_ENABLED
2588 | if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) {
2589 | bool firstDispatched = false;
2590 | base::type::string_t logMessage;
2591 | std::size_t i = 0;
2592 | do {
2593 | if (m_proceed) {
2594 | if (firstDispatched) {
2595 | m_logger->stream() << logMessage;
2596 | } else {
2597 | firstDispatched = true;
2598 | if (m_loggerIds.size() > 1) {
2599 | logMessage = m_logger->stream().str();
2600 | }
2601 | }
2602 | triggerDispatch();
2603 | } else if (m_logger != nullptr) {
2604 | m_logger->stream().str(ELPP_LITERAL(""));
2605 | m_logger->releaseLock();
2606 | }
2607 | if (i + 1 < m_loggerIds.size()) {
2608 | initializeLogger(m_loggerIds.at(i + 1));
2609 | }
2610 | } while (++i < m_loggerIds.size());
2611 | } else {
2612 | if (m_proceed) {
2613 | triggerDispatch();
2614 | } else if (m_logger != nullptr) {
2615 | m_logger->stream().str(ELPP_LITERAL(""));
2616 | m_logger->releaseLock();
2617 | }
2618 | }
2619 | #else
2620 | if (m_logger != nullptr) {
2621 | m_logger->stream().str(ELPP_LITERAL(""));
2622 | m_logger->releaseLock();
2623 | }
2624 | #endif // ELPP_LOGGING_ENABLED
2625 | }
2626 |
2627 | void Writer::triggerDispatch(void) {
2628 | if (m_proceed) {
2629 | if (m_msg == nullptr) {
2630 | LogMessage msg(m_level, m_file, m_line, m_func, m_verboseLevel,
2631 | m_logger);
2632 | base::LogDispatcher(m_proceed, &msg, m_dispatchAction).dispatch();
2633 | } else {
2634 | base::LogDispatcher(m_proceed, m_msg, m_dispatchAction).dispatch();
2635 | }
2636 | }
2637 | if (m_logger != nullptr) {
2638 | m_logger->stream().str(ELPP_LITERAL(""));
2639 | m_logger->releaseLock();
2640 | }
2641 | if (m_proceed && m_level == Level::Fatal
2642 | && !ELPP->hasFlag(LoggingFlag::DisableApplicationAbortOnFatalLog)) {
2643 | base::Writer(Level::Warning, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId)
2644 | << "Aborting application. Reason: Fatal log at [" << m_file << ":" << m_line << "]";
2645 | std::stringstream reasonStream;
2646 | reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]"
2647 | << " If you wish to disable 'abort on fatal log' please use "
2648 | << "el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)";
2649 | base::utils::abort(1, reasonStream.str());
2650 | }
2651 | m_proceed = false;
2652 | }
2653 |
2654 | // PErrorWriter
2655 |
2656 | PErrorWriter::~PErrorWriter(void) {
2657 | if (m_proceed) {
2658 | #if ELPP_COMPILER_MSVC
2659 | char buff[256];
2660 | strerror_s(buff, 256, errno);
2661 | m_logger->stream() << ": " << buff << " [" << errno << "]";
2662 | #else
2663 | m_logger->stream() << ": " << strerror(errno) << " [" << errno << "]";
2664 | #endif
2665 | }
2666 | }
2667 |
2668 | // PerformanceTracker
2669 |
2670 | #if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
2671 |
2672 | PerformanceTracker::PerformanceTracker(const std::string& blockName,
2673 | base::TimestampUnit timestampUnit,
2674 | const std::string& loggerId,
2675 | bool scopedLog, Level level) :
2676 | m_blockName(blockName), m_timestampUnit(timestampUnit), m_loggerId(loggerId), m_scopedLog(scopedLog),
2677 | m_level(level), m_hasChecked(false), m_lastCheckpointId(std::string()), m_enabled(false) {
2678 | #if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
2679 | // We store it locally so that if user happen to change configuration by the end of scope
2680 | // or before calling checkpoint, we still depend on state of configuraton at time of construction
2681 | el::Logger* loggerPtr = ELPP->registeredLoggers()->get(loggerId, false);
2682 | m_enabled = loggerPtr != nullptr && loggerPtr->m_typedConfigurations->performanceTracking(m_level);
2683 | if (m_enabled) {
2684 | base::utils::DateTime::gettimeofday(&m_startTime);
2685 | }
2686 | #endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
2687 | }
2688 |
2689 | PerformanceTracker::~PerformanceTracker(void) {
2690 | #if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
2691 | if (m_enabled) {
2692 | base::threading::ScopedLock scopedLock(lock());
2693 | if (m_scopedLog) {
2694 | base::utils::DateTime::gettimeofday(&m_endTime);
2695 | base::type::string_t formattedTime = getFormattedTimeTaken();
2696 | PerformanceTrackingData data(PerformanceTrackingData::DataType::Complete);
2697 | data.init(this);
2698 | data.m_formattedTimeTaken = formattedTime;
2699 | PerformanceTrackingCallback* callback = nullptr;
2700 | for (const std::pair& h
2701 | : ELPP->m_performanceTrackingCallbacks) {
2702 | callback = h.second.get();
2703 | if (callback != nullptr && callback->enabled()) {
2704 | callback->handle(&data);
2705 | }
2706 | }
2707 | }
2708 | }
2709 | #endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING)
2710 | }
2711 |
2712 | void PerformanceTracker::checkpoint(const std::string& id, const char* file, base::type::LineNumber line,
2713 | const char* func) {
2714 | #if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
2715 | if (m_enabled) {
2716 | base::threading::ScopedLock scopedLock(lock());
2717 | base::utils::DateTime::gettimeofday(&m_endTime);
2718 | base::type::string_t formattedTime = m_hasChecked ? getFormattedTimeTaken(m_lastCheckpointTime) : ELPP_LITERAL("");
2719 | PerformanceTrackingData data(PerformanceTrackingData::DataType::Checkpoint);
2720 | data.init(this);
2721 | data.m_checkpointId = id;
2722 | data.m_file = file;
2723 | data.m_line = line;
2724 | data.m_func = func;
2725 | data.m_formattedTimeTaken = formattedTime;
2726 | PerformanceTrackingCallback* callback = nullptr;
2727 | for (const std::pair& h
2728 | : ELPP->m_performanceTrackingCallbacks) {
2729 | callback = h.second.get();
2730 | if (callback != nullptr && callback->enabled()) {
2731 | callback->handle(&data);
2732 | }
2733 | }
2734 | base::utils::DateTime::gettimeofday(&m_lastCheckpointTime);
2735 | m_hasChecked = true;
2736 | m_lastCheckpointId = id;
2737 | }
2738 | #endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
2739 | ELPP_UNUSED(id);
2740 | ELPP_UNUSED(file);
2741 | ELPP_UNUSED(line);
2742 | ELPP_UNUSED(func);
2743 | }
2744 |
2745 | const base::type::string_t PerformanceTracker::getFormattedTimeTaken(struct timeval startTime) const {
2746 | if (ELPP->hasFlag(LoggingFlag::FixedTimeFormat)) {
2747 | base::type::stringstream_t ss;
2748 | ss << base::utils::DateTime::getTimeDifference(m_endTime,
2749 | startTime, m_timestampUnit) << " " << base::consts::kTimeFormats[static_cast
2750 | (m_timestampUnit)].unit;
2751 | return ss.str();
2752 | }
2753 | return base::utils::DateTime::formatTime(base::utils::DateTime::getTimeDifference(m_endTime,
2754 | startTime, m_timestampUnit), m_timestampUnit);
2755 | }
2756 |
2757 | #endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
2758 |
2759 | namespace debug {
2760 | #if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG)
2761 |
2762 | // StackTrace
2763 |
2764 | StackTrace::StackTraceEntry::StackTraceEntry(std::size_t index, const std::string& loc, const std::string& demang,
2765 | const std::string& hex,
2766 | const std::string& addr) :
2767 | m_index(index),
2768 | m_location(loc),
2769 | m_demangled(demang),
2770 | m_hex(hex),
2771 | m_addr(addr) {
2772 | }
2773 |
2774 | std::ostream& operator<<(std::ostream& ss, const StackTrace::StackTraceEntry& si) {
2775 | ss << "[" << si.m_index << "] " << si.m_location << (si.m_hex.empty() ? "" : "+") << si.m_hex << " " << si.m_addr <<
2776 | (si.m_demangled.empty() ? "" : ":") << si.m_demangled;
2777 | return ss;
2778 | }
2779 |
2780 | std::ostream& operator<<(std::ostream& os, const StackTrace& st) {
2781 | std::vector::const_iterator it = st.m_stack.begin();
2782 | while (it != st.m_stack.end()) {
2783 | os << " " << *it++ << "\n";
2784 | }
2785 | return os;
2786 | }
2787 |
2788 | void StackTrace::generateNew(void) {
2789 | #if ELPP_STACKTRACE
2790 | m_stack.clear();
2791 | void* stack[kMaxStack];
2792 | unsigned int size = backtrace(stack, kMaxStack);
2793 | char** strings = backtrace_symbols(stack, size);
2794 | if (size > kStackStart) { // Skip StackTrace c'tor and generateNew
2795 | for (std::size_t i = kStackStart; i < size; ++i) {
2796 | std::string mangName;
2797 | std::string location;
2798 | std::string hex;
2799 | std::string addr;
2800 |
2801 | // entry: 2 crash.cpp.bin 0x0000000101552be5 _ZN2el4base5debug10StackTraceC1Ev + 21
2802 | const std::string line(strings[i]);
2803 | auto p = line.find("_");
2804 | if (p != std::string::npos) {
2805 | mangName = line.substr(p);
2806 | mangName = mangName.substr(0, mangName.find(" +"));
2807 | }
2808 | p = line.find("0x");
2809 | if (p != std::string::npos) {
2810 | addr = line.substr(p);
2811 | addr = addr.substr(0, addr.find("_"));
2812 | }
2813 | // Perform demangling if parsed properly
2814 | if (!mangName.empty()) {
2815 | int status = 0;
2816 | char* demangName = abi::__cxa_demangle(mangName.data(), 0, 0, &status);
2817 | // if demangling is successful, output the demangled function name
2818 | if (status == 0) {
2819 | // Success (see http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html)
2820 | StackTraceEntry entry(i - 1, location, demangName, hex, addr);
2821 | m_stack.push_back(entry);
2822 | } else {
2823 | // Not successful - we will use mangled name
2824 | StackTraceEntry entry(i - 1, location, mangName, hex, addr);
2825 | m_stack.push_back(entry);
2826 | }
2827 | free(demangName);
2828 | } else {
2829 | StackTraceEntry entry(i - 1, line);
2830 | m_stack.push_back(entry);
2831 | }
2832 | }
2833 | }
2834 | free(strings);
2835 | #else
2836 | ELPP_INTERNAL_INFO(1, "Stacktrace generation not supported for selected compiler");
2837 | #endif // ELPP_STACKTRACE
2838 | }
2839 |
2840 | // Static helper functions
2841 |
2842 | static std::string crashReason(int sig) {
2843 | std::stringstream ss;
2844 | bool foundReason = false;
2845 | for (int i = 0; i < base::consts::kCrashSignalsCount; ++i) {
2846 | if (base::consts::kCrashSignals[i].numb == sig) {
2847 | ss << "Application has crashed due to [" << base::consts::kCrashSignals[i].name << "] signal";
2848 | if (ELPP->hasFlag(el::LoggingFlag::LogDetailedCrashReason)) {
2849 | ss << std::endl <<
2850 | " " << base::consts::kCrashSignals[i].brief << std::endl <<
2851 | " " << base::consts::kCrashSignals[i].detail;
2852 | }
2853 | foundReason = true;
2854 | }
2855 | }
2856 | if (!foundReason) {
2857 | ss << "Application has crashed due to unknown signal [" << sig << "]";
2858 | }
2859 | return ss.str();
2860 | }
2861 | /// @brief Logs reason of crash from sig
2862 | static void logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) {
2863 | if (sig == SIGINT && ELPP->hasFlag(el::LoggingFlag::IgnoreSigInt)) {
2864 | return;
2865 | }
2866 | std::stringstream ss;
2867 | ss << "CRASH HANDLED; ";
2868 | ss << crashReason(sig);
2869 | #if ELPP_STACKTRACE
2870 | if (stackTraceIfAvailable) {
2871 | ss << std::endl << " ======= Backtrace: =========" << std::endl << base::debug::StackTrace();
2872 | }
2873 | #else
2874 | ELPP_UNUSED(stackTraceIfAvailable);
2875 | #endif // ELPP_STACKTRACE
2876 | ELPP_WRITE_LOG(el::base::Writer, level, base::DispatchAction::NormalLog, logger) << ss.str();
2877 | }
2878 |
2879 | static inline void crashAbort(int sig) {
2880 | base::utils::abort(sig, std::string());
2881 | }
2882 |
2883 | /// @brief Default application crash handler
2884 | ///
2885 | /// @detail This function writes log using 'default' logger, prints stack trace for GCC based compilers and aborts program.
2886 | static inline void defaultCrashHandler(int sig) {
2887 | base::debug::logCrashReason(sig, true, Level::Fatal, base::consts::kDefaultLoggerId);
2888 | base::debug::crashAbort(sig);
2889 | }
2890 |
2891 | // CrashHandler
2892 |
2893 | CrashHandler::CrashHandler(bool useDefault) {
2894 | if (useDefault) {
2895 | setHandler(defaultCrashHandler);
2896 | }
2897 | }
2898 |
2899 | void CrashHandler::setHandler(const Handler& cHandler) {
2900 | m_handler = cHandler;
2901 | #if defined(ELPP_HANDLE_SIGABRT)
2902 | int i = 0; // SIGABRT is at base::consts::kCrashSignals[0]
2903 | #else
2904 | int i = 1;
2905 | #endif // defined(ELPP_HANDLE_SIGABRT)
2906 | for (; i < base::consts::kCrashSignalsCount; ++i) {
2907 | m_handler = signal(base::consts::kCrashSignals[i].numb, cHandler);
2908 | }
2909 | }
2910 |
2911 | #endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG)
2912 | } // namespace debug
2913 | } // namespace base
2914 |
2915 | // el
2916 |
2917 | // Helpers
2918 |
2919 | #if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG)
2920 |
2921 | void Helpers::crashAbort(int sig, const char* sourceFile, unsigned int long line) {
2922 | std::stringstream ss;
2923 | ss << base::debug::crashReason(sig).c_str();
2924 | ss << " - [Called el::Helpers::crashAbort(" << sig << ")]";
2925 | if (sourceFile != nullptr && strlen(sourceFile) > 0) {
2926 | ss << " - Source: " << sourceFile;
2927 | if (line > 0)
2928 | ss << ":" << line;
2929 | else
2930 | ss << " (line number not specified)";
2931 | }
2932 | base::utils::abort(sig, ss.str());
2933 | }
2934 |
2935 | void Helpers::logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) {
2936 | el::base::debug::logCrashReason(sig, stackTraceIfAvailable, level, logger);
2937 | }
2938 |
2939 | #endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG)
2940 |
2941 | // Loggers
2942 |
2943 | Logger* Loggers::getLogger(const std::string& identity, bool registerIfNotAvailable) {
2944 | return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable);
2945 | }
2946 |
2947 | void Loggers::setDefaultLogBuilder(el::LogBuilderPtr& logBuilderPtr) {
2948 | ELPP->registeredLoggers()->setDefaultLogBuilder(logBuilderPtr);
2949 | }
2950 |
2951 | bool Loggers::unregisterLogger(const std::string& identity) {
2952 | return ELPP->registeredLoggers()->remove(identity);
2953 | }
2954 |
2955 | bool Loggers::hasLogger(const std::string& identity) {
2956 | return ELPP->registeredLoggers()->has(identity);
2957 | }
2958 |
2959 | Logger* Loggers::reconfigureLogger(Logger* logger, const Configurations& configurations) {
2960 | if (!logger) return nullptr;
2961 | logger->configure(configurations);
2962 | return logger;
2963 | }
2964 |
2965 | Logger* Loggers::reconfigureLogger(const std::string& identity, const Configurations& configurations) {
2966 | return Loggers::reconfigureLogger(Loggers::getLogger(identity), configurations);
2967 | }
2968 |
2969 | Logger* Loggers::reconfigureLogger(const std::string& identity, ConfigurationType configurationType,
2970 | const std::string& value) {
2971 | Logger* logger = Loggers::getLogger(identity);
2972 | if (logger == nullptr) {
2973 | return nullptr;
2974 | }
2975 | logger->configurations()->set(Level::Global, configurationType, value);
2976 | logger->reconfigure();
2977 | return logger;
2978 | }
2979 |
2980 | void Loggers::reconfigureAllLoggers(const Configurations& configurations) {
2981 | for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin();
2982 | it != ELPP->registeredLoggers()->end(); ++it) {
2983 | Loggers::reconfigureLogger(it->second, configurations);
2984 | }
2985 | }
2986 |
2987 | void Loggers::reconfigureAllLoggers(Level level, ConfigurationType configurationType,
2988 | const std::string& value) {
2989 | for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin();
2990 | it != ELPP->registeredLoggers()->end(); ++it) {
2991 | Logger* logger = it->second;
2992 | logger->configurations()->set(level, configurationType, value);
2993 | logger->reconfigure();
2994 | }
2995 | }
2996 |
2997 | void Loggers::setDefaultConfigurations(const Configurations& configurations, bool reconfigureExistingLoggers) {
2998 | ELPP->registeredLoggers()->setDefaultConfigurations(configurations);
2999 | if (reconfigureExistingLoggers) {
3000 | Loggers::reconfigureAllLoggers(configurations);
3001 | }
3002 | }
3003 |
3004 | const Configurations* Loggers::defaultConfigurations(void) {
3005 | return ELPP->registeredLoggers()->defaultConfigurations();
3006 | }
3007 |
3008 | const base::LogStreamsReferenceMap* Loggers::logStreamsReference(void) {
3009 | return ELPP->registeredLoggers()->logStreamsReference();
3010 | }
3011 |
3012 | base::TypedConfigurations Loggers::defaultTypedConfigurations(void) {
3013 | return base::TypedConfigurations(
3014 | ELPP->registeredLoggers()->defaultConfigurations(),
3015 | ELPP->registeredLoggers()->logStreamsReference());
3016 | }
3017 |
3018 | std::vector* Loggers::populateAllLoggerIds(std::vector* targetList) {
3019 | targetList->clear();
3020 | for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->list().begin();
3021 | it != ELPP->registeredLoggers()->list().end(); ++it) {
3022 | targetList->push_back(it->first);
3023 | }
3024 | return targetList;
3025 | }
3026 |
3027 | void Loggers::configureFromGlobal(const char* globalConfigurationFilePath) {
3028 | std::ifstream gcfStream(globalConfigurationFilePath, std::ifstream::in);
3029 | ELPP_ASSERT(gcfStream.is_open(), "Unable to open global configuration file [" << globalConfigurationFilePath
3030 | << "] for parsing.");
3031 | std::string line = std::string();
3032 | std::stringstream ss;
3033 | Logger* logger = nullptr;
3034 | auto configure = [&](void) {
3035 | ELPP_INTERNAL_INFO(8, "Configuring logger: '" << logger->id() << "' with configurations \n" << ss.str()
3036 | << "\n--------------");
3037 | Configurations c;
3038 | c.parseFromText(ss.str());
3039 | logger->configure(c);
3040 | };
3041 | while (gcfStream.good()) {
3042 | std::getline(gcfStream, line);
3043 | ELPP_INTERNAL_INFO(1, "Parsing line: " << line);
3044 | base::utils::Str::trim(line);
3045 | if (Configurations::Parser::isComment(line)) continue;
3046 | Configurations::Parser::ignoreComments(&line);
3047 | base::utils::Str::trim(line);
3048 | if (line.size() > 2 && base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLoggerId))) {
3049 | if (!ss.str().empty() && logger != nullptr) {
3050 | configure();
3051 | }
3052 | ss.str(std::string(""));
3053 | line = line.substr(2);
3054 | base::utils::Str::trim(line);
3055 | if (line.size() > 1) {
3056 | ELPP_INTERNAL_INFO(1, "Getting logger: '" << line << "'");
3057 | logger = getLogger(line);
3058 | }
3059 | } else {
3060 | ss << line << "\n";
3061 | }
3062 | }
3063 | if (!ss.str().empty() && logger != nullptr) {
3064 | configure();
3065 | }
3066 | }
3067 |
3068 | bool Loggers::configureFromArg(const char* argKey) {
3069 | #if defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS)
3070 | ELPP_UNUSED(argKey);
3071 | #else
3072 | if (!Helpers::commandLineArgs()->hasParamWithValue(argKey)) {
3073 | return false;
3074 | }
3075 | configureFromGlobal(Helpers::commandLineArgs()->getParamValue(argKey));
3076 | #endif // defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS)
3077 | return true;
3078 | }
3079 |
3080 | void Loggers::flushAll(void) {
3081 | ELPP->registeredLoggers()->flushAll();
3082 | }
3083 |
3084 | void Loggers::setVerboseLevel(base::type::VerboseLevel level) {
3085 | ELPP->vRegistry()->setLevel(level);
3086 | }
3087 |
3088 | base::type::VerboseLevel Loggers::verboseLevel(void) {
3089 | return ELPP->vRegistry()->level();
3090 | }
3091 |
3092 | void Loggers::setVModules(const char* modules) {
3093 | if (ELPP->vRegistry()->vModulesEnabled()) {
3094 | ELPP->vRegistry()->setModules(modules);
3095 | }
3096 | }
3097 |
3098 | void Loggers::clearVModules(void) {
3099 | ELPP->vRegistry()->clearModules();
3100 | }
3101 |
3102 | // VersionInfo
3103 |
3104 | const std::string VersionInfo::version(void) {
3105 | return std::string("9.96.7");
3106 | }
3107 | /// @brief Release date of current version
3108 | const std::string VersionInfo::releaseDate(void) {
3109 | return std::string("24-11-2018 0728hrs");
3110 | }
3111 |
3112 | } // namespace el
3113 |
--------------------------------------------------------------------------------
/AsyncIPC/named_pipe_async.cpp:
--------------------------------------------------------------------------------
1 | #include "stdafx.h"
2 | #include "named_pipe_async.h"
3 |
4 | NamedPipeAsync::NamedPipeAsync()
5 | : NamedPipeImpl()
6 | {
7 | }
8 |
9 | NamedPipeAsync::~NamedPipeAsync()
10 | {
11 | }
12 |
13 | bool NamedPipeAsync::PipeWrite(void* data, uint32_t size)
14 | {
15 | int ol_size = sizeof(PipeOverlapped);
16 | PipeOverlapped* ol_data = (PipeOverlapped*)malloc(ol_size);
17 | ZeroMemory(ol_data, ol_size);
18 | ol_data->param = this;
19 | BOOL result = ::WriteFileEx(pipe_handle_, data, size, (LPOVERLAPPED)ol_data, OnWriteCompletionRoutine);
20 | if (!result && GetLastError() != ERROR_IO_PENDING) {
21 | LOG(ERROR) << "WriteFileEx failed, error code: " << GetLastError();
22 | return false;
23 | }
24 | return true;
25 | }
26 |
27 | bool NamedPipeAsync::PipeRead()
28 | {
29 | int ol_size = sizeof(PipeOverlapped);
30 | PipeOverlapped* ol_data = (PipeOverlapped*)malloc(ol_size);
31 | ZeroMemory(ol_data, ol_size);
32 | ol_data->param = this;
33 | BOOL result = ::ReadFileEx(pipe_handle_, ol_data->buffer, kPipeBufferSize, (LPOVERLAPPED)ol_data, OnReadCompletionRoutine);
34 | if (!result && GetLastError() != ERROR_IO_PENDING) {
35 | LOG(ERROR) << "ReadFileEx failed, error code: " << GetLastError();
36 | return false;
37 | }
38 | return true;
39 | }
40 |
41 | void WINAPI NamedPipeAsync::OnWriteCompletionRoutine(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
42 | {
43 | PipeOverlapped* ol_data = (PipeOverlapped*)lpOverlapped;
44 | if (dwErrorCode != 0) {
45 | LOG(ERROR) << "OnWriteCompletionRoutine failed, error code: " << dwErrorCode;
46 | free(ol_data);
47 | return;
48 | }
49 | NamedPipeAsync* server = (NamedPipeAsync*)ol_data->param;
50 | if (server->delegate_) {
51 | server->delegate_->OnSend(dwNumberOfBytesTransfered);
52 | }
53 | free(ol_data);
54 | }
55 |
56 | void WINAPI NamedPipeAsync::OnReadCompletionRoutine(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
57 | {
58 | PipeOverlapped* ol_data = (PipeOverlapped*)lpOverlapped;
59 | NamedPipeAsync* server = (NamedPipeAsync*)ol_data->param;
60 | if (dwErrorCode != 0) {
61 | if ((dwErrorCode == ERROR_PIPE_NOT_CONNECTED || dwErrorCode == ERROR_BROKEN_PIPE) && server->delegate_) {
62 | server->delegate_->OnDisconnected();
63 | }
64 | LOG(ERROR) << "OnWriteCompletionRoutine failed, error code: " << dwErrorCode;
65 | free(ol_data);
66 | return;
67 | }
68 | ol_data->buffer[dwNumberOfBytesTransfered] = '\0';
69 | server->PipeRead();
70 | DWORD bytes_trans;
71 | BOOL ol_result = ::GetOverlappedResult(server->pipe_handle_, lpOverlapped, &bytes_trans, FALSE);
72 | if (!ol_result && GetLastError() == ERROR_MORE_DATA) {
73 | LOG(INFO) << "read buffer is not enough";
74 | server->recv_buffer_.append(ol_data->buffer);
75 | free(ol_data);
76 | return;
77 | }
78 | bool is_long_data = !server->recv_buffer_.empty();
79 | if (is_long_data) {
80 | server->recv_buffer_.append(ol_data->buffer);
81 | }
82 | LOG(INFO) << "OnReadCompletionRoutine dwNumberOfBytesTransfered: " << dwNumberOfBytesTransfered << ", data: " << ol_data->buffer;
83 | if (server->delegate_) {
84 | if (is_long_data) {
85 | server->delegate_->OnRecv((void*)server->recv_buffer_.c_str(), server->recv_buffer_.size());
86 | server->recv_buffer_.clear();
87 | }
88 | else {
89 | server->delegate_->OnRecv(ol_data->buffer, dwNumberOfBytesTransfered);
90 | }
91 | }
92 | free(ol_data);
93 | }
94 |
95 | void NamedPipeAsync::DoWork()
96 | {
97 | PipeRead();
98 | while (!need_exit_) {
99 | DWORD wait_result = ::WaitForSingleObjectEx(wait_event_, INFINITE, TRUE);
100 | if (need_exit_)
101 | break;
102 | if (wait_result != WAIT_OBJECT_0 && wait_result != WAIT_IO_COMPLETION)
103 | break;
104 | std::unique_lock auto_lock(send_mutex_);
105 | if (send_list_.empty())
106 | continue;
107 | auto msg = std::move(send_list_.front());
108 | send_list_.pop_front();
109 | auto_lock.unlock();
110 | if (!PipeWrite((void*)msg.c_str(), msg.size())) {
111 | LOG(ERROR) << "Pipe disconnected";
112 | if (delegate_) {
113 | delegate_->OnDisconnected();
114 | }
115 | break;
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/AsyncIPC/named_pipe_async.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "async_ipc.h"
3 | #include "named_pipe_define.h"
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include "named_pipe_impl.h"
13 |
14 | class IPipeDelegate;
15 | class NamedPipeAsync
16 | : public NamedPipeImpl
17 | {
18 | public:
19 | NamedPipeAsync();
20 | ~NamedPipeAsync();
21 |
22 | protected:
23 |
24 | bool PipeWrite(void* data, uint32_t size) override;
25 | bool PipeRead() override;
26 |
27 | static void WINAPI OnWriteCompletionRoutine(
28 | DWORD dwErrorCode,
29 | DWORD dwNumberOfBytesTransfered,
30 | LPOVERLAPPED lpOverlapped
31 | );
32 |
33 | static void WINAPI OnReadCompletionRoutine(
34 | DWORD dwErrorCode,
35 | DWORD dwNumberOfBytesTransfered,
36 | LPOVERLAPPED lpOverlapped
37 | );
38 |
39 | void DoWork();
40 |
41 | private:
42 | std::string recv_buffer_;
43 | };
44 |
45 |
--------------------------------------------------------------------------------
/AsyncIPC/named_pipe_define.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 |
4 | enum OverlappedType {
5 | OverlappedRead = 0,
6 | OverlappedWrite,
7 | };
8 |
9 | static const int kPipeTimeout = 5000;
10 |
11 | static const int kPipeBufferSize = 4096;
12 |
13 | struct PipeOverlapped {
14 | OVERLAPPED ol;
15 | void* param;
16 | char buffer[kPipeBufferSize + 1];
17 | unsigned char type;
18 | DWORD trans_bytes;
19 | };
20 |
21 | static const int kOverlappedSize = sizeof(PIPE_OVERLAPPED);
--------------------------------------------------------------------------------
/AsyncIPC/named_pipe_impl.cpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPC/named_pipe_impl.cpp
--------------------------------------------------------------------------------
/AsyncIPC/named_pipe_impl.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "async_ipc.h"
3 | #include
4 | #include
5 | #include
6 |
7 | #include "named_pipe_define.h"
8 |
9 | class IPipeDelegate;
10 | class NamedPipeImpl
11 | : public INamedPipe
12 | {
13 | public:
14 | NamedPipeImpl();
15 | virtual ~NamedPipeImpl();
16 |
17 | virtual bool Create(const wchar_t* pipe_name, PipeType type, IPipeDelegate* delegate);
18 | virtual void Exit();
19 | virtual void Send(const char* msg);
20 |
21 | protected:
22 | bool CreatePipeServer(const wchar_t* pipe_name);
23 | bool CreatePipeClient(const wchar_t* pipe_name);
24 | void ClosePipe();
25 |
26 | virtual bool PipeWrite(void* data, uint32_t size) = 0;
27 | virtual bool PipeRead() = 0;
28 |
29 | static void ThreadProc(void* param);
30 | virtual void DoWork() = 0;
31 |
32 | protected:
33 | bool need_exit_;
34 | HANDLE pipe_handle_;
35 | IPipeDelegate* delegate_;
36 | std::unique_ptr async_thread_;
37 | std::list send_list_;
38 | std::mutex send_mutex_;
39 | HANDLE wait_event_;
40 | };
41 |
42 |
--------------------------------------------------------------------------------
/AsyncIPC/named_pipe_overlapped.cpp:
--------------------------------------------------------------------------------
1 | #include "stdafx.h"
2 | #include "named_pipe_overlapped.h"
3 |
4 | NamedPipeOverlapped::NamedPipeOverlapped()
5 | : NamedPipeImpl()
6 | , send_flag_(0)
7 | , recv_flag_(0)
8 | {
9 | ZeroMemory(&overlappeds_, sizeof(overlappeds_));
10 | }
11 |
12 | NamedPipeOverlapped::~NamedPipeOverlapped()
13 | {
14 | }
15 |
16 | bool NamedPipeOverlapped::Create(LPCWSTR pipe_name, PipeType type, IPipeDelegate* delegate)
17 | {
18 | overlappeds_[0].type = OverlappedType::OverlappedWrite;
19 | overlappeds_[0].ol.hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
20 | overlappeds_[1].type = OverlappedType::OverlappedRead;
21 | overlappeds_[1].ol.hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
22 | return NamedPipeImpl::Create(pipe_name, type, delegate);
23 | }
24 |
25 | void NamedPipeOverlapped::Exit()
26 | {
27 | NamedPipeImpl::Exit();
28 | if (overlappeds_[0].ol.hEvent) {
29 | ::CloseHandle(overlappeds_[0].ol.hEvent);
30 | overlappeds_[0].ol.hEvent = NULL;
31 | }
32 | if (overlappeds_[1].ol.hEvent) {
33 | ::CloseHandle(overlappeds_[1].ol.hEvent);
34 | overlappeds_[1].ol.hEvent = NULL;
35 | }
36 | }
37 |
38 | bool NamedPipeOverlapped::PipeWrite(void* data, uint32_t size)
39 | {
40 | BOOL result = ::WriteFile(pipe_handle_, data, size, &overlappeds_[0].trans_bytes, (LPOVERLAPPED)&overlappeds_[0]);
41 | DWORD error_code = ::GetLastError();
42 | if (!result && error_code != ERROR_IO_PENDING) {
43 | LOG(ERROR) << "WriteFile failed, error code: " << error_code;
44 | return false;
45 | }
46 | send_flag_++;
47 | return true;
48 | }
49 |
50 | bool NamedPipeOverlapped::PipeRead()
51 | {
52 | BOOL result = ::ReadFile(pipe_handle_, overlappeds_[1].buffer, kPipeBufferSize, &overlappeds_[1].trans_bytes, (LPOVERLAPPED)&overlappeds_[1]);
53 | DWORD error_code = ::GetLastError();
54 | if (!result && error_code != ERROR_IO_PENDING) {
55 | LOG(ERROR) << "ReadFile failed, error code: " << error_code;
56 | return false;
57 | }
58 | recv_flag_++;
59 | return true;
60 | }
61 |
62 | void NamedPipeOverlapped::DoWork()
63 | {
64 | HANDLE handles[3] = { wait_event_, overlappeds_[0].ol.hEvent, overlappeds_[1].ol.hEvent };
65 | DWORD wait_result = 0;
66 | PipeRead();
67 | while (!need_exit_) {
68 | if (send_flag_ < 1) {
69 | std::unique_lock auto_lock(send_mutex_);
70 | if (!send_list_.empty()) {
71 | auto msg = std::move(send_list_.front());
72 | send_list_.pop_front();
73 | PipeWrite((void*)msg.c_str(), msg.size());
74 | }
75 | }
76 | wait_result = ::WaitForMultipleObjects(3, handles, FALSE, INFINITE);
77 | if (need_exit_) {
78 | break;
79 | }
80 | switch (wait_result)
81 | {
82 | case WAIT_OBJECT_0: {
83 | break;
84 | }
85 | case WAIT_OBJECT_0 + 1: {
86 | OnWriteComplete();
87 | break;
88 | }
89 | case WAIT_OBJECT_0 + 2: {
90 | OnReadComplete();
91 | break;
92 | }
93 | default: {
94 | LOG(ERROR) << "WaitForMultipleObjects failed, error code: " << ::GetLastError();
95 | break;
96 | }
97 | }
98 | }
99 | }
100 |
101 | bool NamedPipeOverlapped::OnWriteComplete()
102 | {
103 | send_flag_--;
104 | int recv_count = overlappeds_[0].ol.InternalHigh;
105 | if (delegate_) {
106 | delegate_->OnSend(recv_count);
107 | }
108 | return true;
109 | }
110 |
111 | bool NamedPipeOverlapped::OnReadComplete()
112 | {
113 | recv_flag_--;
114 | int recv_count = overlappeds_[1].ol.InternalHigh;
115 | overlappeds_[1].buffer[recv_count] = '\0';
116 | DWORD bytes_trans;
117 | BOOL ol_result = ::GetOverlappedResult(pipe_handle_, &overlappeds_[1].ol, &bytes_trans, FALSE);
118 | if (!ol_result) {
119 | DWORD code = ::GetLastError();
120 | if (code != ERROR_MORE_DATA) {
121 | LOG(ERROR) << "GetOverlappedResult failed, last error: " << code;
122 | return false;
123 | }
124 | LOG(INFO) << "read buffer is not enough";
125 | recv_buffer_.append(overlappeds_[1].buffer);
126 | PipeRead();
127 | return true;
128 | }
129 | bool is_long_data = !recv_buffer_.empty();
130 | if (is_long_data) {
131 | recv_buffer_.append(overlappeds_[1].buffer);
132 | }
133 | if (delegate_) {
134 | if (is_long_data) {
135 | delegate_->OnRecv((void*)recv_buffer_.c_str(), recv_buffer_.size());
136 | recv_buffer_.clear();
137 | }
138 | else {
139 | delegate_->OnRecv(overlappeds_[1].buffer, recv_count);
140 | }
141 | }
142 | PipeRead();
143 | return true;
144 | }
145 |
--------------------------------------------------------------------------------
/AsyncIPC/named_pipe_overlapped.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "async_ipc.h"
3 | #include "named_pipe_impl.h"
4 |
5 | class NamedPipeOverlapped
6 | : public NamedPipeImpl
7 | {
8 | public:
9 | NamedPipeOverlapped();
10 | ~NamedPipeOverlapped();
11 | bool Create(LPCWSTR pipe_name, PipeType type, IPipeDelegate* delegate) override;
12 | void Exit() override;
13 |
14 | protected:
15 | bool PipeWrite(void* data, uint32_t size) override;
16 | bool PipeRead() override;
17 | void DoWork() override;
18 |
19 | bool OnReadComplete();
20 | bool OnWriteComplete();
21 |
22 | private:
23 | int recv_flag_;
24 | int send_flag_;
25 | PipeOverlapped overlappeds_[2];
26 | std::string recv_buffer_;
27 | };
28 |
29 |
--------------------------------------------------------------------------------
/AsyncIPC/overlapped_pool.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | class OverlappedPool {
4 | public:
5 |
6 | protected:
7 |
8 | private:
9 |
10 | };
--------------------------------------------------------------------------------
/AsyncIPC/stdafx.cpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPC/stdafx.cpp
--------------------------------------------------------------------------------
/AsyncIPC/stdafx.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPC/stdafx.h
--------------------------------------------------------------------------------
/AsyncIPC/targetver.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPC/targetver.h
--------------------------------------------------------------------------------
/AsyncIPCClient.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.21005.1
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AsyncIPCClient", "AsyncIPCClient\AsyncIPCClient.vcxproj", "{B3C852AF-3336-4CE9-8294-1ABAE9FC968E}"
7 | ProjectSection(ProjectDependencies) = postProject
8 | {1A79C766-1CF7-4393-A31D-93E3B728FD1E} = {1A79C766-1CF7-4393-A31D-93E3B728FD1E}
9 | EndProjectSection
10 | EndProject
11 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AsyncIPC", "AsyncIPC\AsyncIPC.vcxproj", "{1A79C766-1CF7-4393-A31D-93E3B728FD1E}"
12 | EndProject
13 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AsyncIPCServer", "AsyncIPCServer\AsyncIPCServer.vcxproj", "{276F0A89-E81C-4F85-A9CA-D7DA52BBD602}"
14 | ProjectSection(ProjectDependencies) = postProject
15 | {1A79C766-1CF7-4393-A31D-93E3B728FD1E} = {1A79C766-1CF7-4393-A31D-93E3B728FD1E}
16 | EndProjectSection
17 | EndProject
18 | Global
19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
20 | Debug|Win32 = Debug|Win32
21 | Release|Win32 = Release|Win32
22 | EndGlobalSection
23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
24 | {B3C852AF-3336-4CE9-8294-1ABAE9FC968E}.Debug|Win32.ActiveCfg = Debug|Win32
25 | {B3C852AF-3336-4CE9-8294-1ABAE9FC968E}.Debug|Win32.Build.0 = Debug|Win32
26 | {B3C852AF-3336-4CE9-8294-1ABAE9FC968E}.Release|Win32.ActiveCfg = Release|Win32
27 | {B3C852AF-3336-4CE9-8294-1ABAE9FC968E}.Release|Win32.Build.0 = Release|Win32
28 | {1A79C766-1CF7-4393-A31D-93E3B728FD1E}.Debug|Win32.ActiveCfg = Debug|Win32
29 | {1A79C766-1CF7-4393-A31D-93E3B728FD1E}.Debug|Win32.Build.0 = Debug|Win32
30 | {1A79C766-1CF7-4393-A31D-93E3B728FD1E}.Release|Win32.ActiveCfg = Release|Win32
31 | {1A79C766-1CF7-4393-A31D-93E3B728FD1E}.Release|Win32.Build.0 = Release|Win32
32 | {276F0A89-E81C-4F85-A9CA-D7DA52BBD602}.Debug|Win32.ActiveCfg = Debug|Win32
33 | {276F0A89-E81C-4F85-A9CA-D7DA52BBD602}.Debug|Win32.Build.0 = Debug|Win32
34 | {276F0A89-E81C-4F85-A9CA-D7DA52BBD602}.Release|Win32.ActiveCfg = Release|Win32
35 | {276F0A89-E81C-4F85-A9CA-D7DA52BBD602}.Release|Win32.Build.0 = Release|Win32
36 | EndGlobalSection
37 | GlobalSection(SolutionProperties) = preSolution
38 | HideSolutionNode = FALSE
39 | EndGlobalSection
40 | EndGlobal
41 |
--------------------------------------------------------------------------------
/AsyncIPCClient/AsyncIPCClient.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "resource.h"
4 |
--------------------------------------------------------------------------------
/AsyncIPCClient/AsyncIPCClient.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPCClient/AsyncIPCClient.ico
--------------------------------------------------------------------------------
/AsyncIPCClient/AsyncIPCClient.rc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPCClient/AsyncIPCClient.rc
--------------------------------------------------------------------------------
/AsyncIPCClient/AsyncIPCClient.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 |
14 | {B3C852AF-3336-4CE9-8294-1ABAE9FC968E}
15 | Win32Proj
16 | AsyncIPCClient
17 |
18 |
19 |
20 | Application
21 | true
22 | v120
23 | Unicode
24 |
25 |
26 | Application
27 | false
28 | v120
29 | true
30 | Unicode
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | true
44 |
45 |
46 | false
47 |
48 |
49 |
50 | Use
51 | Level3
52 | Disabled
53 | WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)
54 | true
55 |
56 |
57 | Windows
58 | true
59 |
60 |
61 |
62 |
63 | Level3
64 | Use
65 | MaxSpeed
66 | true
67 | true
68 | WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
69 | true
70 |
71 |
72 | Windows
73 | true
74 | true
75 | true
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | Create
90 | Create
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/AsyncIPCClient/AsyncIPCClient.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | 头文件
23 |
24 |
25 | 头文件
26 |
27 |
28 | 头文件
29 |
30 |
31 |
32 |
33 | 源文件
34 |
35 |
36 | 源文件
37 |
38 |
39 |
40 |
41 | 资源文件
42 |
43 |
44 |
45 |
46 | 资源文件
47 |
48 |
49 | 资源文件
50 |
51 |
52 |
--------------------------------------------------------------------------------
/AsyncIPCClient/ReadMe.txt:
--------------------------------------------------------------------------------
1 | ========================================================================
2 | WIN32 应用程序:AsyncIPCClient 项目概述
3 | ========================================================================
4 |
5 | 应用程序向导已为您创建了此 AsyncIPCClient 应用程序。
6 |
7 | 本文件概要介绍组成 AsyncIPCClient 应用程序的每个文件的内容。
8 |
9 |
10 | AsyncIPCClient.vcxproj
11 | 这是使用应用程序向导生成的 VC++ 项目的主项目文件,其中包含生成该文件的 Visual C++ 的版本信息,以及有关使用应用程序向导选择的平台、配置和项目功能的信息。
12 |
13 | AsyncIPCClient.vcxproj.filters
14 | 这是使用“应用程序向导”生成的 VC++ 项目筛选器文件。它包含有关项目文件与筛选器之间的关联信息。在 IDE 中,通过这种关联,在特定节点下以分组形式显示具有相似扩展名的文件。例如,“.cpp”文件与“源文件”筛选器关联。
15 |
16 | AsyncIPCClient.cpp
17 | 这是主应用程序源文件。
18 |
19 | /////////////////////////////////////////////////////////////////////////////
20 | 应用程序向导创建了下列资源:
21 |
22 | AsyncIPCClient.rc
23 | 这是程序使用的所有 Microsoft Windows 资源的列表。它包括 RES 子目录中存储的图标、位图和光标。此文件可以直接在 Microsoft Visual C++ 中进行编辑。
24 |
25 | Resource.h
26 | 这是标准头文件,可用于定义新的资源 ID。Microsoft Visual C++ 将读取并更新此文件。
27 |
28 | AsyncIPCClient.ico
29 | 这是用作应用程序图标 (32x32) 的图标文件。此图标包括在主资源文件 AsyncIPCClient.rc 中。
30 |
31 | small.ico
32 | 这是一个图标文件,其中包含应用程序的图标的较小版本 (16x16)。此图标包括在主资源文件 AsyncIPCClient.rc 中。
33 |
34 | /////////////////////////////////////////////////////////////////////////////
35 | 其他标准文件:
36 |
37 | StdAfx.h, StdAfx.cpp
38 | 这些文件用于生成名为 AsyncIPCClient.pch 的预编译头 (PCH) 文件和名为 StdAfx.obj 的预编译类型文件。
39 |
40 | /////////////////////////////////////////////////////////////////////////////
41 | 其他注释:
42 |
43 | 应用程序向导使用“TODO:”注释来指示应添加或自定义的源代码部分。
44 |
45 | /////////////////////////////////////////////////////////////////////////////
46 |
--------------------------------------------------------------------------------
/AsyncIPCClient/Resource.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPCClient/Resource.h
--------------------------------------------------------------------------------
/AsyncIPCClient/main.cpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPCClient/main.cpp
--------------------------------------------------------------------------------
/AsyncIPCClient/small.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPCClient/small.ico
--------------------------------------------------------------------------------
/AsyncIPCClient/stdafx.cpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPCClient/stdafx.cpp
--------------------------------------------------------------------------------
/AsyncIPCClient/stdafx.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPCClient/stdafx.h
--------------------------------------------------------------------------------
/AsyncIPCClient/targetver.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPCClient/targetver.h
--------------------------------------------------------------------------------
/AsyncIPCServer/AsyncIPCServer.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "resource.h"
4 |
--------------------------------------------------------------------------------
/AsyncIPCServer/AsyncIPCServer.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPCServer/AsyncIPCServer.ico
--------------------------------------------------------------------------------
/AsyncIPCServer/AsyncIPCServer.rc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPCServer/AsyncIPCServer.rc
--------------------------------------------------------------------------------
/AsyncIPCServer/AsyncIPCServer.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 |
14 | {276F0A89-E81C-4F85-A9CA-D7DA52BBD602}
15 | Win32Proj
16 | AsyncIPCServer
17 |
18 |
19 |
20 | Application
21 | true
22 | v120
23 | Unicode
24 |
25 |
26 | Application
27 | false
28 | v120
29 | true
30 | Unicode
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | true
44 |
45 |
46 | false
47 |
48 |
49 |
50 | Use
51 | Level3
52 | Disabled
53 | WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)
54 | true
55 |
56 |
57 | Windows
58 | true
59 |
60 |
61 |
62 |
63 | Level3
64 | Use
65 | MaxSpeed
66 | true
67 | true
68 | WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
69 | true
70 |
71 |
72 | Windows
73 | true
74 | true
75 | true
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | Create
91 | Create
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/AsyncIPCServer/AsyncIPCServer.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | 头文件
23 |
24 |
25 | 头文件
26 |
27 |
28 | 头文件
29 |
30 |
31 | 头文件
32 |
33 |
34 |
35 |
36 | 源文件
37 |
38 |
39 | 源文件
40 |
41 |
42 |
43 |
44 | 资源文件
45 |
46 |
47 |
48 |
49 | 资源文件
50 |
51 |
52 | 资源文件
53 |
54 |
55 |
--------------------------------------------------------------------------------
/AsyncIPCServer/ReadMe.txt:
--------------------------------------------------------------------------------
1 | ========================================================================
2 | WIN32 应用程序:AsyncIPCServer 项目概述
3 | ========================================================================
4 |
5 | 应用程序向导已为您创建了此 AsyncIPCServer 应用程序。
6 |
7 | 本文件概要介绍组成 AsyncIPCServer 应用程序的每个文件的内容。
8 |
9 |
10 | AsyncIPCServer.vcxproj
11 | 这是使用应用程序向导生成的 VC++ 项目的主项目文件,其中包含生成该文件的 Visual C++ 的版本信息,以及有关使用应用程序向导选择的平台、配置和项目功能的信息。
12 |
13 | AsyncIPCServer.vcxproj.filters
14 | 这是使用“应用程序向导”生成的 VC++ 项目筛选器文件。它包含有关项目文件与筛选器之间的关联信息。在 IDE 中,通过这种关联,在特定节点下以分组形式显示具有相似扩展名的文件。例如,“.cpp”文件与“源文件”筛选器关联。
15 |
16 | AsyncIPCServer.cpp
17 | 这是主应用程序源文件。
18 |
19 | /////////////////////////////////////////////////////////////////////////////
20 | 应用程序向导创建了下列资源:
21 |
22 | AsyncIPCServer.rc
23 | 这是程序使用的所有 Microsoft Windows 资源的列表。它包括 RES 子目录中存储的图标、位图和光标。此文件可以直接在 Microsoft Visual C++ 中进行编辑。
24 |
25 | Resource.h
26 | 这是标准头文件,可用于定义新的资源 ID。Microsoft Visual C++ 将读取并更新此文件。
27 |
28 | AsyncIPCServer.ico
29 | 这是用作应用程序图标 (32x32) 的图标文件。此图标包括在主资源文件 AsyncIPCServer.rc 中。
30 |
31 | small.ico
32 | 这是一个图标文件,其中包含应用程序的图标的较小版本 (16x16)。此图标包括在主资源文件 AsyncIPCServer.rc 中。
33 |
34 | /////////////////////////////////////////////////////////////////////////////
35 | 其他标准文件:
36 |
37 | StdAfx.h, StdAfx.cpp
38 | 这些文件用于生成名为 AsyncIPCServer.pch 的预编译头 (PCH) 文件和名为 StdAfx.obj 的预编译类型文件。
39 |
40 | /////////////////////////////////////////////////////////////////////////////
41 | 其他注释:
42 |
43 | 应用程序向导使用“TODO:”注释来指示应添加或自定义的源代码部分。
44 |
45 | /////////////////////////////////////////////////////////////////////////////
46 |
--------------------------------------------------------------------------------
/AsyncIPCServer/Resource.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPCServer/Resource.h
--------------------------------------------------------------------------------
/AsyncIPCServer/main.cpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPCServer/main.cpp
--------------------------------------------------------------------------------
/AsyncIPCServer/small.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPCServer/small.ico
--------------------------------------------------------------------------------
/AsyncIPCServer/stdafx.cpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPCServer/stdafx.cpp
--------------------------------------------------------------------------------
/AsyncIPCServer/stdafx.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPCServer/stdafx.h
--------------------------------------------------------------------------------
/AsyncIPCServer/targetver.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/AsyncIPCServer/targetver.h
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Jelin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 | ///////////////////////////////////////////////////////////////////////////////////
24 | easyloggingpp license: https://github.com/amrayn/easyloggingpp/blob/master/LICENSE
25 |
26 | The MIT License (MIT)
27 |
28 | Copyright (c) 2012-2018 Amrayn Web Services
29 | Copyright (c) 2012-2018 @abumusamq
30 |
31 | https://github.com/amrayn/
32 | https://amrayn.com
33 | https://muflihun.com
34 |
35 | Permission is hereby granted, free of charge, to any person obtaining a copy of
36 | this software and associated documentation files (the "Software"), to deal in
37 | the Software without restriction, including without limitation the rights to
38 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
39 | the Software, and to permit persons to whom the Software is furnished to do so,
40 | subject to the following conditions:
41 |
42 | The above copyright notice and this permission notice shall be included in all
43 | copies or substantial portions of the Software.
44 |
45 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
46 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
47 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
48 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
49 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
50 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AsyncIPC
2 | Windows上高性能异步IPC实现(基于命名管道),支持异步双向通信,接口简洁使用简单。
3 | A high-performance asynchronous IPC implementation on Windows (based on named pipes),Support asynchronous two-way communication, the interface is concise and easy to use.
4 |
5 | ## 实现介绍
6 | 异步IPC框架提供2套实现方案,都是基于Windows系统的能力,分别是异步过程调用和重叠I/O。
7 |
8 | ### 1.异步过程调用(Asynchronous Procedure Calls)
9 | MSDN:https://docs.microsoft.com/zh-cn/windows/win32/sync/asynchronous-procedure-calls
10 |
11 | 基于异步过程调用实现的命名管道:https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipe-server-using-completion-routines
12 |
13 | ### 2.重叠I/O
14 | 基于重叠I/O实现的命名管道:https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipe-server-using-overlapped-i-o
15 |
16 | ## 接口介绍
17 | ### 1.导出接口
18 | 全部以C接口形式导出,便于其他语言调用。
19 | ```C++
20 | // 创建IPC实例
21 | ASYNCIPC_API int CreateInstance(PipeImplType name, INamedPipe** pipe);
22 | // 初始化日志库
23 | ASYNCIPC_API void InitEasyLog(const char* process_name);
24 | ```
25 | ### 2.IPC接口类
26 | 提供IPC创建、发送消息、退出等功能。
27 | ```C++
28 | class INamedPipe
29 | {
30 | public:
31 | virtual bool Create(const wchar_t* pipe_name, PipeType type, IPipeDelegate* delegate) = 0;
32 | virtual void Exit() = 0;
33 | virtual void Send(const char* msg) = 0;
34 | };
35 | ```
36 | ### 3.IPC回调接口类
37 | 将IPC内部状态信息回调到接入方,包括数据发送、消息接收、连接状态等信息。
38 | ```C++
39 | class IPipeDelegate {
40 | public:
41 | virtual void OnCreate(bool) = 0;
42 | virtual void OnConnected(bool) = 0;
43 | virtual void OnSend(int size) = 0;
44 | virtual void OnRecv(void* data, int size) = 0;
45 | virtual void OnDisconnected() = 0;
46 | };
47 | ```
48 | ## 使用介绍
49 | ### 1.server端
50 | 示例代码参考:AsyncIPCServer项目,实现在AsyncIPC\AsyncIPCServer\main.cpp中。
51 | ```c++
52 | #define ENABLE_ASYNCPIPE
53 | INamedPipe* pipe_server_ = nullptr;
54 | ...
55 | PipeCallback pipe_clllback;
56 | PipeImplType name;
57 | #ifdef ENABLE_ASYNCPIPE
58 | name = PIPE_ASYNC;
59 | #else
60 | name = PIPE_OVERLAPPED;
61 | #endif
62 | CreateInstance(name, &pipe_server_);
63 | pipe_server_->Create(kPipeName, PIPE_SERVER, &pipe_clllback);
64 | ...
65 | pipe_server_->Send(text);
66 | ...
67 | pipe_server_->Exit();
68 | ```
69 |
70 | ### 2.client端
71 | 示例代码参考:AsyncIPCClient项目,实现在AsyncIPC\AsyncIPCClient\main.cpp中。
72 | ```c++
73 | #define ENABLE_ASYNCPIPE
74 | INamedPipe* pipe_client_ = nullptr;
75 | PipeCallback pipe_clllback;
76 | PipeImplType name;
77 | #ifdef ENABLE_ASYNCPIPE
78 | name = PIPE_ASYNC;
79 | #else
80 | name = PIPE_OVERLAPPED;
81 | #endif
82 | CreateInstance(name, &pipe_client_);
83 | pipe_client_->Create(kPipeName, PIPE_CLIENT, &pipe_clllback);
84 |
85 | // send msg
86 | pipe_client_->Send(text);
87 | // exit
88 | pipe_client_->Exit();
89 | ```
90 | ## 测试截图
91 |
92 | 1.server和client建立连接
93 |
94 | 
95 |
96 | 2.server和client之间互相发送消息
97 |
98 | 
99 |
100 | 3.断开连接
101 |
102 | 
103 |
104 | 
--------------------------------------------------------------------------------
/img/connect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/img/connect.png
--------------------------------------------------------------------------------
/img/disconnect1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/img/disconnect1.png
--------------------------------------------------------------------------------
/img/disconnect2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/img/disconnect2.png
--------------------------------------------------------------------------------
/img/msg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JelinYao/AsyncIPC/a9f4709d5b735f47e3616a3204dad42f74209e97/img/msg.png
--------------------------------------------------------------------------------