Adding updated logging system similar to log4

Updates to ssh client
Updated reflect enum
This commit is contained in:
Daniel Larimer 2013-02-04 23:08:48 -05:00
parent b7f1f7bdc7
commit 71ea16cf8b
15 changed files with 657 additions and 16 deletions

View file

@ -49,6 +49,9 @@ include_directories( ${OPENSSL_INCLUDE_DIR} )
include_directories( include ) include_directories( include )
set( sources set( sources
src/logger.cpp
src/appender.cpp
src/logger_config.cpp
src/ssh.cpp src/ssh.cpp
src/url.cpp src/url.cpp
src/process.cpp src/process.cpp
@ -105,6 +108,8 @@ setup_library( fc SOURCES ${sources} )
#setup_executable( json_rpc_test SOURCES tests/json_rpc_test.cpp LIBRARIES fc ${ZLIB_LIBRARY} ${pthread_library} ${rt_library} ${Boost_THREAD_LIBRARY} ${Boost_CONTEXT_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_CHRONO_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${rt_library} ${Boost_DATE_TIME_LIBRARY}) #setup_executable( json_rpc_test SOURCES tests/json_rpc_test.cpp LIBRARIES fc ${ZLIB_LIBRARY} ${pthread_library} ${rt_library} ${Boost_THREAD_LIBRARY} ${Boost_CONTEXT_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_CHRONO_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${rt_library} ${Boost_DATE_TIME_LIBRARY})
#setup_executable( ssh_test SOURCES tests/ssh.cpp LIBRARIES fc ${pthread_library} ${rt_library} ${Boost_THREAD_LIBRARY} ${Boost_CONTEXT_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_CHRONO_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${rt_library} ssh2 ${OPENSSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY} ${ZLIB_LIBRARY} ${ALL_OPENSSL_LIBRARIES} ${Boost_DATE_TIME_LIBRARY}) #setup_executable( ssh_test SOURCES tests/ssh.cpp LIBRARIES fc ${pthread_library} ${rt_library} ${Boost_THREAD_LIBRARY} ${Boost_CONTEXT_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_CHRONO_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${rt_library} ssh2 ${OPENSSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY} ${ZLIB_LIBRARY} ${ALL_OPENSSL_LIBRARIES} ${Boost_DATE_TIME_LIBRARY})
setup_executable( logger_test SOURCES tests/logger.cpp LIBRARIES fc ${pthread_library} ${rt_library} ${Boost_THREAD_LIBRARY} ${Boost_CONTEXT_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_CHRONO_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${rt_library} ssh2 ${OPENSSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY} ${ZLIB_LIBRARY} ${ALL_OPENSSL_LIBRARIES} ${Boost_DATE_TIME_LIBRARY})
#add_executable( test_vec tests/vector_test.cpp ) #add_executable( test_vec tests/vector_test.cpp )
#target_link_libraries( test_vec fc ${Boost_SYSTEM_LIBRARY} ${Boost_CHRONO_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_CONTEXT_LIBRARY} ) #target_link_libraries( test_vec fc ${Boost_SYSTEM_LIBRARY} ${Boost_CHRONO_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_CONTEXT_LIBRARY} )

43
include/fc/appender.hpp Normal file
View file

@ -0,0 +1,43 @@
#pragma once
#include <fc/shared_ptr.hpp>
namespace fc {
class appender;
class log_message;
class value;
class string;
class appender_factory : public fc::retainable {
public:
typedef fc::shared_ptr<appender_factory> ptr;
virtual ~appender_factory(){};
virtual fc::shared_ptr<appender> create( const value& args ) = 0;
};
namespace detail {
template<typename T>
class appender_factory_impl : public appender_factory {
public:
virtual fc::shared_ptr<appender> create( const value& args ) {
return fc::shared_ptr<appender>(new T(args));
}
};
}
class appender : public fc::retainable {
public:
typedef fc::shared_ptr<appender> ptr;
template<typename T>
static bool register_appender(const fc::string& type) {
return register_appender( type, new detail::appender_factory_impl<T>() );
}
static appender::ptr create( const fc::string& name, const fc::string& type, const value& args );
static appender::ptr get( const fc::string& name );
static bool register_appender( const fc::string& type, const appender_factory::ptr& f );
virtual void log( const log_message& m ) = 0;
};
}

View file

@ -41,6 +41,7 @@ namespace fc {
fc::path parent_path()const; fc::path parent_path()const;
fc::string string()const; fc::string string()const;
fc::string generic_string()const; fc::string generic_string()const;
bool is_relative()const; bool is_relative()const;
bool is_absolute()const; bool is_absolute()const;
private: private:

138
include/fc/logger.hpp Normal file
View file

@ -0,0 +1,138 @@
#pragma once
#include <fc/value.hpp>
#include <fc/string.hpp>
#include <fc/time.hpp>
#include <fc/shared_ptr.hpp>
namespace fc {
struct log_level {
enum type {
all, trace, debug, info, warn, error, fatal, off
};
};
struct log_message {
log_message(log_level::type, const string& file, int line, const string& func, const string& format );
log_message();
otime_point when;
log_level::type level;
ostring context;
ostring thread;
ostring fiber;
string file;
int line;
string method;
string format;
value args;
ovalue meta;
// key based args
log_message& operator()( const string& arg, value&& v );
log_message& operator()( const string& arg, const value& v );
// position based args...
log_message& operator()( value&& v );
log_message& operator()( const value& v );
};
class appender;
/**
*
*
@code
void my_class::func()
{
fc_dlog( my_class_logger, "Format four: ${arg} five: ${five}", ("arg",4)("five",5) );
}
@endcode
*/
class logger {
public:
static logger get( const fc::string& name = "default");
logger();
logger( const string& name, const logger& parent = nullptr );
logger( std::nullptr_t );
logger( const logger& c );
logger( logger&& c );
~logger();
logger& operator=(const logger&);
logger& operator=(logger&&);
friend bool operator==( const logger&, std::nullptr_t );
friend bool operator!=( const logger&, std::nullptr_t );
logger& set_log_level( log_level::type e );
log_level::type get_log_level()const;
logger& set_parent( const logger& l );
logger get_parent()const;
void set_name( const fc::string& n );
const fc::string& name()const;
void add_appender( const fc::shared_ptr<appender>& a );
bool is_enabled( log_level::type e )const;
void log( log_message m );
private:
class impl;
fc::shared_ptr<impl> my;
};
/**
* This helper class is used to automatically print a log message
* once upon construction, and again upon destruction and is therefore
* helpful in catching scope changes.
struct tracer {
tracer( const logger::ptr& lgr );
~tracer();
void set_message( log_message&& ms g);
private:
logger::ptr logger;
log_message msg;
};
*/
} // namespace fc
#include <fc/reflect.hpp>
FC_REFLECT( fc::log_message, (when)(level)(context)(thread)(method)(file)(line)(format)(args)(meta) )
FC_REFLECT_ENUM( fc::log_level::type, (all)(trace)(debug)(info)(warn)(error)(fatal)(off) )
#define fc_scope_log( LOGGER, FORMAT, ... ) \
fc::tracer __tracer; \
if( (LOGGER).is_enabled( fc::log_level::trace ) ) { \
__tracer.set_message( fc::log_message( fc::log_level::trace, __FILE__, __LINE__, __func__, FORMAT ) __VA_ARGS__ );\
}
#define fc_dlog( LOGGER, FORMAT, ... ) \
if( (LOGGER).is_enabled( fc::log_level::debug ) ) { \
(LOGGER).log( fc::log_message( fc::log_level::debug, __FILE__, __LINE__, __func__, FORMAT ) __VA_ARGS__ );\
}
#define fc_ilog( LOGGER, FORMAT, ... ) \
if( (LOGGER).is_enabled( fc::log_level::info ) ) { \
(LOGGER).log( fc::log_message( fc::log_level::info, __FILE__, __LINE__, __func__, FORMAT ) __VA_ARGS__ );\
}
#define fc_wlog( LOGGER, FORMAT, ... ) \
if( (LOGGER).is_enabled( fc::log_level::warn ) ) { \
(LOGGER).log( fc::log_message( fc::log_level::warn, __FILE__, __LINE__, __func__, FORMAT ) __VA_ARGS__ );\
}
#define fc_elog( LOGGER, FORMAT, ... ) \
if( (LOGGER).is_enabled( fc::log_level::error ) ) { \
(LOGGER).log( fc::log_message( fc::log_level::error, __FILE__, __LINE__, __func__, FORMAT ) __VA_ARGS__ );\
}
#define fc_flog( LOGGER, FORMAT, ... ) \
if( (LOGGER).is_enabled( fc::log_level::fatal ) ) { \
(LOGGER).log( fc::log_message( fc::log_level::fatal, __FILE__, __LINE__, __func__, FORMAT ) __VA_ARGS__ );\
}

View file

@ -0,0 +1,41 @@
#pragma once
#include <fc/logger.hpp>
namespace fc {
class path;
struct appender_config {
appender_config(const fc::string& n="",const fc::string& t="", const value& a=value())
:name(n),type(t),args(a),enabled(true){}
string name;
string type;
value args;
bool enabled;
};
struct logger_config {
logger_config(const fc::string& n=""):name(n),enabled(true),additivity(false){}
string name;
ostring parent;
/// if not set, then parents level is used.
fc::optional<log_level::type> level;
bool enabled;
/// if any appenders are sepecified, then parent's appenders are not set.
bool additivity;
fc::vector<string> appenders;
};
struct logging_config {
static logging_config default_config();
fc::vector<string> includes;
fc::vector<appender_config> appenders;
fc::vector<logger_config> loggers;
};
void configure_logging( const fc::path& log_config );
bool configure_logging( const logging_config& l );
}
#include <fc/reflect.hpp>
FC_REFLECT( fc::appender_config, (name)(type)(args)(enabled) )
FC_REFLECT( fc::logger_config, (name)(parent)(level)(enabled)(additivity)(appenders) )
FC_REFLECT( fc::logging_config, (includes)(appenders)(loggers) )

View file

@ -105,13 +105,13 @@ void fc::reflector<TYPE>::visit( const Visitor& v ) { \
#endif // DOXYGEN #endif // DOXYGEN
#define FC_REFLECT_VISIT_ENUM( r, visitor, elem ) \ #define FC_REFLECT_VISIT_ENUM( r, enum_type, elem ) \
visitor.TEMPLATE operator()<elem>(BOOST_PP_STRINGIZE(elem)); v.TEMPLATE operator()<enum_type::elem>(BOOST_PP_STRINGIZE(elem));
#define FC_REFLECT_ENUM_TO_STRING( r, visitor, elem ) \ #define FC_REFLECT_ENUM_TO_STRING( r, enum_type, elem ) \
case elem: return BOOST_PP_STRINGIZE(elem); case enum_type::elem: return BOOST_PP_STRINGIZE(elem);
#define FC_REFLECT_ENUM_FROM_STRING( r, visitor, elem ) \ #define FC_REFLECT_ENUM_FROM_STRING( r, enum_type, elem ) \
if( strcmp( s, BOOST_PP_STRINGIZE(elem) ) == 0 ) return elem; if( strcmp( s, BOOST_PP_STRINGIZE(elem) ) == 0 ) return enum_type::elem;
#define FC_REFLECT_ENUM( ENUM, FIELDS ) \ #define FC_REFLECT_ENUM( ENUM, FIELDS ) \
namespace fc { \ namespace fc { \
@ -120,18 +120,21 @@ template<> struct reflector<ENUM> { \
typedef fc::true_type is_enum; \ typedef fc::true_type is_enum; \
template<typename Visitor> \ template<typename Visitor> \
static inline void visit( const Visitor& v ) { \ static inline void visit( const Visitor& v ) { \
BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_VISIT_ENUM, v, FIELDS ) \ BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_VISIT_ENUM, ENUM, FIELDS ) \
}\ }\
static const char* to_string(int64_t i) { \ static const char* to_string(int64_t i) { \
switch( ENUM(i) ) { \ switch( ENUM(i) ) { \
BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_ENUM_TO_STRING, v, FIELDS ) \ BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_ENUM_TO_STRING, ENUM, FIELDS ) \
default: \ default: \
FC_THROW_MSG( "Unknown field %s not in enum '%s'", i, BOOST_PP_STRINGIZE(ENUM) ); \ FC_THROW_REPORT( "Unknown field ${field} not in enum ${enum}", \
fc::value().set("field",i).set("enum",BOOST_PP_STRINGIZE(ENUM)) ); \
}\ }\
return nullptr; \
} \ } \
static ENUM from_string( const char* s ) { \ static ENUM from_string( const char* s ) { \
BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_ENUM_FROM_STRING, v, FIELDS ) \ BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_ENUM_FROM_STRING, ENUM, FIELDS ) \
FC_THROW_MSG( "Unknown field %s not in enum '%s'", s, BOOST_PP_STRINGIZE(ENUM) ); \ FC_THROW_REPORT( "Unknown field ${field} not in enum ${enum}", \
fc::value().set("field",s).set("enum",BOOST_PP_STRINGIZE(ENUM)) ); \
} \ } \
}; \ }; \
} }

View file

@ -88,6 +88,16 @@ namespace fc {
void scp_send( const fc::path& local_path, const fc::path& remote_path, void scp_send( const fc::path& local_path, const fc::path& remote_path,
std::function<bool(size_t,size_t)> progress = [](size_t,size_t){return true;} ); std::function<bool(size_t,size_t)> progress = [](size_t,size_t){return true;} );
/**
* @brief recursively sends the contents of local_dir to the remote_path
*
* If remote_path ends in '/' then a new directory at <code>remote_path/local_dir.filename()</code> will
* be created, otherwise <code>local_dir / *</code> will be copied to <code>remote_path / *</code>
*
* Progress will be reported as total bytes transferred for all files.
*/
void scp_send_dir( const fc::path& local_dir, const fc::path& remote_path,
std::function<bool(size_t,size_t)> progress = [](size_t,size_t){return true;} );
/** /**
* @pre remote_path is not a directory * @pre remote_path is not a directory

View file

@ -4,6 +4,7 @@
#include <fc/vector.hpp> #include <fc/vector.hpp>
#include <fc/aligned.hpp> #include <fc/aligned.hpp>
#include <fc/typename.hpp> #include <fc/typename.hpp>
#include <fc/optional.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp> #include <boost/preprocessor/repetition/enum_params.hpp>
namespace fc { namespace fc {
@ -131,7 +132,8 @@ namespace fc {
/** array interface **/ /** array interface **/
void resize( size_t s ); void resize( size_t s );
void reserve( size_t s ); void reserve( size_t s );
void push_back( value&& v ); value& push_back( value&& v );
value& push_back( const value& v );
value& operator[]( int32_t idx ); value& operator[]( int32_t idx );
const value& operator[]( int32_t idx )const; const value& operator[]( int32_t idx )const;
@ -163,6 +165,7 @@ namespace fc {
aligned<40> holder; aligned<40> holder;
}; };
typedef fc::optional<value> ovalue;
bool operator == ( const value& v, std::nullptr_t ); bool operator == ( const value& v, std::nullptr_t );
bool operator != ( const value& v, std::nullptr_t ); bool operator != ( const value& v, std::nullptr_t );

View file

@ -172,7 +172,7 @@ namespace fc {
v = fc::reflector<T>::from_string( fc::value_cast<fc::string>(jsv).c_str() ); v = fc::reflector<T>::from_string( fc::value_cast<fc::string>(jsv).c_str() );
} else { } else {
// throw if invalid int, by attempting to convert to string // throw if invalid int, by attempting to convert to string
fc::reflector<T>::to_string( v = value_cast<int64_t>(jsv) ); fc::reflector<T>::to_string( v = static_cast<T>(value_cast<int64_t>(jsv)) );
} }
} }
}; };

146
src/appender.cpp Normal file
View file

@ -0,0 +1,146 @@
#include <fc/appender.hpp>
#include <fc/logger.hpp>
#include <fc/value.hpp>
#include <fc/unique_lock.hpp>
#include <unordered_map>
#include <string>
#include <fc/spin_lock.hpp>
#include <fc/scoped_lock.hpp>
#include <fc/console_defines.h>
#include <fc/log.hpp>
#include <fc/value_cast.hpp>
#include <boost/thread/mutex.hpp>
#ifndef WIN32
#include <unistd.h>
#endif
namespace fc {
static fc::spin_lock appender_spinlock;
std::unordered_map<std::string,appender::ptr>& get_appender_map() {
static std::unordered_map<std::string,appender::ptr> lm;
return lm;
}
std::unordered_map<std::string,appender_factory::ptr>& get_appender_factory_map() {
static std::unordered_map<std::string,appender_factory::ptr> lm;
return lm;
}
appender::ptr appender::get( const fc::string& s ) {
scoped_lock<spin_lock> lock(appender_spinlock);
return get_appender_map()[s];
}
bool appender::register_appender( const fc::string& type, const appender_factory::ptr& f )
{
get_appender_factory_map()[type] = f;
return true;
}
appender::ptr appender::create( const fc::string& name, const fc::string& type, const value& args )
{
auto fact_itr = get_appender_factory_map().find(type);
if( fact_itr == get_appender_factory_map().end() ) {
wlog( "Unknown appender type '%s'", type.c_str() );
return appender::ptr();
}
auto ap = fact_itr->second->create( args );
get_appender_map()[name] = ap;
return ap;
}
class console_appender : public appender{
public:
struct color {
enum type {
red,
green,
brown,
blue,
magenta,
cyan,
white,
console_default,
};
};
struct stream { enum type { std_out, std_error }; };
struct level_color {
level_color( log_level::type l=log_level::all,
color::type c=color::console_default )
:level(l),color(c){}
log_level::type level;
console_appender::color::type color;
};
struct config {
config()
:format( "${when} ${thread} ${context} ${file}:${line} ${method} ${level}] ${message}" ),
stream(console_appender::stream::std_error),flush(true){}
fc::string format;
console_appender::stream::type stream;
fc::vector<level_color> level_colors;
bool flush;
};
config cfg;
color::type lc[log_level::off+1];
console_appender( const value& args );
const char* get_color(color::type t ) {
switch( t ) {
case color::red: return CONSOLE_RED;
case color::green: return CONSOLE_GREEN;
case color::brown: return CONSOLE_BROWN;
case color::blue: return CONSOLE_BLUE;
case color::magenta: return CONSOLE_MAGENTA;
case color::cyan: return CONSOLE_CYAN;
case color::white: return CONSOLE_WHITE;
case color::console_default:
default:
return CONSOLE_DEFAULT;
}
}
const char* get_color( log_level::type l ) {
return get_color( lc[l] );
}
virtual void log( const log_message& m ) {
fc::string message = fc::substitute( m.format, m.args );
fc::value lmsg(m);
FILE* out = stream::std_error ? stderr : stdout;
fc::string fmt_str = fc::substitute( cfg.format, value(m).set( "message", message) );
fc::unique_lock<boost::mutex> lock(log_mutex());
#ifndef WIN32
if(isatty(fileno(out))) fprintf( out, "\r%s", get_color( m.level ) );
#endif
fprintf( out, "%s", fmt_str.c_str() );
#ifndef WIN32
if(isatty(fileno(out))) fprintf( out, "\r%s", CONSOLE_DEFAULT );
#endif
fprintf( out, "\n" );
if( cfg.flush ) fflush( out );
}
};
} // namespace fc
FC_REFLECT_ENUM( fc::console_appender::stream::type, (std_out)(std_error) )
FC_REFLECT_ENUM( fc::console_appender::color::type, (red)(green)(brown)(blue)(magenta)(cyan)(white)(console_default) )
FC_REFLECT( fc::console_appender::level_color, (level)(color) )
FC_REFLECT( fc::console_appender::config, (format)(stream)(level_colors)(flush) )
namespace fc {
console_appender::console_appender( const value& args ) {
cfg = fc::value_cast<config>(args);
for( int i = 0; i < log_level::off+1; ++i )
lc[i] = color::console_default;
for( auto itr = cfg.level_colors.begin(); itr != cfg.level_colors.end(); ++itr )
lc[itr->level] = itr->color;
}
static bool reg_console_appender = appender::register_appender<console_appender>( "console" );
} // namespace fc

110
src/logger.cpp Normal file
View file

@ -0,0 +1,110 @@
#include <fc/logger.hpp>
#include <fc/thread.hpp>
#include <fc/spin_lock.hpp>
#include <fc/scoped_lock.hpp>
#include <fc/appender.hpp>
#include <fc/filesystem.hpp>
#include <unordered_map>
#include <string>
// tmp...
#include <fc/json.hpp>
namespace fc {
log_message::log_message(){}
log_message::log_message(log_level::type ll, const string& f, int l, const string& fun, const string& fmt )
:when( fc::time_point::now() ), level(ll), thread( fc::thread::current().name() ),file(fc::path(f).filename().generic_string()),line(l),method(fun),format(fmt){}
log_message& log_message::operator()( const fc::string& k, fc::value&& v ) {
args[k] = fc::move(v);
return *this;
}
log_message& log_message::operator()( fc::value&& v ) {
args.push_back( fc::move(v) );
return *this;
}
log_message& log_message::operator()( const fc::string& k, const fc::value& v ) {
args[k] = v;
return *this;
}
log_message& log_message::operator()( const fc::value& v ) {
args.push_back( v );
return *this;
}
class logger::impl : public fc::retainable {
public:
impl()
:_parent(nullptr),_enabled(true),_level(log_level::warn){}
fc::string _name;
logger _parent;
bool _enabled;
bool _additivity;
log_level::type _level;
fc::vector<appender::ptr> _appenders;
};
logger::logger()
:my( new impl() ){}
logger::logger(std::nullptr_t){}
logger::logger( const logger& l )
:my(l.my){}
logger::logger( logger&& l )
:my(fc::move(l.my)){}
logger::~logger(){}
logger& logger::operator=( const logger& l ){
my = l.my;
return *this;
}
logger& logger::operator=( logger&& l ){
fc_swap(my,l.my);
return *this;
}
bool operator==( const logger& l, std::nullptr_t ) { return !l.my; }
bool operator!=( const logger& l, std::nullptr_t ) { return l.my; }
bool logger::is_enabled( log_level::type e )const {
return e >= my->_level;
}
void logger::log( log_message m ) {
m.context = my->_name;
for( auto itr = my->_appenders.begin(); itr != my->_appenders.end(); ++itr )
(*itr)->log( m );
if( my->_additivity && my->_parent != nullptr) {
my->_parent.log(m);
}
}
void logger::set_name( const fc::string& n ) { my->_name = n; }
const fc::string& logger::name()const { return my->_name; }
static fc::spin_lock logger_spinlock;
std::unordered_map<std::string,logger>& get_logger_map() {
static std::unordered_map<std::string,logger> lm;
return lm;
}
logger logger::get( const fc::string& s ) {
scoped_lock<spin_lock> lock(logger_spinlock);
return get_logger_map()[s];
}
logger logger::get_parent()const { return my->_parent; }
logger& logger::set_parent(const logger& p) { my->_parent = p; return *this; }
log_level::type logger::get_log_level()const { return my->_level; }
logger& logger::set_log_level(log_level::type ll) { my->_level = ll; return *this; }
void logger::add_appender( const fc::shared_ptr<appender>& a )
{ my->_appenders.push_back(a); }
} // namespace fc

