Skip to content
Snippets Groups Projects
easylogging++.h 278 KiB
Newer Older
Radim Vavřík's avatar
Radim Vavřík committed
//
//  Easylogging++ v9.75
//  Single-header only, cross-platform logging library for C++ applications
//
//  Copyright (c) 2014 Majid Khan
//
//  This library is released under the MIT Licence.
//  http://www.easylogging.org/licence.php
//
//  support@easylogging.org
//  http://easylogging.org
//  https://github.com/easylogging/easyloggingpp
//
#ifndef EASYLOGGINGPP_H  // NOLINT
#define EASYLOGGINGPP_H
// Compilers and C++0x/C++11 Evaluation
#if defined(__GNUC__)
#   define _ELPP_COMPILER_GCC 1
#   define _ELPP_GCC_VERSION (__GNUC__ * 10000 \
                               + __GNUC_MINOR__ * 100 \
                               + __GNUC_PATCHLEVEL__)
#   if defined(__GXX_EXPERIMENTAL_CXX0X__)
#      define _ELPP_CXX0X 1
#   elif(_ELPP_GCC_VERSION >= 40801)
#      define _ELPP_CXX11 1
#   endif  // defined(__GXX_EXPERIMENTAL_CXX0X__)
#endif  // defined(__GNUC__)
// Visual C++
#if defined(_MSC_VER)
#   define _ELPP_COMPILER_MSVC 1
#   define _ELPP_CRT_DBG_WARNINGS 1
#   if (_MSC_VER == 1600)
#      define _ELPP_CXX0X 1
#   elif(_MSC_VER >= 1700)
#      define _ELPP_CXX11 1
#   endif  // (_MSC_VER == 1600)
#endif  // defined(_MSC_VER)
// Clang++
#if defined(__clang__) && (__clang__ == 1)
#   define _ELPP_COMPILER_CLANG 1
#   define _ELPP_CLANG_VERSION (__clang_major__ * 10000 \
                                + __clang_minor__ * 100 \
                                + __clang_patchlevel__)
#   if (_ELPP_CLANG_VERSION >= 30300)
#      define _ELPP_CXX11 1
#   endif  // (_ELPP_CLANG_VERSION >= 30300)
#endif  // defined(__clang__) && (__clang__ == 1)
// MinGW
#if defined(__MINGW32__) || defined(__MINGW64__)
#   define _ELPP_MINGW 1
#endif  // defined(__MINGW32__) || defined(__MINGW64__)
// Cygwin
#if defined(__CYGWIN__) && (__CYGWIN__ == 1)
#   define _ELPP_CYGWIN 1
#endif  // defined(__CYGWIN__) && (__CYGWIN__ == 1)
// Intel C++
#if defined(__INTEL_COMPILER)
#   define _ELPP_COMPILER_INTEL 1
#endif
// Operating System Evaluation
// Windows
#if defined(_WIN32) || defined(_WIN64)
#   define _ELPP_OS_WINDOWS 1
#endif  // defined(_WIN32) || defined(_WIN64)
// Linux
#if (defined(__linux) || defined(__linux__))
#   define _ELPP_OS_LINUX 1
#endif  // (defined(__linux) || defined(__linux__))
// Mac
#if defined(__APPLE__)
#   define _ELPP_OS_MAC 1
#endif  // defined(__APPLE__)
// FreeBSD
#if defined(__FreeBSD__)
#   define _ELPP_OS_FREEBSD 1
#endif
// Unix
#if ((_ELPP_OS_LINUX || _ELPP_OS_MAC || _ELPP_OS_FREEBSD) && (!_ELPP_OS_WINDOWS))
#   define _ELPP_OS_UNIX 1
#endif  // ((_ELPP_OS_LINUX || _ELPP_OS_MAC || _ELPP_OS_FREEBSD) && (!_ELPP_OS_WINDOWS))
// Android
#if defined(__ANDROID__)
#   define _ELPP_OS_ANDROID 1
#endif  // defined(__ANDROID__)
// Evaluating Cygwin as *nix OS
#if !_ELPP_OS_UNIX && !_ELPP_OS_WINDOWS && _ELPP_CYGWIN
#   undef _ELPP_OS_UNIX
#   undef _ELPP_OS_LINUX
#   define _ELPP_OS_UNIX 1
#   define _ELPP_OS_LINUX 1
#endif //  !_ELPP_OS_UNIX && !_ELPP_OS_WINDOWS && _ELPP_CYGWIN
#if !defined(_ELPP_INTERNAL_DEBUGGING_OUT_INFO)
#   define _ELPP_INTERNAL_DEBUGGING_OUT_INFO std::cout
#endif // !defined(_ELPP_INTERNAL_DEBUGGING_OUT)
#if !defined(_ELPP_INTERNAL_DEBUGGING_OUT_ERROR)
#   define _ELPP_INTERNAL_DEBUGGING_OUT_ERROR std::cerr
#endif // !defined(_ELPP_INTERNAL_DEBUGGING_OUT)
#if !defined(_ELPP_INTERNAL_DEBUGGING_ENDL)
#   define _ELPP_INTERNAL_DEBUGGING_ENDL std::endl
#endif // !defined(_ELPP_INTERNAL_DEBUGGING_OUT)
#if !defined(_ELPP_INTERNAL_DEBUGGING_MSG)
#   define _ELPP_INTERNAL_DEBUGGING_MSG(msg) msg
#endif // !defined(_ELPP_INTERNAL_DEBUGGING_OUT)
// Internal Assertions and errors
#if !defined(_ELPP_DISABLE_ASSERT)
#   if (defined(_ELPP_DEBUG_ASSERT_FAILURE))
#      define ELPP_ASSERT(expr, msg) if (!(expr)) { \
          std::stringstream internalInfoStream; internalInfoStream << msg; \
          _ELPP_INTERNAL_DEBUGGING_OUT_ERROR \
              << "EASYLOGGING++ ASSERTION FAILED (LINE: " << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" \
              << _ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" << _ELPP_INTERNAL_DEBUGGING_ENDL; base::utils::abort(1, \
                  "ELPP Assertion failure, please define _ELPP_DEBUG_ASSERT_FAILURE"); }
#   else
#      define ELPP_ASSERT(expr, msg) if (!(expr)) { \
          std::stringstream internalInfoStream; internalInfoStream << msg; \
          _ELPP_INTERNAL_DEBUGGING_OUT_ERROR\
             << "ASSERTION FAILURE FROM EASYLOGGING++ (LINE: " \
             << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" << _ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" \
             << _ELPP_INTERNAL_DEBUGGING_ENDL; }
#   endif  // (defined(_ELPP_DEBUG_ASSERT_FAILURE))
#else
#   define ELPP_ASSERT(x, y)
#endif  //(!defined(_ELPP_DISABLE_ASSERT)
#if _ELPP_COMPILER_MSVC
#   define _ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \
       { char buff[256]; strerror_s(buff, 256, errno); \
       _ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << buff << " [" << errno << "]";} (void)0
#else
#   define _ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \
        _ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << strerror(errno) << " [" << errno << "]"; (void)0
#endif  // _ELPP_COMPILER_MSVC
#if defined(_ELPP_DEBUG_ERRORS)
#   if !defined(ELPP_INTERNAL_ERROR)
#      define ELPP_INTERNAL_ERROR(msg, pe) { \
          std::stringstream internalInfoStream; internalInfoStream << "<ERROR> " << msg; \
          _ELPP_INTERNAL_DEBUGGING_OUT_ERROR \
          << "ERROR FROM EASYLOGGING++ (LINE: " << __LINE__ << ") " \
          << _ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << _ELPP_INTERNAL_DEBUGGING_ENDL; \
          if (pe) { _ELPP_INTERNAL_DEBUGGING_OUT_ERROR << "    "; _ELPP_INTERNAL_DEBUGGING_WRITE_PERROR; }} (void)0
#   endif
#else
#   undef ELPP_INTERNAL_INFO
#   define ELPP_INTERNAL_ERROR(msg, pe)
#endif  // defined(_ELPP_DEBUG_ERRORS)
#if (defined(_ELPP_DEBUG_INFO))
#   if !(defined(_ELPP_INTERNAL_INFO_LEVEL))
#      define _ELPP_INTERNAL_INFO_LEVEL 9
#   endif  // !(defined(_ELPP_INTERNAL_INFO_LEVEL))
#   if !defined(ELPP_INTERNAL_INFO)
#      define ELPP_INTERNAL_INFO(lvl, msg) { if (lvl <= _ELPP_INTERNAL_INFO_LEVEL) { \
          std::stringstream internalInfoStream; internalInfoStream << "<INFO> " << msg; \
          _ELPP_INTERNAL_DEBUGGING_OUT_INFO << _ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) \
             << _ELPP_INTERNAL_DEBUGGING_ENDL; }}
