Created
March 24, 2017 09:47
-
-
Save hutorny/6666eb025c6188ad6e30c401af928822 to your computer and use it in GitHub Desktop.
Minimalistic C++11 logger with per-user-class configuration
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* This file is intended for miculog per-class configuration | |
*/ | |
/* remove this message from your own copy of miculog.config */ | |
#pragma message "Default miculog configuration is in use" | |
/* add forward declaration of your classes here, | |
* embrace classes in namespaces, as needed | |
* | |
* to enable code remove space there -> * / | |
class myclass; | |
namespace miculog { | |
template<> struct class_log_levels<myclass> { | |
static constexpr auto value = levels< | |
level::warn, level::error, level::fail>::value; | |
}; | |
template<> struct appender<myclass> { | |
static void log(const char* fmt, ...) noexcept; | |
}; | |
} | |
// as an lazy option you may use macro SET_MIN_LOGLEVEL | |
/// Sets min log level for a given class CLASS. | |
/// CLASS must be declared prior to use of this macro | |
#define SET_MIN_LOGLEVEL(CLASS, LEVEL) \ | |
namespace miculog { \ | |
template<> struct class_log_levels<CLASS> { \ | |
static constexpr auto value = from<LEVEL>::value; \ | |
};} \ | |
class myclass2; | |
SET_MIN_LOGLEVEL(myclass2, level::warn) | |
// */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2017 Eugene Hutorny <[email protected]> | |
* | |
* miculog.hpp - simple logging facilities | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), | |
* to deal in the Software without restriction, including without limitation | |
* the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
* and/or sell copies of the Software, and to permit persons to whom the | |
* Software is furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in | |
* all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
* IN THE SOFTWARE. | |
* | |
* https://opensource.org/licenses/MIT | |
*/ | |
#include <cstdio> | |
#include <cstdarg> | |
#include "miculog.hpp" | |
namespace miculog { | |
inline constexpr unsigned char operator+(level lvl) noexcept { | |
return static_cast<unsigned char>(lvl); | |
} | |
namespace details { | |
static const char * const names[1 + +level::none] = { | |
"!TRACE: ", | |
"!DEBUG: ", | |
"!INFO : ", | |
"!WARN : ", | |
"!ERROR: ", | |
"!FAIL : ", | |
"!?NONE: " | |
}; | |
void default_appender::log(level lvl, const char* fmt, ...) noexcept { | |
if( lvl <= level::none ) | |
fputs(names[+lvl], stdout); | |
va_list args; | |
va_start(args, fmt); | |
vfprintf(stdout, fmt, args); | |
va_end(args); | |
} | |
}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2017 Eugene Hutorny <[email protected]> | |
* | |
* miculog.hpp - simple logging facilities | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), | |
* to deal in the Software without restriction, including without limitation | |
* the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
* and/or sell copies of the Software, and to permit persons to whom the | |
* Software is furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in | |
* all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
* IN THE SOFTWARE. | |
* | |
* https://opensource.org/licenses/MIT | |
*/ | |
#ifndef MICULOG_HPP_ | |
#define MICULOG_HPP_ | |
/* | |
* Simple and configurable logging facilities | |
* | |
* Concepts | |
* Classes defined here facilitate logging functionality with | |
* compile-time configurable levels and appenders on per-user-class basis. | |
* Logging statement with opted off levels are completely removed from | |
* generated code by the compiler on optimization phase. If logging is | |
* turned off completely, compiler builds application without any appenders | |
* provided | |
* | |
* Usage | |
* Define a static instance of Log class in the compilation unit where it will | |
* be used: | |
* | |
* static miculog::Log<myclass> log; | |
* | |
* Use methods, available in log: | |
* | |
* log.error("error code:%d\n", err); | |
* | |
* This will emit log statements ending up in default appender, which | |
* calls vprintf for levels error and fail | |
* | |
* To change enabled levels, specialize template class_log_levels | |
* as the following: | |
* | |
* namespace miculog { | |
* template<> struct class_log_levels<myclass> { | |
* static constexpr auto value = levels<level::info>::value; | |
* };} | |
* | |
* To change appender specialize template appender as the following: | |
* | |
* namespace miculog { | |
* template<> struct appender<myclass> { | |
* static void log(const char* fmt, ...) noexcept; | |
* };} | |
* | |
* and implement appender<myclass>::log | |
* | |
* Generally, you may add these template specializations in any place in | |
* your code before the actual instantiation of template Log, | |
* Recommended practice is to include them in your own copy of | |
* micolug.config file, which should be found in the include path | |
* before the one provided with this library | |
* | |
* You may also replace default appender with a suitable implementation | |
* simply by removing miculog.cpp from the build and providing your own | |
* implementation elsewhere | |
*/ | |
namespace miculog { | |
enum class level : unsigned char { | |
trace, | |
debug, | |
info, | |
warn, | |
error, | |
fail, | |
none | |
}; | |
namespace details { | |
/* all log calls end up in this function, implemented by the user */ | |
struct default_appender { | |
static void log(level, const char* fmt, ...) noexcept | |
__attribute__ ((format (printf, 2, 3))); | |
}; | |
template<typename V, typename T, T...> | |
struct enumset; | |
template<typename V, typename T> | |
struct enumset<V, T> { | |
static constexpr const V value = 0; | |
}; | |
template<typename V, typename T> | |
static constexpr const V shift(int v, T t) noexcept { | |
return static_cast<V>(v) << static_cast<V>(t); | |
} | |
template<typename V, typename T, T t> | |
struct enumset<V, T, t> { | |
static constexpr const V value = { shift<V>(1, t) }; | |
}; | |
template<typename V, typename T, T t, T ... l> | |
struct enumset<V, T, t, l...> { | |
static constexpr const V value = { | |
(shift<V>(1, t)) | enumset<V, T, l...>::value }; | |
}; | |
template<typename V> | |
static inline bool constexpr is_set(V v, level t) noexcept { | |
return v & (static_cast<V>(1) << static_cast<V>(t)); | |
} | |
} | |
template<class C> struct appender: public details::default_appender {}; | |
template<level t> | |
struct from { | |
static constexpr const unsigned char value = { | |
t == level::none ? 0 : details::shift<unsigned char>(-1, t) }; | |
}; | |
template<level ... list> | |
struct levels: details::enumset<unsigned char, level, list...> {}; | |
//TODO switch to variable template when compilers catch up on C++14 | |
template<class C> struct class_log_levels { | |
static constexpr auto value = from<level::error>::value; | |
}; | |
template<class C> | |
struct Log { | |
typedef appender<C> appender; | |
static inline constexpr const auto enabled(level lvl) { | |
return details::is_set(levels, lvl); | |
} | |
template<typename ... T> | |
inline static void trace(const char* fmt, T ... args) noexcept { | |
if( enabled(level::trace) ) | |
appender::log(level::trace, fmt, args...); | |
} | |
template<typename ... T> | |
inline static void debug(const char* fmt, T ... args) noexcept { | |
if( enabled(level::debug) ) | |
appender::log(level::debug, fmt, args...); | |
} | |
template<typename ... T> | |
inline static void info(const char* fmt, T ... args) noexcept { | |
if( enabled(level::info) ) | |
appender::log(level::info, fmt, args...); | |
} | |
template<typename ... T> | |
inline static void warn(const char* fmt, T ... args) noexcept { | |
if( enabled(level::warn) ) | |
appender::log(level::warn, fmt, args...); | |
} | |
template<typename ... T> | |
inline static void error(const char* fmt, T ... args) noexcept { | |
if( enabled(level::error) ) | |
appender::log(level::error, fmt, args...); | |
} | |
template<typename ... T> | |
inline static void fail(const char* fmt, T ... args) noexcept { | |
if( enabled(level::fail) ) | |
appender::log(level::fail, fmt, args...); | |
} | |
template<typename Bool, typename ... T> | |
inline static void warn_if(Bool cond, const char* fmt, T ... args) | |
noexcept { | |
if( enabled(level::warn) ) | |
if (cond) | |
appender::log(level::warn, fmt, args...); | |
} | |
template<typename Bool, typename ... T> | |
inline static void error_if(Bool cond, const char* fmt, T ... args) | |
noexcept { | |
if( enabled(level::error) ) | |
if( cond ) | |
appender::log(fmt, args...); | |
} | |
template<typename Bool, typename ... T> | |
inline static void fail_if(Bool cond, const char* fmt, T ... args) | |
noexcept { | |
if( enabled(level::fail) ) | |
if( cond ) | |
appender::log(level::fail, fmt, args...); | |
} | |
private: | |
static constexpr unsigned char levels = class_log_levels<C>::value; | |
}; | |
} | |
#include <miculog.config> | |
#endif /* MICULOG_HPP_ */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment