HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Policy-based, variadic logger class in C++

Submitted by: @import:stackexchange-codereview··
0
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

Solution

First off, you are using std:: for everything else, might as well use it for std::uint32_t and std::size_t

I'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 snprintf to 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.