#   endif
#else
#   undef ELPP_INTERNAL_INFO
#   define ELPP_INTERNAL_INFO(lvl, msg)
#endif  // (defined(_ELPP_DEBUG_INFO))
#if defined(_ELPP_STACKTRACE_ON_CRASH)
#   if (_ELPP_COMPILER_GCC && !_ELPP_MINGW)
#      define _ELPP_STACKTRACE 1
#   else
#      if _ELPP_COMPILER_MSVC
#         pragma message("Stack trace not available for this compiler")
#      else
#         warning "Stack trace not available for this compiler";
#      endif  // _ELPP_COMPILER_MSVC
#   endif  // _ELPP_COMPILER_GCC
#endif  // (defined(_ELPP_STACKTRACE_ON_CRASH))
// Miscellaneous macros
#define _ELPP_UNUSED(x) (void)x
#if _ELPP_OS_UNIX
// Log file permissions for unix-based systems
#   define _ELPP_LOG_PERMS S_IRUSR | S_IWUSR | S_IXUSR | S_IWGRP | S_IRGRP | S_IXGRP | S_IWOTH | S_IXOTH
#endif  // _ELPP_OS_UNIX
#if defined(_ELPP_AS_DLL) && _ELPP_COMPILER_MSVC
#   if defined(_ELPP_EXPORT_SYMBOLS)
#      define _ELPP_EXPORT __declspec(dllexport)
#   else
#      define _ELPP_EXPORT __declspec(dllimport)
#   endif  // defined(_ELPP_EXPORT_SYMBOLS)
#else
#   define _ELPP_EXPORT
#endif  // defined(_ELPP_AS_DLL) && _ELPP_COMPILER_MSVC
// Some special functions that are VC++ specific
#undef STRTOK
#undef STRERROR
#undef STRCAT
#undef STRCPY
#if _ELPP_CRT_DBG_WARNINGS
#   define STRTOK(a, b, c) strtok_s(a, b, c)
#   define STRERROR(a, b, c) strerror_s(a, b, c)
#   define STRCAT(a, b, len) strcat_s(a, len, b)
#   define STRCPY(a, b, len) strcpy_s(a, len, b)
#else
#   define STRTOK(a, b, c) strtok(a, b)  // NOLINT
#   define STRERROR(a, b, c) strerror(c)
#   define STRCAT(a, b, len) strcat(a, b)  // NOLINT
#   define STRCPY(a, b, len) strcpy(a, b)  // NOLINT
#endif
Radim Vavřík's avatar
Radim Vavřík committed
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200
// Compiler specific support evaluations
#if (!_ELPP_MINGW && !_ELPP_COMPILER_CLANG) || defined(_ELPP_FORCE_USE_STD_THREAD)
#   define _ELPP_USE_STD_THREADING 1
#endif  // (!_ELPP_MINGW && !_ELPP_COMPILER_CLANG) || defined(_ELPP_FORCE_USE_STD_THREAD)
#undef ELPP_FINAL
#if _ELPP_COMPILER_INTEL || (_ELPP_GCC_VERSION < 40702)
#   define ELPP_FINAL
#else
#   define ELPP_FINAL final
#endif  // _ELPP_COMPILER_INTEL || (_ELPP_GCC_VERSION < 40702)
#if defined(_ELPP_THREAD_SAFE)
#   define _ELPP_THREADING_ENABLED 1
#endif  // defined(_ELPP_THREAD_SAFE)
// Function macro _ELPP_FUNC
#undef _ELPP_FUNC
#if _ELPP_COMPILER_MSVC  // Visual C++
#   define _ELPP_FUNC __FUNCSIG__
#elif _ELPP_COMPILER_GCC  // GCC
#   define _ELPP_FUNC __PRETTY_FUNCTION__
#elif _ELPP_COMPILER_INTEL  // Intel C++
#   define _ELPP_FUNC __PRETTY_FUNCTION__
#elif _ELPP_COMPILER_CLANG  // Clang++
#   define _ELPP_FUNC __PRETTY_FUNCTION__
#else
#   if defined(__func__)
#      define _ELPP_FUNC __func__
#   else
#      define _ELPP_FUNC ""
#   endif  // defined(__func__)
#endif  // defined(_MSC_VER)
#undef _ELPP_VARIADIC_TEMPLATES_SUPPORTED
// Keep following line commented until features are fixed
#if _ELPP_COMPILER_GCC || _ELPP_COMPILER_CLANG || _ELPP_COMPILER_INTEL || (_ELPP_COMPILER_MSVC && _MSC_VER >= 1800)
#   define _ELPP_VARIADIC_TEMPLATES_SUPPORTED 1
#endif  // _ELPP_COMPILER_GCC || _ELPP_COMPILER_CLANG || _ELPP_COMPILER_INTEL || (_ELPP_COMPILER_MSVC && _MSC_VER >= 1800)
// Logging Enable/Disable macros
#if (!defined(_ELPP_DISABLE_LOGS))
#   define _ELPP_LOGGING_ENABLED 1
#endif  // (!defined(_ELPP_DISABLE_LOGS))
#if (!defined(_ELPP_DISABLE_DEBUG_LOGS) && (_ELPP_LOGGING_ENABLED) && ((defined(_DEBUG)) || (!defined(NDEBUG))))
#   define _ELPP_DEBUG_LOG 1
#else
#   define _ELPP_DEBUG_LOG 0
#endif  // (!defined(_ELPP_DISABLE_DEBUG_LOGS) && (_ELPP_LOGGING_ENABLED) && ((defined(_DEBUG)) || (!defined(NDEBUG))))
#if (!defined(_ELPP_DISABLE_INFO_LOGS) && (_ELPP_LOGGING_ENABLED))
#   define _ELPP_INFO_LOG 1
#else
#   define _ELPP_INFO_LOG 0
#endif  // (!defined(_ELPP_DISABLE_INFO_LOGS) && (_ELPP_LOGGING_ENABLED))
#if (!defined(_ELPP_DISABLE_WARNING_LOGS) && (_ELPP_LOGGING_ENABLED))
#   define _ELPP_WARNING_LOG 1
#else
#   define _ELPP_WARNING_LOG 0
#endif  // (!defined(_ELPP_DISABLE_WARNING_LOGS) && (_ELPP_LOGGING_ENABLED))
#if (!defined(_ELPP_DISABLE_ERROR_LOGS) && (_ELPP_LOGGING_ENABLED))
#   define _ELPP_ERROR_LOG 1
#else
#   define _ELPP_ERROR_LOG 0
#endif  // (!defined(_ELPP_DISABLE_ERROR_LOGS) && (_ELPP_LOGGING_ENABLED))
#if (!defined(_ELPP_DISABLE_FATAL_LOGS) && (_ELPP_LOGGING_ENABLED))
#   define _ELPP_FATAL_LOG 1
#else
#   define _ELPP_FATAL_LOG 0
#endif  // (!defined(_ELPP_DISABLE_FATAL_LOGS) && (_ELPP_LOGGING_ENABLED))
#if (!defined(_ELPP_DISABLE_TRACE_LOGS) && (_ELPP_LOGGING_ENABLED))
#   define _ELPP_TRACE_LOG 1
#else
#   define _ELPP_TRACE_LOG 0
#endif  // (!defined(_ELPP_DISABLE_TRACE_LOGS) && (_ELPP_LOGGING_ENABLED))
#if (!defined(_ELPP_DISABLE_VERBOSE_LOGS) && (_ELPP_LOGGING_ENABLED))
#   define _ELPP_VERBOSE_LOG 1
#else
#   define _ELPP_VERBOSE_LOG 0
#endif  // (!defined(_ELPP_DISABLE_VERBOSE_LOGS) && (_ELPP_LOGGING_ENABLED))
#if (!(_ELPP_CXX0X || _ELPP_CXX11))
#   error "Easylogging++ 9.0+ is only compatible with C++0x (or higher) compliant compiler"
#endif  // (!(_ELPP_CXX0X || _ELPP_CXX11))
// Headers
#if defined(_ELPP_SYSLOG)
#   include <syslog.h>
#endif  // defined(_ELPP_SYSLOG)
#include <ctime>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cwchar>
#include <csignal>
#include <cerrno>
#include <cstdarg>
#if defined(_ELPP_UNICODE)
#   include <locale>
#endif  // defined(_ELPP_UNICODE)
#if _ELPP_STACKTRACE
#   include <cxxabi.h>
#   include <execinfo.h>
#endif  // _ELPP_STACKTRACE
#if _ELPP_OS_ANDROID
#   include <sys/system_properties.h>
#endif  // _ELPP_OS_ANDROID
#if _ELPP_OS_UNIX
#   include <sys/stat.h>
#   include <sys/time.h>
#elif _ELPP_OS_WINDOWS
#   include <direct.h>
#   include <Windows.h>
#   if defined(WIN32_LEAN_AND_MEAN)
#      include <winsock.h>
#   endif // defined(WIN32_LEAN_AND_MEAN)
#endif  // _ELPP_OS_UNIX
#include <string>
#include <vector>
#include <map>
#include <utility>
#include <functional>
#include <algorithm>
#include <fstream>  // NOLINT
#include <iostream>  // NOLINT
#include <sstream>
#include <memory>
#include <type_traits>
#if _ELPP_THREADING_ENABLED
#   if _ELPP_USE_STD_THREADING
#      include <mutex>
#      include <thread>
#   else
#      if _ELPP_OS_UNIX
#         include <pthread.h>
#      endif  // _ELPP_OS_UNIX
#   endif  // _ELPP_USE_STD_THREADING
#endif  // _ELPP_THREADING_ENABLED
#if defined(_ELPP_STL_LOGGING)
// For logging STL based templates
#   include <list>
#   include <queue>
#   include <deque>
#   include <set>
#   include <bitset>
#   include <stack>
#   if defined(_ELPP_LOG_STD_ARRAY)
#      include <array>
#   endif  // defined(_ELPP_LOG_STD_ARRAY)
#   if defined(_ELPP_LOG_UNORDERED_MAP)
#      include <unordered_map>
#   endif  // defined(_ELPP_LOG_UNORDERED_MAP)
#   if defined(_ELPP_LOG_UNORDERED_SET)
#      include <unordered_set>
#   endif  // defined(_ELPP_UNORDERED_SET)
#endif  // defined(_ELPP_STL_LOGGING)
#if defined(_ELPP_QT_LOGGING)
// For logging Qt based classes & templates
#   include <QString>
#   include <QByteArray>
#   include <QVector>
#   include <QList>
#   include <QPair>
#   include <QMap>
#   include <QQueue>
#   include <QSet>
#   include <QLinkedList>
#   include <QHash>
#   include <QMultiHash>
#   include <QStack>
#endif  // defined(_ELPP_QT_LOGGING)
#if defined(_ELPP_BOOST_LOGGING)
// For logging boost based classes & templates
#   include <boost/container/vector.hpp>
#   include <boost/container/stable_vector.hpp>
#   include <boost/container/list.hpp>
#   include <boost/container/deque.hpp>
#   include <boost/container/map.hpp>
#   include <boost/container/flat_map.hpp>
#   include <boost/container/set.hpp>
#   include <boost/container/flat_set.hpp>
#endif  // defined(_ELPP_BOOST_LOGGING)
#if defined(_ELPP_WXWIDGETS_LOGGING)
// For logging wxWidgets based classes & templates
#   include <wx/vector.h>
#endif  // defined(_ELPP_WXWIDGETS_LOGGING)
// Forward declarations
namespace el {
class Logger;
class LogMessage;
class PerformanceTrackingData;
class Loggers;
class Helpers;
template <typename T> class Callback;
class LogDispatchCallback;
class PerformanceTrackingCallback;
class LogDispatchData;
namespace base {
class Storage;
class RegisteredLoggers;
class PerformanceTracker;
class MessageBuilder;
class Writer;
class PErrorWriter;
class LogDispatcher;
class DefaultLogBuilder;
class DefaultLogDispatchCallback;
class DefaultPerformanceTrackingCallback;
}  // namespace base
}  // namespace el
/// @brief Easylogging++ entry namespace
namespace el {
/// @brief Namespace containing base/internal functionality used by Easylogging++
namespace base {
/// @brief Data types used by Easylogging++
namespace type {
#undef ELPP_LITERAL
#undef ELPP_STRLEN
#undef ELPP_COUT
#if defined(_ELPP_UNICODE)
#   define ELPP_LITERAL(txt) L##txt
#   define ELPP_STRLEN wcslen
#   if defined ELPP_CUSTOM_COUT
#      define ELPP_COUT ELPP_CUSTOM_COUT
#   else
#      define ELPP_COUT std::wcout
#   endif  // defined ELPP_CUSTOM_COUT
typedef wchar_t char_t;
typedef std::wstring string_t;
typedef std::wstringstream stringstream_t;
typedef std::wfstream fstream_t;
typedef std::wostream ostream_t;
#else
#   define ELPP_LITERAL(txt) txt
#   define ELPP_STRLEN strlen
#   if defined ELPP_CUSTOM_COUT
#      define ELPP_COUT ELPP_CUSTOM_COUT
#   else
#      define ELPP_COUT std::cout
#   endif  // defined ELPP_CUSTOM_COUT
typedef char char_t;
typedef std::string string_t;
typedef std::stringstream stringstream_t;
typedef std::fstream fstream_t;
typedef std::ostream ostream_t;
#endif  // defined(_ELPP_UNICODE)
#if defined(ELPP_CUSTOM_COUT_LINE)
#   define ELPP_COUT_LINE(logLine) ELPP_CUSTOM_COUT_LINE(logLine)
#else
#   define ELPP_COUT_LINE(logLine) logLine << std::flush
#endif // defined(ELPP_CUSTOM_COUT_LINE)
typedef unsigned short EnumType;  // NOLINT
typedef std::shared_ptr<base::Storage> StoragePointer;
typedef int VerboseLevel;
typedef std::shared_ptr<LogDispatchCallback> LogDispatchCallbackPtr;
typedef std::shared_ptr<PerformanceTrackingCallback> PerformanceTrackingCallbackPtr;
}  // namespace type
/// @brief Internal helper class that prevent copy constructor for class
///
/// @detail When using this class simply inherit it privately
class NoCopy {
protected:
    NoCopy(void) {}
private:
    NoCopy(const NoCopy&);
    NoCopy& operator=(const NoCopy&);
};
/// @brief Internal helper class that makes all default constructors private.
///
/// @detail This prevents initializing class making it static unless an explicit constructor is declared.
/// When using this class simply inherit it privately
class StaticClass {
private:
    StaticClass(void);
    StaticClass(const StaticClass&);
    StaticClass& operator=(const StaticClass&);
};
}  // namespace base
/// @brief Represents enumeration for severity level used to determine level of logging
///
/// @detail With Easylogging++, developers may disable or enable any level regardless of
/// what the severity is. Or they can choose to log using hierarchical logging flag
enum class Level : base::type::EnumType {
        /// @brief Generic level that represents all the levels. Useful when setting global configuration for all levels
        Global = 1,
        /// @brief Information that can be useful to back-trace certain events - mostly useful than debug logs.
        Trace = 2,
        /// @brief Informational events most useful for developers to debug application
        Debug = 4,
        /// @brief Severe error information that will presumably abort application
        Fatal = 8, 
        /// @brief Information representing errors in application but application will keep running
        Error = 16,
        /// @brief Useful when application has potentially harmful situtaions
        Warning = 32, 
        /// @brief Information that can be highly useful and vary with verbose logging level.
        Verbose = 64,
        /// @brief Mainly useful to represent current progress of application
        Info = 128, 
        /// @brief Represents unknown level
        Unknown = 1010
};
/// @brief Static class that contains helper functions for el::Level
class LevelHelper : base::StaticClass {
public:
    /// @brief Represents minimum valid level. Useful when iterating through enum.
    static const base::type::EnumType kMinValid = static_cast<base::type::EnumType>(Level::Trace);
    /// @brief Represents maximum valid level. This is used internally and you should not need it.
    static const base::type::EnumType kMaxValid = static_cast<base::type::EnumType>(Level::Info);
    /// @brief Casts level to int, useful for iterating through enum.
    static base::type::EnumType castToInt(Level level) {
        return static_cast<base::type::EnumType>(level);
    }
    /// @brief Casts int(ushort) to level, useful for iterating through enum.
    static Level castFromInt(base::type::EnumType l) {
        return static_cast<Level>(l);
    }
    /// @brief Converts level to associated const char*
    /// @return Upper case string based level.
    static const char* convertToString(Level level) {
       // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet.
        if (level == Level::Global) return "GLOBAL";
        if (level == Level::Debug) return "DEBUG";
        if (level == Level::Info) return "INFO";
        if (level == Level::Warning) return "WARNING";
        if (level == Level::Error) return "ERROR";
        if (level == Level::Fatal) return "FATAL";
        if (level == Level::Verbose) return "VERBOSE";
        if (level == Level::Trace) return "TRACE";
        return "UNKNOWN";
    }
    /// @brief Converts from levelStr to Level
    /// @param levelStr Upper case string based level.
    ///        Lower case is also valid but providing upper case is recommended.
    static Level convertFromString(const char* levelStr) {
        if ((strcmp(levelStr, "GLOBAL") == 0) || (strcmp(levelStr, "global") == 0))
            return Level::Global;
        if ((strcmp(levelStr, "DEBUG") == 0) || (strcmp(levelStr, "debug") == 0))
            return Level::Debug;
        if ((strcmp(levelStr, "INFO") == 0) || (strcmp(levelStr, "info") == 0))
            return Level::Info;
        if ((strcmp(levelStr, "WARNING") == 0) || (strcmp(levelStr, "warning") == 0))
            return Level::Warning;
        if ((strcmp(levelStr, "ERROR") == 0) || (strcmp(levelStr, "error") == 0))
            return Level::Error;
        if ((strcmp(levelStr, "FATAL") == 0) || (strcmp(levelStr, "fatal") == 0))
            return Level::Fatal;
        if ((strcmp(levelStr, "VERBOSE") == 0) || (strcmp(levelStr, "verbose") == 0))
            return Level::Verbose;
        if ((strcmp(levelStr, "TRACE") == 0) || (strcmp(levelStr, "trace") == 0))
            return Level::Trace;
        return Level::Unknown;
    }
    /// @brief Applies specified function to each level starting from startIndex
    /// @param startIndex initial value to start the iteration from. This is passed as pointer and 
    ///        is left-shifted so this can be used inside function (fn) to represent current level.
    /// @param fn function to apply with each level. This bool represent whether or not to stop iterating through levels.
    static inline void forEachLevel(base::type::EnumType* startIndex, const std::function<bool(void)>& fn) {
        base::type::EnumType lIndexMax = LevelHelper::kMaxValid;
        do {
            if (fn()) {
                break;
            }
            *startIndex = static_cast<base::type::EnumType>(*startIndex << 1);
        } while (*startIndex <= lIndexMax);
    }
};
/// @brief Represents enumeration of ConfigurationType used to configure or access certain aspect
/// of logging
enum class ConfigurationType : base::type::EnumType {
   /// @brief Determines whether or not corresponding level and logger of logging is enabled
   /// You may disable all logs by using el::Level::Global
    Enabled = 1,
   /// @brief Whether or not to write corresponding log to log file
    ToFile = 2,
   /// @brief Whether or not to write corresponding level and logger log to standard output.
   /// By standard output meaning termnal, command prompt etc
    ToStandardOutput = 4,
   /// @brief Determines format of logging corresponding level and logger.
    Format = 8,
   /// @brief Determines log file (full path) to write logs to for correponding level and logger
    Filename = 16,
   /// @brief Specifies milliseconds width. Width can be within range (1-6)
    MillisecondsWidth = 32,
   /// @brief Determines whether or not performance tracking is enabled.
   ///
   /// @detail This does not depend on logger or level. Performance tracking always uses 'performance' logger
    PerformanceTracking = 64,
   /// @brief Specifies log file max size.
   ///
   /// @detail If file size of corresponding log file (for corresponding level) is >= specified size, log file will 
   /// be truncated and re-initiated.
    MaxLogFileSize = 128,
   /// @brief Specifies number of log entries to hold until we flush pending log data
    LogFlushThreshold = 256,
   /// @brief Represents unknown configuration
    Unknown = 1010
};
/// @brief Static class that contains helper functions for el::ConfigurationType
class ConfigurationTypeHelper : base::StaticClass {
public:
    /// @brief Represents minimum valid configuration type. Useful when iterating through enum.
    static const base::type::EnumType kMinValid = static_cast<base::type::EnumType>(ConfigurationType::Enabled);
    /// @brief Represents maximum valid configuration type. This is used internally and you should not need it.
    static const base::type::EnumType kMaxValid = static_cast<base::type::EnumType>(ConfigurationType::MaxLogFileSize);
    /// @brief Casts configuration type to int, useful for iterating through enum.
    static base::type::EnumType castToInt(ConfigurationType configurationType) {
        return static_cast<base::type::EnumType>(configurationType);
    }
    /// @brief Casts int(ushort) to configurationt type, useful for iterating through enum.
    static ConfigurationType castFromInt(base::type::EnumType c) {
        return static_cast<ConfigurationType>(c);
    }
    /// @brief Converts configuration type to associated const char*
    /// @returns Upper case string based configuration type.
    static const char* convertToString(ConfigurationType configurationType) {
        // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet.
        if (configurationType == ConfigurationType::Enabled) return "ENABLED";
        if (configurationType == ConfigurationType::Filename) return "FILENAME";
        if (configurationType == ConfigurationType::Format) return "FORMAT";
        if (configurationType == ConfigurationType::ToFile) return "TO_FILE";
        if (configurationType == ConfigurationType::ToStandardOutput) return "TO_STANDARD_OUTPUT";
        if (configurationType == ConfigurationType::MillisecondsWidth) return "MILLISECONDS_WIDTH";
        if (configurationType == ConfigurationType::PerformanceTracking) return "PERFORMANCE_TRACKING";
        if (configurationType == ConfigurationType::MaxLogFileSize) return "MAX_LOG_FILE_SIZE";
        if (configurationType == ConfigurationType::LogFlushThreshold) return "LOG_FLUSH_THRESHOLD";
        return "UNKNOWN";
    }
    /// @brief Converts from configStr to ConfigurationType
    /// @param configStr Upper case string based configuration type.
    ///        Lower case is also valid but providing upper case is recommended.
    static ConfigurationType convertFromString(const char* configStr) {
        if ((strcmp(configStr, "ENABLED") == 0) || (strcmp(configStr, "enabled") == 0))
            return ConfigurationType::Enabled;
        if ((strcmp(configStr, "TO_FILE") == 0) || (strcmp(configStr, "to_file") == 0))
            return ConfigurationType::ToFile;
        if ((strcmp(configStr, "TO_STANDARD_OUTPUT") == 0) || (strcmp(configStr, "to_standard_output") == 0))
            return ConfigurationType::ToStandardOutput;
        if ((strcmp(configStr, "FORMAT") == 0) || (strcmp(configStr, "format") == 0))
            return ConfigurationType::Format;
        if ((strcmp(configStr, "FILENAME") == 0) || (strcmp(configStr, "filename") == 0))
            return ConfigurationType::Filename;
        if ((strcmp(configStr, "MILLISECONDS_WIDTH") == 0) || (strcmp(configStr, "milliseconds_width") == 0))
            return ConfigurationType::MillisecondsWidth;
        if ((strcmp(configStr, "PERFORMANCE_TRACKING") == 0) || (strcmp(configStr, "performance_tracking") == 0))
            return ConfigurationType::PerformanceTracking;
        if ((strcmp(configStr, "MAX_LOG_FILE_SIZE") == 0) || (strcmp(configStr, "max_log_file_size") == 0))
            return ConfigurationType::MaxLogFileSize;
        if ((strcmp(configStr, "LOG_FLUSH_THRESHOLD") == 0) || (strcmp(configStr, "log_flush_threshold") == 0))
            return ConfigurationType::LogFlushThreshold;
        return ConfigurationType::Unknown;
    }
    /// @brief Applies specified function to each configuration type starting from startIndex
    /// @param startIndex initial value to start the iteration from. This is passed by pointer and is left-shifted
    ///        so this can be used inside function (fn) to represent current configuration type.
    /// @param fn function to apply with each configuration type. 
    ///        This bool represent whether or not to stop iterating through configurations.
    static inline void forEachConfigType(base::type::EnumType* startIndex, const std::function<bool(void)>& fn) {
        base::type::EnumType cIndexMax = ConfigurationTypeHelper::kMaxValid;
        do {
            if (fn()) {
                break;
            }
            *startIndex = static_cast<base::type::EnumType>(*startIndex << 1);
        } while (*startIndex <= cIndexMax);
    }
};
/// @brief Flags used while writing logs. This flags are set by user
enum class LoggingFlag : base::type::EnumType {
    /// @brief Makes sure we have new line for each container log entry
    NewLineForContainer = 1,
    /// @brief Makes sure if -vmodule is used and does not specifies a module, then verbose
    /// logging is allowed via that module.
    AllowVerboseIfModuleNotSpecified = 2,
    /// @brief When handling crashes by default, detailed crash reason will be logged as well
    LogDetailedCrashReason = 4,
    /// @brief Allows to disable application abortion when logged using FATAL level
    DisableApplicationAbortOnFatalLog = 8,
    /// @brief Flushes log with every log-entry (performance sensative) - Disabled by default
    ImmediateFlush = 16,
    /// @brief Enables strict file rolling
    StrictLogFileSizeCheck = 32,
    /// @brief Make terminal output colorful for supported terminals
    ColoredTerminalOutput = 64,
    /// @brief Supports use of multiple logging in same macro, e.g, CLOG(INFO, "default", "network")
    MultiLoggerSupport = 128,
    /// @brief Disables comparing performance tracker's checkpoints
    DisablePerformanceTrackingCheckpointComparison = 256,
    /// @brief Disable VModules
    DisableVModules = 512,
    /// @brief Disable VModules extensions
    DisableVModulesExtensions = 1024,
    /// @brief Enables hierarchical logging
    HierarchicalLogging = 2048,
    /// @brief Creates logger automatically when not available
    CreateLoggerAutomatically = 4096,
    /// @brief Adds spaces b/w logs that separated by left-shift operator
    AutoSpacing = 8192
};
namespace base {
/// @brief Namespace containing constants used internally.
namespace consts {
    // Level log values - These are values that are replaced in place of %level format specifier
    static const base::type::char_t* kInfoLevelLogValue     =   ELPP_LITERAL("INFO ");
    static const base::type::char_t* kDebugLevelLogValue    =   ELPP_LITERAL("DEBUG");
    static const base::type::char_t* kWarningLevelLogValue  =   ELPP_LITERAL("WARN ");
    static const base::type::char_t* kErrorLevelLogValue    =   ELPP_LITERAL("ERROR");
    static const base::type::char_t* kFatalLevelLogValue    =   ELPP_LITERAL("FATAL");
    static const base::type::char_t* kVerboseLevelLogValue  =   ELPP_LITERAL("VER");
    static const base::type::char_t* kTraceLevelLogValue    =   ELPP_LITERAL("TRACE");
    static const base::type::char_t* kInfoLevelShortLogValue     =   ELPP_LITERAL("I");
    static const base::type::char_t* kDebugLevelShortLogValue    =   ELPP_LITERAL("D");
    static const base::type::char_t* kWarningLevelShortLogValue  =   ELPP_LITERAL("W");
    static const base::type::char_t* kErrorLevelShortLogValue    =   ELPP_LITERAL("E");
    static const base::type::char_t* kFatalLevelShortLogValue    =   ELPP_LITERAL("F");
    static const base::type::char_t* kVerboseLevelShortLogValue  =   ELPP_LITERAL("V");
    static const base::type::char_t* kTraceLevelShortLogValue    =   ELPP_LITERAL("T");
    // Format specifiers - These are used to define log format
    static const base::type::char_t* kAppNameFormatSpecifier          =      ELPP_LITERAL("%app");
    static const base::type::char_t* kLoggerIdFormatSpecifier         =      ELPP_LITERAL("%logger");
    static const base::type::char_t* kThreadIdFormatSpecifier         =      ELPP_LITERAL("%thread");
    static const base::type::char_t* kSeverityLevelFormatSpecifier    =      ELPP_LITERAL("%level");
    static const base::type::char_t* kSeverityLevelShortFormatSpecifier    =      ELPP_LITERAL("%levshort");
    static const base::type::char_t* kDateTimeFormatSpecifier         =      ELPP_LITERAL("%datetime");
    static const base::type::char_t* kLogFileFormatSpecifier          =      ELPP_LITERAL("%file");
    static const base::type::char_t* kLogFileBaseFormatSpecifier      =      ELPP_LITERAL("%fbase");
    static const base::type::char_t* kLogLineFormatSpecifier          =      ELPP_LITERAL("%line");
    static const base::type::char_t* kLogLocationFormatSpecifier      =      ELPP_LITERAL("%loc");
    static const base::type::char_t* kLogFunctionFormatSpecifier      =      ELPP_LITERAL("%func");
    static const base::type::char_t* kCurrentUserFormatSpecifier      =      ELPP_LITERAL("%user");
    static const base::type::char_t* kCurrentHostFormatSpecifier      =      ELPP_LITERAL("%host");
    static const base::type::char_t* kMessageFormatSpecifier          =      ELPP_LITERAL("%msg");
    static const base::type::char_t* kVerboseLevelFormatSpecifier     =      ELPP_LITERAL("%vlevel");
    static const char* kDateTimeFormatSpecifierForFilename            =      "%datetime";
    // Date/time
    static const char* kDays[7]                         =      { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
    static const char* kDaysAbbrev[7]                   =      { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
    static const char* kMonths[12]                      =      { "January", "February", "March", "Apri", "May", "June", "July", "August",
            "September", "October", "November", "December" };
    static const char* kMonthsAbbrev[12]                =      { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
    static const char* kDefaultDateTimeFormat           =      "%d/%M/%Y %H:%m:%s,%g";
    static const char* kDefaultDateTimeFormatInFilename =      "%d-%M-%Y_%H-%m";
    static const int kYearBase                          =      1900;
    static const char* kAm                              =      "AM";
    static const char* kPm                              =      "PM";
    // Miscellaneous constants
    static const char* kDefaultLoggerId                        =      "default";
    static const char* kPerformanceLoggerId                    =      "performance";
    static const char* kSysLogLoggerId                         =      "syslog";
    static const char* kNullPointer                            =      "nullptr";
    static const char  kFormatSpecifierChar                    =      '%';
#if _ELPP_VARIADIC_TEMPLATES_SUPPORTED
    static const char  kFormatSpecifierCharValue               =      'v';
#endif  // _ELPP_VARIADIC_TEMPLATES_SUPPORTED
    static const unsigned int kMaxLogPerContainer              =      100;
    static const unsigned int kMaxLogPerCounter                =      100000;
    static const unsigned int  kDefaultMillisecondsWidth       =      3;
    static const base::type::VerboseLevel kMaxVerboseLevel     =      9;
    static const char* kUnknownUser                            =      "user";
    static const char* kUnknownHost                            =      "unknown-host";
#if defined(_ELPP_DEFAULT_LOG_FILE)
    static const char* kDefaultLogFile                         =      _ELPP_DEFAULT_LOG_FILE;
#else
#   if _ELPP_OS_UNIX
#      if _ELPP_OS_ANDROID
    static const char* kDefaultLogFile                         =      "logs/myeasylog.log";
#      else
    static const char* kDefaultLogFile                         =      "logs/myeasylog.log";
#      endif  // _ELPP_OS_ANDROID
#   elif _ELPP_OS_WINDOWS
    static const char* kDefaultLogFile                         =      "logs\\myeasylog.log";
#   endif  // _ELPP_OS_UNIX
#endif  // defined(_ELPP_DEFAULT_LOG_FILE)
#if !defined(_ELPP_DISABLE_LOG_FILE_FROM_ARG)
    static const char* kDefaultLogFileParam                    =      "--default-log-file";
#endif  // !defined(_ELPP_DISABLE_LOG_FILE_FROM_ARG)
#if defined(_ELPP_LOGGING_FLAGS_FROM_ARG)
    static const char* kLoggingFlagsParam                      =      "--logging-flags";
#endif  // defined(_ELPP_LOGGING_FLAGS_FROM_ARG)
#if _ELPP_OS_WINDOWS
    static const char* kFilePathSeperator                      =      "\\";
#else
    static const char* kFilePathSeperator                      =      "/";
#endif  // _ELPP_OS_WINDOWS
    static const char* kValidLoggerIdSymbols                   =      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._";
    static const char* kConfigurationComment                   =      "##";
    static const char* kConfigurationLevel                     =      "*";
    static const char* kConfigurationLoggerId                  =      "--";
    static const std::size_t kSourceFilenameMaxLength          =      100;
    static const std::size_t kSourceLineMaxLength              =      10;
    static const Level kPerformanceTrackerDefaultLevel         =      Level::Info;
    const struct {
        double value;
        const base::type::char_t* unit;
    } kTimeFormats[] = {
       { 1000.0f, ELPP_LITERAL("mis") },
       { 1000.0f, ELPP_LITERAL("ms") },
       { 60.0f, ELPP_LITERAL("seconds") },
       { 60.0f, ELPP_LITERAL("minutes") },
       { 24.0f, ELPP_LITERAL("hours") },
       { 7.0f, ELPP_LITERAL("days") }
    };
    static const int kTimeFormatsCount                           =      sizeof(kTimeFormats) / sizeof(kTimeFormats[0]);
    const struct {
        int numb;
        const char* name;
        const char* brief;
        const char* detail;
    } kCrashSignals[] = {
        // NOTE: Do not re-order, if you do please check CrashHandler(bool) constructor and CrashHandler::setHandler(..)
        { SIGABRT, "SIGABRT", "Abnormal termination",
                "Program was abnormally terminated." },
        { SIGFPE, "SIGFPE", "Erroneous arithmetic operation",
                "Arithemetic operation issue such as division by zero or operation resulting in overflow." },
        { SIGILL, "SIGILL", "Illegal instruction",
                "Generally due to a corruption in the code or to an attempt to execute data."},
        { SIGSEGV, "SIGSEGV", "Invalid access to memory",
                "Program is trying to read an invalid (unallocated, deleted or corrupted) or inaccessible memory." },
        { SIGINT, "SIGINT", "Interactive attention signal",
                 "Interruption generated (generally) by user or operating system." },
    };
    static const int kCrashSignalsCount                          =      sizeof(kCrashSignals) / sizeof(kCrashSignals[0]);
}  // namespace consts
}  // namespace base
typedef std::function<void(const char*, std::size_t)> PreRollOutCallback;
namespace base {
static inline void defaultPreRollOutCallback(const char*, std::size_t) {}
/// @brief Enum to represent timestamp unit
enum class TimestampUnit : base::type::EnumType {
    Microsecond = 0, Millisecond = 1, Second = 2, Minute = 3, Hour = 4, Day = 5
};
/// @brief Format flags used to determine specifiers that are active for performance improvements.
enum class FormatFlags : base::type::EnumType {
    DateTime = 1<<1, LoggerId = 1<<2, File = 1<<3, Line = 1<<4, Location = 1<<5, Function = 1<<6,
    User = 1<<7, Host = 1<<8, LogMessage = 1<<9, VerboseLevel = 1<<10, AppName = 1<<11, ThreadId = 1<<12,
    Level = 1<<13, FileBase = 1<<14, LevelShort = 1<<15
};
/// @brief A milliseconds width class containing actual width and offset for date/time
class MillisecondsWidth {
public:
    MillisecondsWidth(void) { init(base::consts::kDefaultMillisecondsWidth); }
    explicit MillisecondsWidth(int width) { init(width); }
    bool operator==(const MillisecondsWidth& msWidth) { return m_width == msWidth.m_width && m_offset == msWidth.m_offset; }
    int m_width; unsigned int m_offset;
private:
    void init(int width) {
        if (width < 1 || width > 6) {
            width = base::consts::kDefaultMillisecondsWidth;
        }
        m_width = width;
        switch (m_width) {
        case 3: m_offset = 1000; break;
        case 4: m_offset = 100; break;
        case 5: m_offset = 10; break;
        case 6: m_offset = 1; break;
        default: m_offset = 1000; break;
        }
    }
};
/// @brief Namespace containing utility functions/static classes used internally
namespace utils {
/// @brief Deletes memory safely and points to null
template <typename T>
static inline
typename std::enable_if<std::is_pointer<T*>::value, void>::type
safeDelete(T*& pointer) {  // NOLINT
    if (pointer == nullptr)
        return;
    delete pointer;
    pointer = nullptr;
}
/// @brief Gets value of const char* but if it is nullptr, a string nullptr is returned
static inline const char* charPtrVal(const char* pointer) {
    return pointer == nullptr ? base::consts::kNullPointer : pointer;
}
/// @brief Aborts application due with user-defined status
static inline void abort(int status, const std::string& reason = std::string()) {
    // Both status and reason params are there for debugging with tools like gdb etc
    _ELPP_UNUSED(status);
    _ELPP_UNUSED(reason);
#if defined(_ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG)
   // Ignore msvc critical error dialog - break instead (on debug mode)
    _asm int 3
#else
    ::abort();
#endif  // defined(_ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG)
}
/// @brief Bitwise operations for C++11 strong enum class. This casts e into Flag_T and returns value after bitwise operation
/// Use these function as <pre>flag = bitwise::Or<MyEnum>(MyEnum::val1, flag);</pre>
namespace bitwise {
template <typename Enum>
static inline base::type::EnumType And(Enum e, base::type::EnumType flag) {
    return static_cast<base::type::EnumType>(flag) & static_cast<base::type::EnumType>(e);
}
template <typename Enum>
static inline base::type::EnumType Not(Enum e, base::type::EnumType flag) {
    return static_cast<base::type::EnumType>(flag) & ~(static_cast<base::type::EnumType>(e));
}
template <typename Enum>
static inline base::type::EnumType Or(Enum e, base::type::EnumType flag) {
    return static_cast<base::type::EnumType>(flag) | static_cast<base::type::EnumType>(e);
}
}  // namespace bitwise
template <typename Enum>
static inline void addFlag(Enum e, base::type::EnumType* flag) {
    *flag = base::utils::bitwise::Or<Enum>(e, *flag);
}
template <typename Enum>
static inline void removeFlag(Enum e, base::type::EnumType* flag) {
    *flag = base::utils::bitwise::Not<Enum>(e, *flag);
}
template <typename Enum>
static inline bool hasFlag(Enum e, base::type::EnumType flag) {
    return base::utils::bitwise::And<Enum>(e, flag) > 0x0;
}
}  // namespace utils
namespace threading {
#if _ELPP_THREADING_ENABLED
#   if !_ELPP_USE_STD_THREADING
namespace internal {
/// @brief A mutex wrapper for compiler that dont yet support std::mutex
class Mutex : base::NoCopy {
public:
    Mutex(void) {
#   if _ELPP_OS_UNIX
        pthread_mutex_init(&m_underlyingMutex, nullptr);
#   elif _ELPP_OS_WINDOWS
        InitializeCriticalSection(&m_underlyingMutex);
#   endif  // _ELPP_OS_UNIX
    }

    virtual ~Mutex(void) {
#   if _ELPP_OS_UNIX
        pthread_mutex_destroy(&m_underlyingMutex);
#   elif _ELPP_OS_WINDOWS
        DeleteCriticalSection(&m_underlyingMutex);
#   endif  // _ELPP_OS_UNIX
    }

    inline void lock(void) {
#   if _ELPP_OS_UNIX
        pthread_mutex_lock(&m_underlyingMutex);
#   elif _ELPP_OS_WINDOWS
        EnterCriticalSection(&m_underlyingMutex);
#   endif  // _ELPP_OS_UNIX
    }

    inline bool try_lock(void) {
#   if _ELPP_OS_UNIX
        return (pthread_mutex_trylock(&m_underlyingMutex) == 0);
#   elif _ELPP_OS_WINDOWS
        return TryEnterCriticalSection(&m_underlyingMutex);
#   endif  // _ELPP_OS_UNIX
    }

    inline void unlock(void) {
#   if _ELPP_OS_UNIX
        pthread_mutex_unlock(&m_underlyingMutex);
#   elif _ELPP_OS_WINDOWS
        LeaveCriticalSection(&m_underlyingMutex);
#   endif  // _ELPP_OS_UNIX
    }

private:
#   if _ELPP_OS_UNIX
    pthread_mutex_t m_underlyingMutex;
#   elif _ELPP_OS_WINDOWS
    CRITICAL_SECTION m_underlyingMutex;
#   endif  // _ELPP_OS_UNIX
};
/// @brief Scoped lock for compiler that dont yet support std::lock_guard
template <typename M>
class ScopedLock : base::NoCopy {
public:
    explicit ScopedLock(M& mutex) {  // NOLINT
        m_mutex = &mutex;
        m_mutex->lock();
    }

    virtual ~ScopedLock(void) {
        m_mutex->unlock();
    }
private:
    M* m_mutex;
    ScopedLock(void);
};
} // namespace internal
/// @brief Gets ID of currently running threading in windows systems. On unix, nothing is returned.
static inline std::string getCurrentThreadId(void) {
    std::stringstream ss;
#      if (_ELPP_OS_WINDOWS)
    ss << GetCurrentThreadId();
#      endif  // (_ELPP_OS_WINDOWS)
    return ss.str();
}
typedef base::threading::internal::Mutex Mutex;
typedef base::threading::internal::ScopedLock<base::threading::Mutex> ScopedLock;
#   else
/// @brief Gets ID of currently running threading using std::this_thread::get_id()
static inline std::string getCurrentThreadId(void) {
    std::stringstream ss;
    ss << std::this_thread::get_id();
    return ss.str();
}
typedef std::mutex Mutex;
typedef std::lock_guard<std::mutex> ScopedLock;
#   endif  // !_ELPP_USE_STD_THREADING
#else
namespace internal {
/// @brief Mutex wrapper used when multi-threading is disabled.
class NoMutex : base::NoCopy {
public:
    NoMutex(void) {}
    inline void lock(void) {}
    inline bool try_lock(void) { return true; }
    inline void unlock(void) {}
};
/// @brief Lock guard wrapper used when multi-threading is disabled.
template <typename Mutex>
class NoScopedLock : base::NoCopy {
public:
    explicit NoScopedLock(Mutex&) {
    }
    virtual ~NoScopedLock(void) {
    }
private:
    NoScopedLock(void);
};
}  // namespace internal
static inline std::string getCurrentThreadId(void) {
    return std::string();
}
typedef base::threading::internal::NoMutex Mutex;
typedef base::threading::internal::NoScopedLock<base::threading::Mutex> ScopedLock;
#endif  // _ELPP_THREADING_ENABLED
/// @brief Base of thread safe class, this class is inheritable-only
class ThreadSafe {
public:
    virtual inline void acquireLock(void) ELPP_FINAL { m_mutex.lock(); }
    virtual inline void releaseLock(void) ELPP_FINAL { m_mutex.unlock(); }
    virtual inline base::threading::Mutex& lock(void) ELPP_FINAL { return m_mutex; }
protected:
    ThreadSafe(void) {}
    virtual ~ThreadSafe(void) {}
private:
    base::threading::Mutex m_mutex;
};
}  // namespace threading
namespace utils {
class File : base::StaticClass {
public:
    /// @brief Creates new out file stream for specified filename.
    /// @return Pointer to newly created fstream or nullptr
    static base::type::fstream_t* newFileStream(const std::string& filename) {
        base::type::fstream_t *fs = new base::type::fstream_t(filename.c_str(), 
            base::type::fstream_t::out | base::type::fstream_t::app);
#if defined(_ELPP_UNICODE)
        std::locale elppUnicodeLocale("");
        fs->imbue(elppUnicodeLocale);
#endif  // defined(_ELPP_UNICODE)
        if (fs->is_open()) {
            fs->flush();
        } else {
            base::utils::safeDelete(fs);
            ELPP_INTERNAL_ERROR("Bad file [" << filename << "]", true);
        }
        return fs;
    }

    /// @brief Gets size of file provided in stream
    static std::size_t getSizeOfFile(base::type::fstream_t* fs) {
        if (fs == nullptr) {
            return 0;
        }
        std::streampos currPos = fs->tellg();
        fs->seekg(0, fs->end);
        std::size_t size = static_cast<std::size_t>(fs->tellg());
        fs->seekg(currPos);
        return size;
    }

    /// @brief Determines whether or not provided path exist in current file system
    static inline bool pathExists(const char* path, bool considerFile = false) {
        if (path == nullptr) {
            return false;
        }
#if _ELPP_OS_UNIX
        _ELPP_UNUSED(considerFile);
        struct stat st;
        return (stat(path, &st) == 0);
#elif _ELPP_OS_WINDOWS
        DWORD fileType = GetFileAttributesA(path);
        if (fileType == INVALID_FILE_ATTRIBUTES) {
            return false;
        }
        return considerFile ? true : ((fileType & FILE_ATTRIBUTE_DIRECTORY) == 0 ? false : true);
#endif  // _ELPP_OS_UNIX
    }

    /// @brief Creates specified path on file system
    /// @param path Path to create.
    static bool createPath(const std::string& path) {
        if (path.empty()) {
            return false;
        }
        if (base::utils::File::pathExists(path.c_str())) {
            return true;
        }
        int status = -1;

        char* currPath = const_cast<char*>(path.c_str());
        std::string builtPath = std::string();
#if _ELPP_OS_UNIX
        if (path[0] == '/') {
            builtPath = "/";
        }
        currPath = STRTOK(currPath, base::consts::kFilePathSeperator, 0);
#elif _ELPP_OS_WINDOWS
        // Use secure functions API
        char* nextTok_;
        currPath = STRTOK(currPath, base::consts::kFilePathSeperator, &nextTok_);
        _ELPP_UNUSED(nextTok_);
#endif  // _ELPP_OS_UNIX
        while (currPath != nullptr) {
            builtPath.append(currPath);
            builtPath.append(base::consts::kFilePathSeperator);
#if _ELPP_OS_UNIX
            status = mkdir(builtPath.c_str(), _ELPP_LOG_PERMS);
            currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, 0);
#elif _ELPP_OS_WINDOWS
            status = _mkdir(builtPath.c_str());
            currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, &nextTok_);
#endif  // _ELPP_OS_UNIX
        }
        if (status == -1) {
            ELPP_INTERNAL_ERROR("Error while creating path [" << path << "]", true);
            return false;
        }
        return true;
    }
    /// @brief Extracts path of filename with leading slash
    static std::string extractPathFromFilename(const std::string& fullPath,
            const char* seperator = base::consts::kFilePathSeperator) {
        if ((fullPath == "") || (fullPath.find(seperator) == std::string::npos)) {
            return fullPath;
        }
        std::size_t lastSlashAt = fullPath.find_last_of(seperator);
        if (lastSlashAt == 0) {
            return std::string(seperator);
        }
        return fullPath.substr(0, lastSlashAt + 1);
    }
    /// @brief builds stripped filename and puts it in buff
    static void buildStrippedFilename(const char* filename, char buff[], 
            std::size_t limit = base::consts::kSourceFilenameMaxLength) {
        std::size_t sizeOfFilename = strlen(filename);
        if (sizeOfFilename >= limit) {
            filename += (sizeOfFilename - limit);
            if (filename[0] != '.' && filename[1] != '.') {  // prepend if not already
                filename += 3;  // 3 = '..'
                STRCAT(buff, "..", limit);
            }
        }
        STRCAT(buff, filename, limit);
    }
    /// @brief builds base filename and puts it in buff
    static void buildBaseFilename(const std::string& fullPath, char buff[], 
            std::size_t limit = base::consts::kSourceFilenameMaxLength,
            const char* seperator = base::consts::kFilePathSeperator) {
        const char *filename = fullPath.c_str();
        std::size_t lastSlashAt = fullPath.find_last_of(seperator);
        filename += lastSlashAt ? lastSlashAt+1 : 0;
        std::size_t sizeOfFilename = strlen(filename);
        if (sizeOfFilename >= limit) {
            filename += (sizeOfFilename - limit);
            if (filename[0] != '.' && filename[1] != '.') {  // prepend if not already
                filename += 3;  // 3 = '..'
                STRCAT(buff, "..", limit);
            }
        }
        STRCAT(buff, filename, limit);
    }
};
/// @brief String utilities helper class used internally. You should not use it.
class Str : base::StaticClass {
public:
    /// @brief Checks if character is digit. Dont use libc implementation of it to prevent locale issues.
    static inline bool isDigit(char c) {
        return c >= '0' && c <= '9';
    }

    /// @brief Matches wildcards, '*' and '?' only supported.
    static bool wildCardMatch(const char* str, const char* pattern) {
        while (*pattern) {
            switch (*pattern) {
            case '?':
                if (!*str)
                    return false;
                ++str;
                ++pattern;
                break;
            case '*':
                if (wildCardMatch(str, pattern + 1))
                    return true;
                if (*str && wildCardMatch(str + 1, pattern))
                    return true;
                return false;
                break;
            default:
                if (*str++ != *pattern++)
                    return false;
                break;
            }
        }
        return !*str && !*pattern;
    }

    /// @brief Trims string from start
    /// @param [in,out] str String to trim
    static inline std::string& ltrim(std::string& str) {  // NOLINT
        str.erase(str.begin(), std::find_if(str.begin(), str.end(), std::not1(std::ptr_fun<int, int>(&std::isspace))));
        return str;
    }

    /// @brief Trim string from end
    /// @param [in,out] str String to trim
    static inline std::string& rtrim(std::string& str) {  // NOLINT
        str.erase(std::find_if(str.rbegin(), str.rend(), std::not1(std::ptr_fun<int, int>(&std::isspace))).base(), str.end());
        return str;
    }

    /// @brief Trims string from left and right
    /// @param [in,out] str String to trim
    static inline std::string& trim(std::string& str) {  // NOLINT
        return ltrim(rtrim(str));
    }

    /// @brief Determines whether or not str starts with specified string
    /// @param str String to check
    /// @param start String to check against
    /// @return Returns true if starts with specified string, false otherwise
    static inline bool startsWith(const std::string& str, const std::string& start) {
        return (str.length() >= start.length()) && (str.compare(0, start.length(), start) == 0);
    }

    /// @brief Determines whether or not str ends with specified string
    /// @param str String to check
    /// @param end String to check against
    /// @return Returns true if ends with specified string, false otherwise
    static inline bool endsWith(const std::string& str, const std::string& end) {
        return (str.length() >= end.length()) && (str.compare(str.length() - end.length(), end.length(), end) == 0);
    }

    /// @brief Replaces all instances of replaceWhat with 'replaceWith'. Original variable is changed for performance.
    /// @param [in,out] str String to replace from
    /// @param replaceWhat Character to replace
    /// @param replaceWith Character to replace with
    /// @return Modified version of str
    static inline std::string& replaceAll(std::string& str, char replaceWhat, char replaceWith) {  // NOLINT
        std::replace(str.begin(), str.end(), replaceWhat, replaceWith);
        return str;
    }

    /// @brief Replaces all instances of 'replaceWhat' with 'replaceWith'. (String version) Replaces in place
    /// @param str String to replace from
    /// @param replaceWhat Character to replace
    /// @param replaceWith Character to replace with
    /// @return Modified (original) str
    static inline std::string& replaceAll(std::string& str, const std::string& replaceWhat, // NOLINT
            const std::string& replaceWith) {
        if (replaceWhat == replaceWith)
            return str;
        std::size_t foundAt = std::string::npos;
        while ((foundAt = str.find(replaceWhat, foundAt + 1)) != std::string::npos) {
            str.replace(foundAt, replaceWhat.length(), replaceWith);
        }
        return str;
    }

    static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, // NOLINT
            const base::type::string_t& replaceWith) {
        std::size_t foundAt = base::type::string_t::npos;
        while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) {
            if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) {
                str.erase(foundAt > 0 ? foundAt - 1 : 0, 1);
                ++foundAt;
            } else {
                str.replace(foundAt, replaceWhat.length(), replaceWith);
                return;
            }
        }
    }
#if defined(_ELPP_UNICODE)
    static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, // NOLINT
            const std::string& replaceWith) {
        replaceFirstWithEscape(str, replaceWhat, base::type::string_t(replaceWith.begin(), replaceWith.end()));
    }
#endif  // defined(_ELPP_UNICODE)
    /// @brief Converts string to uppercase
    /// @param str String to convert
    /// @return Uppercase string
    static inline std::string& toUpper(std::string& str) {  // NOLINT
        std::transform(str.begin(), str.end(), str.begin(), ::toupper);
        return str;
    }

