From 04d8d3826d411d61f11a930e4ffcc03e136992ca Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Mon, 8 Jun 2015 13:20:28 +0200 Subject: [PATCH] add: Import Logging functionality by Florian Weber Original source at https://github.com/Florianjw/simple_logger/ Minor modifications and source formatting have been applied --- Makefile | 2 +- src/log/logger.cpp | 146 +++++++++++++++++++++++++++++++++++++++++++++ src/log/logger.hpp | 97 ++++++++++++++++++++++++++++++ 3 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 src/log/logger.cpp create mode 100644 src/log/logger.hpp diff --git a/Makefile b/Makefile index a22b766..562abb5 100644 --- a/Makefile +++ b/Makefile @@ -46,7 +46,7 @@ SRC_DIR=src OBJ_DIR=obj DEP_DIR=dep -FS_SRC=$(wildcard ${SRC_DIR}/*.cpp) $(wildcard ${SRC_DIR}/io/*.cpp) $(wildcard ${SRC_DIR}/crypto/*.cpp) $(wildcard ${SRC_DIR}/db/*.cpp) +FS_SRC=$(wildcard ${SRC_DIR}/*.cpp) $(wildcard ${SRC_DIR}/log/*.cpp) $(wildcard ${SRC_DIR}/io/*.cpp) $(wildcard ${SRC_DIR}/crypto/*.cpp) $(wildcard ${SRC_DIR}/db/*.cpp) FS_BIN=$(wildcard ${SRC_DIR}/app/*.cpp) FS_LIBS=$(wildcard lib/*/) diff --git a/src/log/logger.cpp b/src/log/logger.cpp new file mode 100644 index 0000000..9a70300 --- /dev/null +++ b/src/log/logger.cpp @@ -0,0 +1,146 @@ +#include "log/logger.hpp" + +#include +#include +#include +#include +#include + +namespace logger { + + namespace { + + std::ostream*& ostream_pointer() { + static std::ostream* stream = &std::cout; + return stream; + } + + std::ostream& get_stream() { + return *ostream_pointer(); + } + + std::string make_prefix( level l ) { + auto prefix = std::string {}; + + switch( l ) { + case level::debug: + prefix = "[debug]["; + break; + + case level::note: + prefix = "[note ]["; + break; + + case level::warn: + prefix = "[warn ]["; + break; + + case level::error: + prefix = "[error]["; + break; + + case level::fatal: + prefix = "[fatal]["; + break; + } + + using clock = std::chrono::system_clock; + const auto now = clock::to_time_t( clock::now() ); + // ctime appends a newline, we don't want that here: + auto time_str = std::ctime( &now ); + prefix.append( time_str, time_str + std::strlen( time_str ) - 1 ); + prefix += "]: "; + return prefix; + } + + } // anonymous namespace + + namespace impl { + + std::string replace_newlines( const std::string& str, std::size_t length ) { + auto returnstring = std::string {}; + auto it = str.begin(); + const auto end = str.end(); + auto nl_it = it; + + while( ( nl_it = std::find( it, end, '\n' ) ) != end ) { + ++nl_it; + returnstring.append( it, nl_it ); + returnstring.append( length, ' ' ); + it = nl_it; + } + + returnstring.append( it, end ); + return returnstring; + } + + void log( level l, const std::vector& args ) { + const auto prefix = make_prefix( l ); + const auto length = prefix.length(); + get_stream() << prefix; + std::transform( args.begin(), args.end(), std::ostream_iterator {get_stream()}, + [length]( const std::string & str ) { + return replace_newlines( str, length ); + } ); + get_stream() << '\n' << std::flush; + } + + void logf( level l, const std::string& format, std::vector args ) { + const auto prefix = make_prefix( l ); + const auto length = prefix.length(); + const auto fmt = replace_newlines( format, length ); + std::transform( args.begin(), args.end(), args.begin(), + [length]( const std::string & str ) { + return replace_newlines( str, length ); + } ); + + auto mesg = prefix; + auto arg_index = std::size_t {0}; + auto it = fmt.begin(); + const auto end = fmt.end(); + + while( it != end ) { + auto pos = std::find( it, end, '%' ); + mesg.append( it, pos ); + + if( pos == end ) { + break; + } + + ++pos; + + if( pos == end ) { + throw std::invalid_argument {"Invalid formatstring (ends on single '%')"}; + } + + switch( *pos ) { + case '%': + mesg.push_back( '%' ); + break; + + case 's': + if( arg_index >= args.size() ) { + throw std::invalid_argument {"Invalid formatstring (not enough arguments)"}; + } + + mesg.append( args[arg_index++] ); + break; + + default: + throw std::invalid_argument {"Invalid formatstring (unknown format-character)"}; + } + + it = std::next( pos ); + } + + mesg.push_back( '\n' ); + get_stream() << mesg << std::flush; + } + + } // namespace impl + + void set_stream( std::ostream& stream ) { + ostream_pointer() = &stream; + } + +} // namespace logger diff --git a/src/log/logger.hpp b/src/log/logger.hpp new file mode 100644 index 0000000..6d1e5e5 --- /dev/null +++ b/src/log/logger.hpp @@ -0,0 +1,97 @@ +#pragma once + +#include +#include +#include +#include + +namespace logger { + + enum class level { + debug, note, warn, error, fatal + }; + + /** + * conv::to_string will be used to convert whatever argument is send + * to the logger to a string. If another type shall be supported, + * just add the appropriate overload and you can start using it right + * away. The default conversion will use a stringstream. + */ + namespace conv { + template + inline std::string to_string( const T& arg ) { + std::ostringstream stream; + stream << arg; + return stream.str(); + } + inline std::string to_string( std::string&& arg ) { + return arg; + } + inline std::string to_string( const std::string& arg ) { + return arg; + } + inline std::string to_string( const char* arg ) { + return arg; + } + } + + namespace impl { + void log( level, const std::vector& args ); + void logf( level, const std::string&, std::vector args ); + } + + void set_stream( std::ostream& ); + + template + void log( level l, Args&& ... data ) { + impl::log( l, {conv::to_string( std::forward( data ) )...} ); + } + + template + void logf( level l, const std::string& format, Args&& ... data ) { + impl::logf( l, format, {conv::to_string( std::forward( data ) )...} ); + } + + template + void debug( Args&& ... args ) { + log( level::debug, std::forward( args )... ); + } + template + void note( Args&& ... args ) { + log( level::note, std::forward( args )... ); + } + template + void warn( Args&& ... args ) { + log( level::warn, std::forward( args )... ); + } + template + void error( Args&& ... args ) { + log( level::error, std::forward( args )... ); + } + template + void fatal( Args&& ... args ) { + log( level::fatal, std::forward( args )... ); + } + + template + void debugf( const std::string& fmt, Args&& ... args ) { + logf( level::debug, fmt, std::forward( args )... ); + } + template + void notef( const std::string& fmt, Args&& ... args ) { + logf( level::note, fmt, std::forward( args )... ); + } + template + void warnf( const std::string& fmt, Args&& ... args ) { + logf( level::warn, fmt, std::forward( args )... ); + } + template + void errorf( const std::string& fmt, Args&& ... args ) { + logf( level::error, fmt, std::forward( args )... ); + } + template + void fatalf( const std::string& fmt, Args&& ... args ) { + logf( level::fatal, fmt, std::forward( args )... ); + } + +} -- 2.39.2