77
src/logger_config.cpp Normal file
View file

@ -0,0 +1,77 @@
#include <fc/logger_config.hpp>
#include <fc/appender.hpp>
#include <fc/json.hpp>
#include <fc/filesystem.hpp>
#include <unordered_map>
#include <string>
namespace fc {
std::unordered_map<std::string,logger>& get_logger_map();
std::unordered_map<std::string,appender::ptr>& get_appender_map();
void configure_logging( const fc::path& lc )
{
configure_logging( fc::json::from_file<logging_config>(lc) );
}
bool configure_logging( const logging_config& cfg )
{
get_logger_map().clear();
get_appender_map().clear();
slog( "\n%s", fc::json::to_pretty_string(cfg).c_str() );
for( size_t i = 0; i < cfg.appenders.size(); ++i ) {
appender::create( cfg.appenders[i].name, cfg.appenders[i].type, cfg.appenders[i].args );
// TODO... process enabled
}
for( size_t i = 0; i < cfg.loggers.size(); ++i ) {
auto lgr = logger::get( cfg.loggers[i].name );
// TODO: configure logger here...
lgr.set_name(cfg.loggers[i].name);
lgr.set_log_level( *cfg.loggers[i].level );
for( auto a = cfg.loggers[i].appenders.begin();
a != cfg.loggers[i].appenders.end(); ++a ){
auto ap = appender::get( *a );
if( ap ) { lgr.add_appender(ap); }
}
}
return true;
}
logging_config logging_config::default_config() {
slog( "default cfg" );
logging_config cfg;
cfg.appenders.push_back(
appender_config( "stderr", "console",
fc::value()
.set( "stream","std_error")
.set( "level_colors",
fc::value().push_back( fc::value().set( "level","debug").set("color", "green") )
.push_back( fc::value().set( "level","warn").set("color", "brown") )
.push_back( fc::value().set( "level","error").set("color", "red") )
.push_back( fc::value().set( "level","fatal").set("color", "red") )
)
) );
cfg.appenders.push_back(
appender_config( "stdout", "console",
fc::value()
.set( "stream","std_out")
.set( "level_colors",
fc::value().push_back( fc::value().set( "level","debug").set("color", "green") )
.push_back( fc::value().set( "level","warn").set("color", "brown") )
.push_back( fc::value().set( "level","error").set("color", "red") )
.push_back( fc::value().set( "level","fatal").set("color", "red") )
)
) );
logger_config dlc;
dlc.name = "default";
dlc.level = log_level::debug;
dlc.appenders.push_back("stderr");
cfg.loggers.push_back( dlc );
return cfg;
}
static bool do_default_config = configure_logging( logging_config::default_config() );
}

View file

@ -450,8 +450,41 @@ namespace fc { namespace ssh {
return ssh::process( *this, cmd, pty_type ); return ssh::process( *this, cmd, pty_type );
} }
/**
* @todo implement progress reporting.
*/
void client::scp_send_dir( const fc::path& local_dir, const fc::path& remote_path,
std::function<bool(size_t,size_t)> progress )
{
fc::path remote_dir = remote_path;
if( remote_dir.filename() == fc::path(".") )
remote_dir /= local_dir.filename();
slog( "scp -r %s %s", local_dir.generic_string().c_str(), remote_dir.generic_string().c_str() );
create_directories( remote_dir );
directory_iterator ditr(local_dir);
directory_iterator dend;
while( ditr != dend ) {
if( (*ditr).filename() == "." ||
(*ditr).filename() == ".." )
{ }
else if( fc::is_directory(*ditr) )
{
scp_send_dir( (*ditr), remote_dir / (*ditr).filename() );
} else if( fc::is_regular_file(*ditr) ) {
scp_send( *ditr, remote_dir / (*ditr).filename() );
} else {
wlog( "Skipping %s", fc::canonical(*ditr).generic_string().c_str() );
}
++ditr;
}
}
void client::scp_send( const fc::path& local_path, const fc::path& remote_path, void client::scp_send( const fc::path& local_path, const fc::path& remote_path,
std::function<bool(size_t,size_t)> progress ) { std::function<bool(size_t,size_t)> progress ) {
slog( "scp %s %s", local_path.generic_string().c_str(), remote_path.generic_string().c_str() );
/** /**
* Tests have shown that if one scp is 'blocked' by a need to read (presumably to * Tests have shown that if one scp is 'blocked' by a need to read (presumably to
* ack recv for the trx window), and then a second transfer begins that the first * ack recv for the trx window), and then a second transfer begins that the first
@ -471,15 +504,21 @@ namespace fc { namespace ssh {
} }
// memory map the file // memory map the file
file_mapping fmap( local_path.string().c_str(), read_only );
size_t fsize = file_size(local_path); size_t fsize = file_size(local_path);
if( fsize == 0 ) {
// TODO: handle empty file case
if( progress ) progress(0,0);
return;
}
file_mapping fmap( local_path.string().c_str(), read_only );
mapped_region mr( fmap, fc::read_only, 0, fsize ); mapped_region mr( fmap, fc::read_only, 0, fsize );
LIBSSH2_CHANNEL* chan = 0; LIBSSH2_CHANNEL* chan = 0;
time_t now; time_t now;
memset( &now, 0, sizeof(now) ); memset( &now, 0, sizeof(now) );
// TODO: preserve creation / modification date // TODO: preserve creation / modification date
// TODO: perserve permissions / exec bit?
chan = libssh2_scp_send64( my->session, remote_path.generic_string().c_str(), 0700, fsize, now, now ); chan = libssh2_scp_send64( my->session, remote_path.generic_string().c_str(), 0700, fsize, now, now );
while( chan == 0 ) { while( chan == 0 ) {
char* msg; char* msg;

View file

@ -391,8 +391,21 @@ void value::resize( size_t s ) {
void value::reserve( size_t s ) { void value::reserve( size_t s ) {
gh(holder)->reserve(s); gh(holder)->reserve(s);
} }
void value::push_back( value&& v ) { value& value::push_back( value&& v ) {
if (strcmp(gh(holder)->type(), "void" ) == 0 ) {
new (gh(holder)) detail::value_holder_impl<value::array>(value::array());
return push_back( fc::move(v) );
}
gh(holder)->push_back(fc::move(v)); gh(holder)->push_back(fc::move(v));
return *this;
}
value& value::push_back( const value& v ) {
if (strcmp(gh(holder)->type(), "void" ) == 0 ) {
new (gh(holder)) detail::value_holder_impl<value::array>(value::array());
return push_back( v );
}
gh(holder)->push_back(value(v));
return *this;
} }
value& value::operator[]( int32_t idx ) { value& value::operator[]( int32_t idx ) {
return gh(holder)->at(idx); return gh(holder)->at(idx);

12
tests/logger.cpp Normal file
View file

@ -0,0 +1,12 @@
#include <fc/logger_config.hpp>
int main( int argc, char** argv ) {
auto lgr = fc::logger::get();
fc::configure_logging( fc::logging_config::default_config() );
fc_dlog( lgr, "Hello Debug" );
fc_ilog( lgr, "Hello Info" );
fc_wlog( lgr, "Hello Warn" );
fc_elog( lgr, "Hello Error" );
fc_flog( lgr, "Hello Fatal" );
return 0;
}