    /// @brief Compares cstring equality - uses strcmp
    static inline bool cStringEq(const char* s1, const char* s2) {
        if (s1 == nullptr && s2 == nullptr) return true;
        if (s1 == nullptr || s2 == nullptr) return false;
        return strcmp(s1, s2) == 0;
    }

    /// @brief Compares cstring equality (case-insensitive) - uses toupper(char)
    /// Dont use strcasecmp because of CRT (VC++)
    static bool cStringCaseEq(const char* s1, const char* s2) {
        if (s1 == nullptr && s2 == nullptr) return true;
        if (s1 == nullptr || s2 == nullptr) return false;
        if (strlen(s1) != strlen(s2)) return false;
        while (*s1 != '\0' && *s2 != '\0') {
            if (::toupper(*s1) != ::toupper(*s2)) return false;
            ++s1;
            ++s2;
        }
        return true;
    }

    /// @brief Returns true if c exist in str
    static inline bool contains(const char* str, char c) {
        for (; *str; ++str) {
            if (*str == c)
                return true;
        }
        return false;
    }

    static inline char* convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded = true) {
        char localBuff[10] = "";
        char* p = localBuff + sizeof(localBuff) - 2;
        if (n > 0) {
            for (; n > 0 && p > localBuff && len > 0; n /= 10, --len)
                *--p = static_cast<char>(n % 10 + '0');
        } else {
            *--p = '0';
            --len;
        }
        if (zeroPadded)
            while (p > localBuff && len-- > 0) *--p = static_cast<char>('0');
        return addToBuff(p, buf, bufLim);
    }

    static inline char* addToBuff(const char* str, char* buf, const char* bufLim) {
        while ((buf < bufLim) && ((*buf = *str++) != '\0'))
            ++buf;
        return buf;
    }

    static inline char* clearBuff(char buff[], std::size_t lim) {
        STRCPY(buff, "", lim);
        _ELPP_UNUSED(lim);  // For *nix we dont have anything using lim in above STRCPY macro
        return buff;
    }

    /// @brief Converst wchar* to char*
    ///        NOTE: Need to free return value after use!
    static char* wcharPtrToCharPtr(const wchar_t* line) {
        std::size_t len_ = wcslen(line) + 1;
        char* buff_ = static_cast<char*>(malloc(len_ + 1));
#      if _ELPP_OS_UNIX || (_ELPP_OS_WINDOWS && !_ELPP_CRT_DBG_WARNINGS)
        std::wcstombs(buff_, line, len_);
#      elif _ELPP_OS_WINDOWS
        std::size_t convCount_ = 0;
        mbstate_t mbState_;
        ::memset(static_cast<void*>(&mbState_), 0, sizeof(mbState_));
        wcsrtombs_s(&convCount_, buff_, len_, &line, len_, &mbState_);
#      endif  // _ELPP_OS_UNIX || (_ELPP_OS_WINDOWS && !_ELPP_CRT_DBG_WARNINGS)
       return buff_;
    }
};
/// @brief Operating System helper static class used internally. You should not use it.
class OS : base::StaticClass {
public:
#if _ELPP_OS_WINDOWS
    /// @brief Gets environment variables for Windows based OS. 
    ///        We are not using <code>getenv(const char*)</code> because of CRT deprecation
    /// @param varname Variable name to get environment variable value for
    /// @return If variable exist the value of it otherwise nullptr
    static const char* getWindowsEnvironmentVariable(const char* varname) {
        const DWORD bufferLen = 50;
        static char buffer[bufferLen];
        if (GetEnvironmentVariableA(varname, buffer, bufferLen)) {
            return buffer;
        }
        return nullptr;
    }
#endif  // _ELPP_OS_WINDOWS
#if _ELPP_OS_ANDROID
    /// @brief Reads android property value
    static inline std::string getProperty(const char* prop) {
        char propVal[PROP_VALUE_MAX + 1];
        int ret = __system_property_get(prop, propVal);
        return ret == 0 ? std::string() : std::string(propVal);
    }

    /// @brief Reads android device name
    static std::string getDeviceName(void) {
        std::stringstream ss;
        std::string manufacturer = getProperty("ro.product.manufacturer");
        std::string model = getProperty("ro.product.model");
        if (manufacturer.empty() || model.empty()) {
            return std::string();
        }
        ss << manufacturer << "-" << model;
        return ss.str();
    }
#endif  // _ELPP_OS_ANDROID

    /// @brief Runs command on terminal and returns the output.
    ///
    /// @detail This is applicable only on unix based systems, for all other OS, an empty string is returned.
    /// @param command Bash command
    /// @return Result of bash output or empty string if no result found.
    static const std::string getBashOutput(const char* command) {
#if (_ELPP_OS_UNIX && !_ELPP_OS_ANDROID && !_ELPP_CYGWIN)
        if (command == nullptr) {
            return std::string();
        }
        FILE* proc = nullptr;
        if ((proc = popen(command, "r")) == nullptr) {
            ELPP_INTERNAL_ERROR("\nUnable to run command [" << command << "]", true);
            return std::string();
        }
        char hBuff[4096];
        if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) {
            pclose(proc);
            if (hBuff[strlen(hBuff) - 1] == '\n') {
                hBuff[strlen(hBuff) - 1] = '\0';
            }
            return std::string(hBuff);
        }
        return std::string();
#else
        _ELPP_UNUSED(command);
        return std::string();
#endif  // (_ELPP_OS_UNIX && !_ELPP_OS_ANDROID && !_ELPP_CYGWIN)
    }

    /// @brief Gets environment variable. This is cross-platform and CRT safe (for VC++)
    /// @param variableName Environment variable name
    /// @param defaultVal If no environment variable or value found the value to return by default
    /// @param alternativeBashCommand If environment variable not found what would be alternative bash command
    ///        in order to look for value user is looking for. E.g, for 'user' alternative command will 'whoami'
    static std::string getEnvironmentVariable(const char* variableName, const char* defaultVal, const char* alternativeBashCommand = nullptr) {
#if _ELPP_OS_UNIX
        const char* val = getenv(variableName);
#elif _ELPP_OS_WINDOWS
        const char* val = getWindowsEnvironmentVariable(variableName);
#endif  // _ELPP_OS_UNIX
        if ((val == nullptr) || ((strcmp(val, "") == 0))) {
#if _ELPP_OS_UNIX && defined(_ELPP_FORCE_ENV_VAR_FROM_BASH)
           // Try harder on unix-based systems
            std::string valBash = base::utils::OS::getBashOutput(alternativeBashCommand);
            if (valBash.empty()) {
                return std::string(defaultVal);
            } else {
                return valBash;
            }
#elif _ELPP_OS_WINDOWS || _ELPP_OS_UNIX
            _ELPP_UNUSED(alternativeBashCommand);
            return std::string(defaultVal);
#endif  // _ELPP_OS_UNIX && defined(_ELPP_FORCE_ENV_VAR_FROM_BASH)
        }
        return std::string(val);
    }
   /// @brief Gets current username.
    static inline std::string currentUser(void) {
#if _ELPP_OS_UNIX && !_ELPP_OS_ANDROID
        return getEnvironmentVariable("USER", base::consts::kUnknownUser, "whoami");
#elif _ELPP_OS_WINDOWS
        return getEnvironmentVariable("USERNAME", base::consts::kUnknownUser);
#elif _ELPP_OS_ANDROID
        _ELPP_UNUSED(base::consts::kUnknownUser);
        return std::string("android");
#else
        return std::string();
#endif  // _ELPP_OS_UNIX && !_ELPP_OS_ANDROID
    }

    /// @brief Gets current host name or computer name.
    ///
    /// @detail For android systems this is device name with its manufacturer and model seperated by hyphen
    static inline std::string currentHost(void) {
#if _ELPP_OS_UNIX && !_ELPP_OS_ANDROID
        return getEnvironmentVariable("HOSTNAME", base::consts::kUnknownHost, "hostname");
#elif _ELPP_OS_WINDOWS
        return getEnvironmentVariable("COMPUTERNAME", base::consts::kUnknownHost);
#elif _ELPP_OS_ANDROID
        _ELPP_UNUSED(base::consts::kUnknownHost);
        return getDeviceName();
#else
        return std::string();
#endif  // _ELPP_OS_UNIX && !_ELPP_OS_ANDROID
    }
    /// @brief Whether or not terminal supports colors
    static inline bool termSupportsColor(void) {
        std::string term = getEnvironmentVariable("TERM", "");
        return term == "xterm" || term == "xterm-color" || term == "xterm-256color" ||
                              term == "screen" || term == "linux" || term == "cygwin";
    }
};
extern std::string s_currentUser;
extern std::string s_currentHost;
extern bool s_termSupportsColor;
#define _ELPP_INITI_BASIC_DECLR \
    namespace el {\
        namespace base {\
            namespace utils {\
                std::string s_currentUser = el::base::utils::OS::currentUser(); \
                std::string s_currentHost = el::base::utils::OS::currentHost(); \
                bool s_termSupportsColor = el::base::utils::OS::termSupportsColor(); \
            }\
        }\
   }
/// @brief Contains utilities for cross-platform date/time. This class make use of el::base::utils::Str
class DateTime : base::StaticClass {
public:
    /// @brief Cross platform gettimeofday for Windows and unix platform. This can be used to determine current millisecond.
    ///
    /// @detail For unix system it uses gettimeofday(timeval*, timezone*) and for Windows, a seperate implementation is provided
    /// @param [in,out] tv Pointer that gets updated
    static void gettimeofday(struct timeval* tv) {
#if _ELPP_OS_WINDOWS
        if (tv != nullptr) {
#   if _ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS)
            const unsigned __int64 delta_ = 11644473600000000Ui64;
#   else
            const unsigned __int64 delta_ = 11644473600000000ULL;
#   endif  // _ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS)
            const double secOffSet = 0.000001;
            const unsigned long usecOffSet = 1000000;  // NOLINT
            FILETIME fileTime;
            GetSystemTimeAsFileTime(&fileTime);
            unsigned __int64 present = 0;
            present |= fileTime.dwHighDateTime;
            present = present << 32;
            present |= fileTime.dwLowDateTime;
            present /= 10;  // mic-sec
           // Subtract the difference
            present -= delta_;
            tv->tv_sec = static_cast<long>(present * secOffSet);  // NOLINT
            tv->tv_usec = static_cast<long>(present % usecOffSet);  // NOLINT
        }
#else
        ::gettimeofday(tv, nullptr);
#endif  // _ELPP_OS_WINDOWS
    }

    /// @brief Gets current date and time with milliseconds.
    /// @param format User provided date/time format
    /// @param msWidth A pointer to base::MillisecondsWidth from configuration (non-null)
    /// @returns string based date time in specified format.
    static inline std::string getDateTime(const char* format, const base::MillisecondsWidth* msWidth) {
        struct timeval currTime;
        gettimeofday(&currTime);
        struct ::tm timeInfo;
        buildTimeInfo(&currTime, &timeInfo);
        const int kBuffSize = 30;
        char buff_[kBuffSize] = "";
        parseFormat(buff_, kBuffSize, format, &timeInfo, static_cast<std::size_t>(currTime.tv_usec / msWidth->m_offset), msWidth);
        return std::string(buff_);
    }

    /// @brief Formats time to get unit accordingly, units like second if > 1000 or minutes if > 60000 etc
    static base::type::string_t formatTime(unsigned long long time, base::TimestampUnit timestampUnit) {  // NOLINT
        double result = static_cast<double>(time);
        base::type::EnumType start = static_cast<base::type::EnumType>(timestampUnit);
        const base::type::char_t* unit = base::consts::kTimeFormats[start].unit;
        for (base::type::EnumType i = start; i < base::consts::kTimeFormatsCount - 1; ++i) {
            if (result <= base::consts::kTimeFormats[i].value) {
                break;
            }
            result /= base::consts::kTimeFormats[i].value;
            unit = base::consts::kTimeFormats[i + 1].unit;
        }
        base::type::stringstream_t ss;
        ss << result << " " << unit;
        return ss.str();
    }

