From 71ea16cf8b18950937d1e056fe6a2b8eef078b69 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Mon, 4 Feb 2013 23:08:48 -0500 Subject: [PATCH] Adding updated logging system similar to log4 Updates to ssh client Updated reflect enum --- CMakeLists.txt | 5 ++ include/fc/appender.hpp | 43 +++++++++++ include/fc/filesystem.hpp | 1 + include/fc/logger.hpp | 138 +++++++++++++++++++++++++++++++++ include/fc/logger_config.hpp | 41 ++++++++++ include/fc/reflect.hpp | 25 +++--- include/fc/ssh/client.hpp | 10 +++ include/fc/value.hpp | 5 +- include/fc/value_io.hpp | 2 +- src/appender.cpp | 146 +++++++++++++++++++++++++++++++++++ src/logger.cpp | 110 ++++++++++++++++++++++++++ src/logger_config.cpp | 77 ++++++++++++++++++ src/ssh.cpp | 43 ++++++++++- src/value.cpp | 15 +++- tests/logger.cpp | 12 +++ 15 files changed, 657 insertions(+), 16 deletions(-) create mode 100644 include/fc/appender.hpp create mode 100644 include/fc/logger.hpp create mode 100644 include/fc/logger_config.hpp create mode 100644 src/appender.cpp create mode 100644 src/logger.cpp create mode 100644 src/logger_config.cpp create mode 100644 tests/logger.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2dba0e3..65bafea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,9 @@ include_directories( ${OPENSSL_INCLUDE_DIR} ) include_directories( include ) set( sources + src/logger.cpp + src/appender.cpp + src/logger_config.cpp src/ssh.cpp src/url.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( 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 ) #target_link_libraries( test_vec fc ${Boost_SYSTEM_LIBRARY} ${Boost_CHRONO_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_CONTEXT_LIBRARY} ) diff --git a/include/fc/appender.hpp b/include/fc/appender.hpp new file mode 100644 index 0000000..f8fa1d4 --- /dev/null +++ b/include/fc/appender.hpp @@ -0,0 +1,43 @@ +#pragma once +#include + +namespace fc { + class appender; + class log_message; + class value; + class string; + + class appender_factory : public fc::retainable { + public: + typedef fc::shared_ptr ptr; + + virtual ~appender_factory(){}; + virtual fc::shared_ptr create( const value& args ) = 0; + }; + + namespace detail { + template + class appender_factory_impl : public appender_factory { + public: + virtual fc::shared_ptr create( const value& args ) { + return fc::shared_ptr(new T(args)); + } + }; + } + + class appender : public fc::retainable { + public: + typedef fc::shared_ptr ptr; + + template + static bool register_appender(const fc::string& type) { + return register_appender( type, new detail::appender_factory_impl() ); + } + + 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; + }; +} diff --git a/include/fc/filesystem.hpp b/include/fc/filesystem.hpp index 0174b0e..7f10516 100644 --- a/include/fc/filesystem.hpp +++ b/include/fc/filesystem.hpp @@ -41,6 +41,7 @@ namespace fc { fc::path parent_path()const; fc::string string()const; fc::string generic_string()const; + bool is_relative()const; bool is_absolute()const; private: diff --git a/include/fc/logger.hpp b/include/fc/logger.hpp new file mode 100644 index 0000000..75019a0 --- /dev/null +++ b/include/fc/logger.hpp @@ -0,0 +1,138 @@ +#pragma once +#include +#include +#include +#include + + +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& a ); + + + bool is_enabled( log_level::type e )const; + void log( log_message m ); + + private: + class impl; + fc::shared_ptr 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( 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__ );\ + } + diff --git a/include/fc/logger_config.hpp b/include/fc/logger_config.hpp new file mode 100644 index 0000000..e1464b7 --- /dev/null +++ b/include/fc/logger_config.hpp @@ -0,0 +1,41 @@ +#pragma once +#include + +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 level; + bool enabled; + /// if any appenders are sepecified, then parent's appenders are not set. + bool additivity; + fc::vector appenders; + }; + + struct logging_config { + static logging_config default_config(); + fc::vector includes; + fc::vector appenders; + fc::vector loggers; + }; + + void configure_logging( const fc::path& log_config ); + bool configure_logging( const logging_config& l ); +} + +#include +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) ) diff --git a/include/fc/reflect.hpp b/include/fc/reflect.hpp index 141ae99..8358429 100644 --- a/include/fc/reflect.hpp +++ b/include/fc/reflect.hpp @@ -105,13 +105,13 @@ void fc::reflector::visit( const Visitor& v ) { \ #endif // DOXYGEN -#define FC_REFLECT_VISIT_ENUM( r, visitor, elem ) \ - visitor.TEMPLATE operator()(BOOST_PP_STRINGIZE(elem)); -#define FC_REFLECT_ENUM_TO_STRING( r, visitor, elem ) \ - case elem: return BOOST_PP_STRINGIZE(elem); +#define FC_REFLECT_VISIT_ENUM( r, enum_type, elem ) \ + v.TEMPLATE operator()(BOOST_PP_STRINGIZE(elem)); +#define FC_REFLECT_ENUM_TO_STRING( r, enum_type, elem ) \ + case enum_type::elem: return BOOST_PP_STRINGIZE(elem); -#define FC_REFLECT_ENUM_FROM_STRING( r, visitor, elem ) \ - if( strcmp( s, BOOST_PP_STRINGIZE(elem) ) == 0 ) return elem; +#define FC_REFLECT_ENUM_FROM_STRING( r, enum_type, elem ) \ + if( strcmp( s, BOOST_PP_STRINGIZE(elem) ) == 0 ) return enum_type::elem; #define FC_REFLECT_ENUM( ENUM, FIELDS ) \ namespace fc { \ @@ -120,18 +120,21 @@ template<> struct reflector { \ typedef fc::true_type is_enum; \ template \ 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) { \ 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: \ - 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 ) { \ - BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_ENUM_FROM_STRING, v, FIELDS ) \ - FC_THROW_MSG( "Unknown field %s not in enum '%s'", s, BOOST_PP_STRINGIZE(ENUM) ); \ + BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_ENUM_FROM_STRING, ENUM, FIELDS ) \ + FC_THROW_REPORT( "Unknown field ${field} not in enum ${enum}", \ + fc::value().set("field",s).set("enum",BOOST_PP_STRINGIZE(ENUM)) ); \ } \ }; \ } diff --git a/include/fc/ssh/client.hpp b/include/fc/ssh/client.hpp index 6e1b9ef..b9c6a79 100644 --- a/include/fc/ssh/client.hpp +++ b/include/fc/ssh/client.hpp @@ -88,6 +88,16 @@ namespace fc { void scp_send( const fc::path& local_path, const fc::path& remote_path, std::function 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 remote_path/local_dir.filename() will + * be created, otherwise local_dir / * will be copied to remote_path / * + * + * 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 progress = [](size_t,size_t){return true;} ); /** * @pre remote_path is not a directory diff --git a/include/fc/value.hpp b/include/fc/value.hpp index 5403c0f..104c9c1 100644 --- a/include/fc/value.hpp +++ b/include/fc/value.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include namespace fc { @@ -131,7 +132,8 @@ namespace fc { /** array interface **/ void resize( 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 ); const value& operator[]( int32_t idx )const; @@ -163,6 +165,7 @@ namespace fc { aligned<40> holder; }; + typedef fc::optional ovalue; bool operator == ( const value& v, std::nullptr_t ); bool operator != ( const value& v, std::nullptr_t ); diff --git a/include/fc/value_io.hpp b/include/fc/value_io.hpp index d839d2c..3e212bc 100644 --- a/include/fc/value_io.hpp +++ b/include/fc/value_io.hpp @@ -172,7 +172,7 @@ namespace fc { v = fc::reflector::from_string( fc::value_cast(jsv).c_str() ); } else { // throw if invalid int, by attempting to convert to string - fc::reflector::to_string( v = value_cast(jsv) ); + fc::reflector::to_string( v = static_cast(value_cast(jsv)) ); } } }; diff --git a/src/appender.cpp b/src/appender.cpp new file mode 100644 index 0000000..9f10240 --- /dev/null +++ b/src/appender.cpp @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#endif + +namespace fc { + + static fc::spin_lock appender_spinlock; + std::unordered_map& get_appender_map() { + static std::unordered_map lm; + return lm; + } + std::unordered_map& get_appender_factory_map() { + static std::unordered_map lm; + return lm; + } + appender::ptr appender::get( const fc::string& s ) { + scoped_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_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 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(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" ); +} // namespace fc diff --git a/src/logger.cpp b/src/logger.cpp new file mode 100644 index 0000000..cc228ae --- /dev/null +++ b/src/logger.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// tmp... +#include + +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 _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& get_logger_map() { + static std::unordered_map lm; + return lm; + } + + logger logger::get( const fc::string& s ) { + scoped_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& a ) + { my->_appenders.push_back(a); } + + +} // namespace fc diff --git a/src/logger_config.cpp b/src/logger_config.cpp new file mode 100644 index 0000000..1ff2603 --- /dev/null +++ b/src/logger_config.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include +#include + +namespace fc { + std::unordered_map& get_logger_map(); + std::unordered_map& get_appender_map(); + + void configure_logging( const fc::path& lc ) + { + configure_logging( fc::json::from_file(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() ); +} diff --git a/src/ssh.cpp b/src/ssh.cpp index 04950de..8d7cf48 100644 --- a/src/ssh.cpp +++ b/src/ssh.cpp @@ -450,8 +450,41 @@ namespace fc { namespace ssh { 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 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, std::function 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 * 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 - file_mapping fmap( local_path.string().c_str(), read_only ); 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 ); LIBSSH2_CHANNEL* chan = 0; time_t now; memset( &now, 0, sizeof(now) ); + // 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 ); while( chan == 0 ) { char* msg; diff --git a/src/value.cpp b/src/value.cpp index 183cffc..bfbfa55 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -391,8 +391,21 @@ void value::resize( size_t s ) { void value::reserve( size_t 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()); + return 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()); + return push_back( v ); + } + gh(holder)->push_back(value(v)); + return *this; } value& value::operator[]( int32_t idx ) { return gh(holder)->at(idx); diff --git a/tests/logger.cpp b/tests/logger.cpp new file mode 100644 index 0000000..2a56739 --- /dev/null +++ b/tests/logger.cpp @@ -0,0 +1,12 @@ +#include + +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; +}