Skip to content

Instantly share code, notes, and snippets.

@hutorny
Created March 24, 2017 09:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hutorny/6666eb025c6188ad6e30c401af928822 to your computer and use it in GitHub Desktop.
Save hutorny/6666eb025c6188ad6e30c401af928822 to your computer and use it in GitHub Desktop.
Minimalistic C++11 logger with per-user-class configuration
/**
* 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)
// */
/*
* Copyright (C) 2017 Eugene Hutorny <eugene@hutorny.in.ua>
*
* 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);
}
}}
/*
* Copyright (C) 2017 Eugene Hutorny <eugene@hutorny.in.ua>
*
* 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