    /// @brief Gets time difference in milli/micro second depending on timestampUnit
    static inline unsigned long long getTimeDifference(const struct timeval& endTime, const struct timeval& startTime, base::TimestampUnit timestampUnit) {  // NOLINT
        if (timestampUnit == base::TimestampUnit::Microsecond) {
            return static_cast<unsigned long long>(static_cast<unsigned long long>(1000000 * endTime.tv_sec + endTime.tv_usec) -  // NOLINT
                    static_cast<unsigned long long>(1000000 * startTime.tv_sec + startTime.tv_usec));  // NOLINT
        } else {
            return static_cast<unsigned long long>((((endTime.tv_sec - startTime.tv_sec) * 1000000) + (endTime.tv_usec - startTime.tv_usec)) / 1000);  // NOLINT
        }
    }

private:
    static inline struct ::tm* buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo) {
#if _ELPP_OS_UNIX
        time_t rawTime = currTime->tv_sec;
        ::localtime_r(&rawTime, timeInfo);
        return timeInfo;
#else
#   if _ELPP_COMPILER_MSVC
        _ELPP_UNUSED(currTime);
        time_t t;
        _time64(&t);
        localtime_s(timeInfo, &t);
        return timeInfo;
#   else
        // For any other compilers that don't have CRT warnings issue e.g, MinGW or TDM GCC- we use different method
        time_t rawTime = currTime->tv_sec;  // NOLINT
        struct tm* tmInf = localtime(&rawTime);  // NOLINT
        *timeInfo = *tmInf;
        return timeInfo;
#   endif  // _ELPP_COMPILER_MSVC
#endif  // _ELPP_OS_UNIX
    }
    static char* parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo,
            std::size_t msec, const base::MillisecondsWidth* msWidth) {
        const char* bufLim = buf + bufSz;
        for (; *format; ++format) {
            if (*format == base::consts::kFormatSpecifierChar) {
                switch (*++format) {
                case base::consts::kFormatSpecifierChar:  // Escape
                    break;
                case '\0':  // End
                    --format;
                    break;
                case 'd':  // Day
                    buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mday, 2, buf, bufLim);
                    continue;
                case 'a':  // Day of week (short)
                    buf = base::utils::Str::addToBuff(base::consts::kDaysAbbrev[tInfo->tm_wday], buf, bufLim);
                    continue;
                case 'A':  // Day of week (long)
                    buf = base::utils::Str::addToBuff(base::consts::kDays[tInfo->tm_wday], buf, bufLim);
                    continue;
                case 'M':  // month
                    buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mon + 1, 2, buf, bufLim);
                    continue;
                case 'b':  // month (short)
                    buf = base::utils::Str::addToBuff(base::consts::kMonthsAbbrev[tInfo->tm_mon], buf, bufLim);
                    continue;
                case 'B':  // month (long)
                    buf = base::utils::Str::addToBuff(base::consts::kMonths[tInfo->tm_mon], buf, bufLim);
                    continue;
                case 'y':  // year (two digits)
                    buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 2, buf, bufLim);
                    continue;
                case 'Y':  // year (four digits)
                    buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 4, buf, bufLim);
                    continue;
                case 'h':  // hour (12-hour)
                    buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour % 12, 2, buf, bufLim);
                    continue;
                case 'H':  // hour (24-hour)
                    buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour, 2, buf, bufLim);
                    continue;
                case 'm':  // minute
                    buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_min, 2, buf, bufLim);
                    continue;
                case 's':  // second
                    buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_sec, 2, buf, bufLim);
                    continue;
                case 'z':  // milliseconds
                case 'g':
                    buf = base::utils::Str::convertAndAddToBuff(msec, msWidth->m_width, buf, bufLim);
                    continue;
                case 'F':  // AM/PM
                    buf = base::utils::Str::addToBuff((tInfo->tm_hour >= 12) ? base::consts::kPm : base::consts::kAm, buf, bufLim);
                    continue;
                default:
                    continue;
                }
            }
            if (buf == bufLim) break;
            *buf++ = *format;
        }
        return buf;
    }
};
/// @brief Command line arguments for application if specified using el::Helpers::setArgs(..) or _START_EASYLOGGINGPP(..)
class CommandLineArgs {
public:
    CommandLineArgs(void) {
        setArgs(0, static_cast<char**>(nullptr));
    }
    CommandLineArgs(int argc, const char** argv) {
        setArgs(argc, argv);
    }
    CommandLineArgs(int argc, char** argv) {
        setArgs(argc, argv);
    }
    virtual ~CommandLineArgs(void) {}
    /// @brief Sets arguments and parses them
    inline void setArgs(int argc, const char** argv) {
        setArgs(argc, const_cast<char**>(argv));
    }
    /// @brief Sets arguments and parses them
    inline void setArgs(int argc, char** argv) {
        m_params.clear();
        m_paramsWithValue.clear();
        if (argc == 0 || argv == nullptr) {
            return;
        }
        m_argc = argc;
        m_argv = argv;
        for (int i = 1; i < m_argc; ++i) {
            const char* v = (strstr(m_argv[i], "="));
            if (v != nullptr && strlen(v) > 0) {
                std::string key = std::string(m_argv[i]);
                key = key.substr(0, key.find_first_of('='));
                if (hasParamWithValue(key.c_str())) {
                    ELPP_INTERNAL_INFO(1, "Skipping [" << key << "] arg since it already has value [" 
                        << getParamValue(key.c_str()) << "]");
                } else {
                    m_paramsWithValue.insert(std::make_pair(key, std::string(v + 1)));
                }
            }
            if (v == nullptr) {
                if (hasParam(m_argv[i])) {
                    ELPP_INTERNAL_INFO(1, "Skipping [" << m_argv[i] << "] arg since it already exists");
                } else {
                    m_params.push_back(std::string(m_argv[i]));
                }
            }
        }
    }
    /// @brief Returns true if arguments contain paramKey with a value (seperated by '=')
    inline bool hasParamWithValue(const char* paramKey) const {
        return m_paramsWithValue.find(std::string(paramKey)) != m_paramsWithValue.end();
    }
    /// @brief Returns value of arguments
    /// @see hasParamWithValue(const char*)
    inline const char* getParamValue(const char* paramKey) const {
        return m_paramsWithValue.find(std::string(paramKey))->second.c_str();
    }
    /// @brief Return true if arguments has a param (not having a value) i,e without '='
    inline bool hasParam(const char* paramKey) const {
        return std::find(m_params.begin(), m_params.end(), std::string(paramKey)) != m_params.end();
    }
    /// @brief Returns true if no params available. This exclude argv[0]
    inline bool empty(void) const {
        return m_params.empty() && m_paramsWithValue.empty();
    }
    /// @brief Returns total number of arguments. This exclude argv[0]
    inline std::size_t size(void) const {
        return m_params.size() + m_paramsWithValue.size();
    }
    inline friend base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c) {
        for (int i = 1; i < c.m_argc; ++i) {
            os << ELPP_LITERAL("[") << c.m_argv[i] << ELPP_LITERAL("]");
            if (i < c.m_argc - 1) {
                os << ELPP_LITERAL(" ");
            }
        }
        return os;
    }

private:
    int m_argc;
    char** m_argv;
    std::map<std::string, std::string> m_paramsWithValue;
    std::vector<std::string> m_params;
};
/// @brief Abstract registry (aka repository) that provides basic interface for pointer repository specified by T_Ptr type.
///
/// @detail Most of the functions are virtual final methods but anything implementing this abstract class should implement
/// unregisterAll() and deepCopy(const AbstractRegistry<T_Ptr, Container>&) and write registerNew() method according to container
/// and few more methods; get() to find element, unregister() to unregister single entry.
/// Please note that this is thread-unsafe and should also implement thread-safety mechanisms in implementation.
template <typename T_Ptr, typename Container>
class AbstractRegistry : public base::threading::ThreadSafe {
public:
    typedef typename Container::iterator iterator;
    typedef typename Container::const_iterator const_iterator;

    /// @brief Default constructor
    AbstractRegistry(void) {}

    /// @brief Move constructor that is useful for base classes
    AbstractRegistry(AbstractRegistry&& sr) {
        if (this == &sr) {
            return;
        }
        unregisterAll();
        m_list = std::move(sr.m_list);
    }

    bool operator==(const AbstractRegistry<T_Ptr, Container>& other) {
        if (size() != other.size()) {
            return false;
        }
        for (std::size_t i = 0; i < m_list.size(); ++i) {
            if (m_list.at(i) != other.m_list.at(i)) {
                return false;
            }
        }
        return true;
    }

    bool operator!=(const AbstractRegistry<T_Ptr, Container>& other) {
        if (size() != other.size()) {
            return true;
        }
        for (std::size_t i = 0; i < m_list.size(); ++i) {
            if (m_list.at(i) != other.m_list.at(i)) {
                return true;
            }
        }
        return false;
    }

    /// @brief Assignment move operator
    AbstractRegistry& operator=(AbstractRegistry&& sr) {
        if (this == &sr) {
            return *this;
        }
        unregisterAll();
        m_list = std::move(sr.m_list);
        return *this;
    }

    virtual ~AbstractRegistry(void) {
    }

    /// @return Iterator pointer from start of repository
    virtual inline iterator begin(void) ELPP_FINAL {
        return m_list.begin();
    }

    /// @return Iterator pointer from end of repository
    virtual inline iterator end(void) ELPP_FINAL {
        return m_list.end();
    }


    /// @return Constant iterator pointer from start of repository
    virtual inline const_iterator cbegin(void) const ELPP_FINAL {
        return m_list.cbegin();
    }

    /// @return End of repository
    virtual inline const_iterator cend(void) const ELPP_FINAL {
        return m_list.cend();
    }

    /// @return Whether or not repository is empty
    virtual inline bool empty(void) const ELPP_FINAL {
        return m_list.empty();
    }

    /// @return Size of repository
    virtual inline std::size_t size(void) const ELPP_FINAL {
        return m_list.size();
    }

    /// @brief Returns underlying container by reference
    virtual inline Container& list(void) ELPP_FINAL {
        return m_list;
    }

    /// @brief Returns underlying container by constant reference.
    virtual inline const Container& list(void) const ELPP_FINAL {
        return m_list;
    }

    /// @brief Unregisters all the pointers from current repository.
    virtual void unregisterAll(void) = 0;

protected:
    virtual void deepCopy(const AbstractRegistry<T_Ptr, Container>&) = 0;
    void reinitDeepCopy(const AbstractRegistry<T_Ptr, Container>& sr) {
        unregisterAll();
        deepCopy(sr);
    }

private:
    Container m_list;
};

