├── .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 | ![](https://raw.githubusercontent.com/JelinYao/AsyncIPC/main/img/connect.png) 95 | 96 | 2.server和client之间互相发送消息 97 | 98 | ![](https://raw.githubusercontent.com/JelinYao/AsyncIPC/main/img/msg.png) 99 | 100 | 3.断开连接 101 | 102 | ![](https://raw.githubusercontent.com/JelinYao/AsyncIPC/main/img/disconnect1.png) 103 | 104 | ![](https://raw.githubusercontent.com/JelinYao/AsyncIPC/main/img/disconnect2.png) -------------------------------------------------------------------------------- /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 --------------------------------------------------------------------------------