patterncppMinor
Policy-based, variadic logger class in C++
Viewed 0 times
policyloggervariadicbasedclass
Problem
I've been exploring design based around some of the more advanced C++11 features lately, and some of them are turning out to be rather useful for some projects I'm working on. One is this policy-based, variadic logger class that supports any combination of policies to determine where it logs output to.
Most loggers I've seen that adopt this design pattern only accept one, and there are cases where you would want more, so this expands on that without having to instantiate several loggers each with a distinct policy.
It's also easily extensible, flexible, being able to add additional logging policies and log levels.
```
/! @brief Defines level severities for logging. /
enum class LogLevel : uint32_t
{
Info = 0,
Warning = 1,
Error = 2
};
template
class Logger
{
typedef std::tuple policies_t;
private:
policies_t _policies;
uint32_t _numWarnings;
uint32_t _numErrors;
static const size_t s_MaxPrefixSize = 128;
static const size_t s_MaxBufferSize = 1024;
public:
Logger ()
: _policies { std::forward(Policies{})... }
, _numWarnings(0)
, _numErrors(0)
{}
static const size_t numPolicies = sizeof...(Policies);
template
T* getPolicy ()
{
// Implementation of FindTypeIndex omitted for brevity.
static const auto index = FindTypeIndex::value;
return getPolicy();
}
template
auto getPolicy () -> decltype(&std::get(_policies))
{
static_assert(Index (_policies);
}
template
void log (const char* format, ...)
{
std::time_t time = std::time(nullptr);
std::string timeString = std::ctime(&time);
char prefixString[s_MaxPrefixSize];
snprintf(prefixString, s_MaxPrefixSize, "[ %s ] : %s ",
timeString.substr(0, timeString.size() - 1).c_str(), // Strip off newline character.
logLevelString(Int2Type()).c_str());
va_list args;
va_start(args, form
Most loggers I've seen that adopt this design pattern only accept one, and there are cases where you would want more, so this expands on that without having to instantiate several loggers each with a distinct policy.
It's also easily extensible, flexible, being able to add additional logging policies and log levels.
```
/! @brief Defines level severities for logging. /
enum class LogLevel : uint32_t
{
Info = 0,
Warning = 1,
Error = 2
};
template
class Logger
{
typedef std::tuple policies_t;
private:
policies_t _policies;
uint32_t _numWarnings;
uint32_t _numErrors;
static const size_t s_MaxPrefixSize = 128;
static const size_t s_MaxBufferSize = 1024;
public:
Logger ()
: _policies { std::forward(Policies{})... }
, _numWarnings(0)
, _numErrors(0)
{}
static const size_t numPolicies = sizeof...(Policies);
template
T* getPolicy ()
{
// Implementation of FindTypeIndex omitted for brevity.
static const auto index = FindTypeIndex::value;
return getPolicy();
}
template
auto getPolicy () -> decltype(&std::get(_policies))
{
static_assert(Index (_policies);
}
template
void log (const char* format, ...)
{
std::time_t time = std::time(nullptr);
std::string timeString = std::ctime(&time);
char prefixString[s_MaxPrefixSize];
snprintf(prefixString, s_MaxPrefixSize, "[ %s ] : %s ",
timeString.substr(0, timeString.size() - 1).c_str(), // Strip off newline character.
logLevelString(Int2Type()).c_str());
va_list args;
va_start(args, form
Solution
First off, you are using
I'm not very familiar with variadic templates, but I feel like dropping into C's varargs is not the way to go.
About the Log levels, is it possible to add levels without access to the header? The way
So this some code for the way I might try to do it. The main thing is that I don't use varargs:
std:: for everything else, might as well use it for std::uint32_t and std::size_tI'm not very familiar with variadic templates, but I feel like dropping into C's varargs is not the way to go.
- You have to do the messy
snprintfto make the prefix (which now means you need the max prefix length)
- Only primitive types can be logged.
About the Log levels, is it possible to add levels without access to the header? The way
logLevelString() works is beyond me.So this some code for the way I might try to do it. The main thing is that I don't use varargs:
#include
#include
#include
#include
struct LogLevel
{
const std::string name;
LogLevel( const std::string& n ): name( n ){}
virtual ~LogLevel( void ) = default;
};
struct Info : public LogLevel
{
Info( void ):LogLevel( "Info" ){}
};
template
void print( T only )
{
std::cout
void print( T current, args... next )
{
std::cout
void print( args ... to_print )
{
std::cout ( 1, "this", 1.2, p );
return 0;
}Code Snippets
#include <iostream>
#include <cstdint>
#include <string>
#include <ctime>
struct LogLevel
{
const std::string name;
LogLevel( const std::string& n ): name( n ){}
virtual ~LogLevel( void ) = default;
};
struct Info : public LogLevel
{
Info( void ):LogLevel( "Info" ){}
};
template <typename T >
void print( T only )
{
std::cout << only << std::endl;
}
template <typename T, typename ... args >
void print( T current, args... next )
{
std::cout << current << ' ';
print( next... );
}
template < typename Level, typename ... args >
void print( args ... to_print )
{
std::cout << Level().name << ": ";
std::time_t time = std::time(nullptr);
std::string time_str = std::ctime( &time );
time_str.pop_back();
std::cout << time_str << ": ";
print( to_print... );
}
struct Point
{
int x; int y;
friend std::ostream& operator<<( std::ostream& out, const Point& p )
{ return out << '{' << p.x << ',' << p.y << '}'; }
};
int main( void )
{
Point p = { 1, 2 };
print< Info >( 1, "this", 1.2, p );
return 0;
}Context
StackExchange Code Review Q#62830, answer score: 4
Revisions (0)
No revisions yet.