/// @brief A pointer registry mechanism to manage memory and provide search functionalities. (non-predicate version)
///
/// @detail NOTE: This is thread-unsafe implementation (although it contains lock function, it does not use these functions)
///         of AbstractRegistry<T_Ptr, Container>. Any implementation of this class should be  
///         explicitly (by using lock functions)
template <typename T_Ptr, typename T_Key = const char*>
class Registry : public AbstractRegistry<T_Ptr, std::map<T_Key, T_Ptr*>> {
public:
    typedef typename Registry<T_Ptr, T_Key>::iterator iterator;
    typedef typename Registry<T_Ptr, T_Key>::const_iterator const_iterator;

    Registry(void) {}

    /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor.
    Registry(const Registry& sr) : AbstractRegistry<T_Ptr, std::vector<T_Ptr*>>() {
        if (this == &sr) {
            return;
        }
        this->reinitDeepCopy(sr);
    }

    /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element
    /// @see unregisterAll()
    /// @see deepCopy(const AbstractRegistry&)
    Registry& operator=(const Registry& sr) {
        if (this == &sr) {
            return *this;
        }
        this->reinitDeepCopy(sr);
        return *this;
    }

    virtual ~Registry(void) {
        unregisterAll();
    }

protected:
    virtual inline void unregisterAll(void) ELPP_FINAL {
        if (!this->empty()) {
            for (auto&& curr : this->list()) {
                base::utils::safeDelete(curr.second);
            }
            this->list().clear();
        }
    }

    /// @brief Registers new registry to repository.
    virtual inline void registerNew(const T_Key& uniqKey, T_Ptr* ptr) ELPP_FINAL {
        unregister(uniqKey);
        this->list().insert(std::make_pair(uniqKey, ptr));
    }

    /// @brief Unregisters single entry mapped to specified unique key
    inline void unregister(const T_Key& uniqKey) {
        T_Ptr* existing = get(uniqKey);
        if (existing != nullptr) {
            base::utils::safeDelete(existing);
            this->list().erase(uniqKey);
        }
    }

    /// @brief Gets pointer from repository. If none found, nullptr is returned.
    inline T_Ptr* get(const T_Key& uniqKey) {
        iterator it = this->list().find(uniqKey);
        return it == this->list().end()
                ? nullptr
                : it->second;
    }

private:
    virtual inline void deepCopy(const AbstractRegistry<T_Ptr, std::map<T_Key, T_Ptr*>>& sr) ELPP_FINAL {
        for (const_iterator it = sr.cbegin(); it != sr.cend(); ++it) {
            registerNew(it->first, new T_Ptr(*it->second));
        }
    }
};

/// @brief A pointer registry mechanism to manage memory and provide search functionalities. (predicate version)
///
/// @detail NOTE: This is thread-unsafe implementation of AbstractRegistry<T_Ptr, Container>. Any implementation of this class
/// should be made thread-safe explicitly
template <typename T_Ptr, typename Pred>
class RegistryWithPred : public AbstractRegistry<T_Ptr, std::vector<T_Ptr*>> {
public:
    typedef typename RegistryWithPred<T_Ptr, Pred>::iterator iterator;
    typedef typename RegistryWithPred<T_Ptr, Pred>::const_iterator const_iterator;

    RegistryWithPred(void) {
    }

    virtual ~RegistryWithPred(void) {
        unregisterAll();
    }

    /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor.
    RegistryWithPred(const RegistryWithPred& sr) : AbstractRegistry<T_Ptr, std::vector<T_Ptr*>>() {
        if (this == &sr) {
            return;
        }
        this->reinitDeepCopy(sr);
    }

    /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element
    /// @see unregisterAll()
    /// @see deepCopy(const AbstractRegistry&)
    RegistryWithPred& operator=(const RegistryWithPred& sr) {
        if (this == &sr) {
            return *this;
        }
        this->reinitDeepCopy(sr);
        return *this;
    }

    friend inline base::type::ostream_t& operator<<(base::type::ostream_t& os, const RegistryWithPred& sr) {
        for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) {
            os << ELPP_LITERAL("    ") << **it << ELPP_LITERAL("\n");
        }
        return os;
    }

protected:
    virtual inline void unregisterAll(void) ELPP_FINAL {
        if (!this->empty()) {
            for (auto&& curr : this->list()) {
                base::utils::safeDelete(curr);
            }
            this->list().clear();
        }
    }

    virtual void unregister(T_Ptr*& ptr) ELPP_FINAL {  // NOLINT
        if (ptr) {
            iterator iter = this->begin();
            for (; iter != this->end(); ++iter) {
                if (ptr == *iter) {
                    break;
                }
            }
            if (iter != this->end() && *iter != nullptr) {
                this->list().erase(iter);
                base::utils::safeDelete(*iter);
            }
        }
    }

    virtual inline void registerNew(T_Ptr* ptr) ELPP_FINAL {
        this->list().push_back(ptr);
    }

    /// @brief Gets pointer from repository with speicifed arguments. Arguments are passed to predicate
    /// in order to validate pointer.
    template <typename T, typename T2>
    inline T_Ptr* get(const T& arg1, const T2 arg2) {
        iterator iter = std::find_if(this->list().begin(), this->list().end(), Pred(arg1, arg2));
        if (iter != this->list().end() && *iter != nullptr) {
            return *iter;
        }
        return nullptr;
    }

private:
    virtual inline void deepCopy(const AbstractRegistry<T_Ptr, std::vector<T_Ptr*>>& sr) {
        for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) {
            registerNew(new T_Ptr(**it));
        }
    }
};

}  // namespace utils
} // namespace base
/// @brief Base of Easylogging++ friendly class
///
/// @detail After inheriting this class publicly, implement pure-virtual function `void log(std::ostream&) const`
class Loggable {
public:
    virtual ~Loggable(void) {}
    virtual void log(el::base::type::ostream_t&) const = 0;
private:
    friend inline el::base::type::ostream_t& operator<<(el::base::type::ostream_t& os, const Loggable& loggable) {
        loggable.log(os);
        return os;
    }
};
namespace base {
/// @brief Represents log format containing flags and date format. This is used internally to start initial log
class LogFormat : public Loggable {
public:
    LogFormat(void) :
        m_level(Level::Unknown),
        m_userFormat(base::type::string_t()),
        m_format(base::type::string_t()),
        m_dateTimeFormat(std::string()),
        m_flags(0x0) {
    }

    LogFormat(Level level, const base::type::string_t& format)
            : m_level(level), m_userFormat(format) {
        parseFromFormat(m_userFormat);
    }

    LogFormat(const LogFormat& logFormat) {
        m_level = logFormat.m_level;
        m_userFormat = logFormat.m_userFormat;
        m_format = logFormat.m_format;
        m_dateTimeFormat = logFormat.m_dateTimeFormat;
        m_flags = logFormat.m_flags;
    }

    LogFormat(LogFormat&& logFormat) {
        m_level = std::move(logFormat.m_level);
        m_userFormat = std::move(logFormat.m_userFormat);
        m_format = std::move(logFormat.m_format);
        m_dateTimeFormat = std::move(logFormat.m_dateTimeFormat);
        m_flags = std::move(logFormat.m_flags);
    }

    LogFormat& operator=(const LogFormat& logFormat) {
        m_level = logFormat.m_level;
        m_userFormat = logFormat.m_userFormat;
        m_dateTimeFormat = logFormat.m_dateTimeFormat;
        m_flags = logFormat.m_flags;
        return *this;
    }

    virtual ~LogFormat(void) {
    }

    inline bool operator==(const LogFormat& other) {
        return m_level == other.m_level && m_userFormat == other.m_userFormat && m_format == other.m_format &&
                m_dateTimeFormat == other.m_dateTimeFormat && m_flags == other.m_flags;
    }

    /// @brief Updates format to be used while logging.
    /// @param userFormat User provided format
    void parseFromFormat(const base::type::string_t& userFormat) {
        // We make copy because we will be changing the format
        // i.e, removing user provided date format from original format
        // and then storing it.
        base::type::string_t formatCopy = userFormat;
        m_flags = 0x0;
        auto conditionalAddFlag = [&](const base::type::char_t* specifier, base::FormatFlags flag) {
            std::size_t foundAt = base::type::string_t::npos;
            while ((foundAt = formatCopy.find(specifier, foundAt + 1)) != base::type::string_t::npos){
                if (foundAt > 0 && formatCopy[foundAt - 1] == base::consts::kFormatSpecifierChar) {
                    if (hasFlag(flag)) {
                        // If we already have flag we remove the escape chars so that '%%' is turned to '%'
                        // even after specifier resolution - this is because we only replaceFirst specifier
                        formatCopy.erase(foundAt > 0 ? foundAt - 1 : 0, 1);
                        ++foundAt;
                    }
                } else {
                    if (!hasFlag(flag)) addFlag(flag);
                }
            }
        };  // NOLINT
        conditionalAddFlag(base::consts::kAppNameFormatSpecifier, base::FormatFlags::AppName);
        conditionalAddFlag(base::consts::kSeverityLevelFormatSpecifier, base::FormatFlags::Level);
        conditionalAddFlag(base::consts::kSeverityLevelShortFormatSpecifier, base::FormatFlags::LevelShort);
        conditionalAddFlag(base::consts::kLoggerIdFormatSpecifier, base::FormatFlags::LoggerId);
        conditionalAddFlag(base::consts::kThreadIdFormatSpecifier, base::FormatFlags::ThreadId);
        conditionalAddFlag(base::consts::kLogFileFormatSpecifier, base::FormatFlags::File);
        conditionalAddFlag(base::consts::kLogFileBaseFormatSpecifier, base::FormatFlags::FileBase);
        conditionalAddFlag(base::consts::kLogLineFormatSpecifier, base::FormatFlags::Line);
        conditionalAddFlag(base::consts::kLogLocationFormatSpecifier, base::FormatFlags::Location);
        conditionalAddFlag(base::consts::kLogFunctionFormatSpecifier, base::FormatFlags::Function);
        conditionalAddFlag(base::consts::kCurrentUserFormatSpecifier, base::FormatFlags::User);
        conditionalAddFlag(base::consts::kCurrentHostFormatSpecifier, base::FormatFlags::Host);
        conditionalAddFlag(base::consts::kMessageFormatSpecifier, base::FormatFlags::LogMessage);
        conditionalAddFlag(base::consts::kVerboseLevelFormatSpecifier, base::FormatFlags::VerboseLevel);
        // For date/time we need to extract user's date format first
        std::size_t dateIndex = std::string::npos;
        if ((dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier)) != std::string::npos) {
            while (dateIndex > 0 && formatCopy[dateIndex - 1] == base::consts::kFormatSpecifierChar) {
                dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier, dateIndex + 1);
            }
            if (dateIndex != std::string::npos) {
                addFlag(base::FormatFlags::DateTime);
                updateDateFormat(dateIndex, formatCopy);
            }
        }
        m_format = formatCopy;
        updateFormatSpec();
    }

    inline Level level(void) const {
        return m_level;
    }

    inline const base::type::string_t& userFormat(void) const {
        return m_userFormat;
    }

    inline const base::type::string_t& format(void) const {
       return m_format;
    }

    inline const std::string& dateTimeFormat(void) const {
       return m_dateTimeFormat;
    }

    inline base::type::EnumType flags(void) const {
       return m_flags;
    }

    inline bool hasFlag(base::FormatFlags flag) const {
        return base::utils::hasFlag(flag, m_flags);
    }

    virtual void log(el::base::type::ostream_t& os) const {
        os << m_format;
    }

protected:
    /// @brief Updates date time format if available in currFormat.
    /// @param index Index where %datetime, %date or %time was found
    /// @param [in,out] currFormat current format that is being used to format
    virtual void updateDateFormat(std::size_t index, base::type::string_t& currFormat) ELPP_FINAL {  // NOLINT
        if (hasFlag(base::FormatFlags::DateTime)) {
            index += ELPP_STRLEN(base::consts::kDateTimeFormatSpecifier);
        }
        const base::type::char_t* ptr = currFormat.c_str() + index;
        if ((currFormat.size() > index) && (ptr[0] == '{')) {
            // User has provided format for date/time
Loading
Loading full blame…