// Copyright (C) 2011 Kornel Kisielewicz // This file is part of NV Libraries. // For conditions of distribution and use, see copyright notice in nv.hh #include "nv/logger.hh" #include "nv/common.hh" #include #include #include #include #include #include #include #if NV_PLATFORM == NV_WINDOWS #define WIN32_LEAN_AND_MEAN #include #endif using namespace nv; // log level names static const char *log_level_names[] = { "NONE", "FATAL", "CRITICAL", "ERROR", "WARNING", "NOTICE", "INFO", "INFO", "DEBUG", "DEBUG2", "TRACE" }; // log level names static const char *log_level_names_pad[] = { "NONE ", "FATAL ", "CRITICAL", "ERROR ", "WARNING ", "NOTICE ", "INFO ", "INFO ", "DEBUG ", "DEBUG2 ", "TRACE " }; // helper macro to access log_level_names #define NV_LOG_LEVEL_NAME(level) (log_level_names[ (level) / 10 ]) #define NV_LOG_LEVEL_NAME_PAD(level) (log_level_names_pad[ (level) / 10 ]) #if NV_PLATFORM == NV_WINDOWS static unsigned short log_color[] = { FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY, FOREGROUND_RED | FOREGROUND_INTENSITY, FOREGROUND_RED | FOREGROUND_INTENSITY, FOREGROUND_RED, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, FOREGROUND_GREEN | FOREGROUND_INTENSITY, FOREGROUND_GREEN, FOREGROUND_GREEN, FOREGROUND_INTENSITY, FOREGROUND_INTENSITY, FOREGROUND_INTENSITY }; #else static const char *log_color[] = { "\33[37;1m", "\33[31;1m", "\33[31;1m", "\33[31m", "\33[33;1m", "\33[32;1m", "\33[32m", "\33[32m", "\33[30;1m", "\33[30;1m", "\33[30;1m" }; #endif // log function void logger::log( log_level level, const std::string& message ) { // get the iterator to the beginning of the log_sink list log_sink_list::reverse_iterator it = m_log_sinks.rbegin(); // iterate while ( it != m_log_sinks.rend() ) { // if we have a log sink with high enough level... if ( it->first >= level ) { // log and iterate it->second->log( level, message ); } else { // otherwise return, the list is sorted by log level return; } ++it; } } // add a new sink void logger::add_sink( log_sink* sink, int level ) { // add a sink m_log_sinks.push_back( std::make_pair( log_level(level), sink ) ); // and sort the list (default sort of pairs is by first element) m_log_sinks.sort(); } // remove existing sink bool logger::remove_sink( log_sink* sink ) { // get the iterator to the beginning of the log_sink list log_sink_list::iterator it = m_log_sinks.begin(); // iterate while ( it != m_log_sinks.end() ) { // found? if ( it->second == sink ) { // erase and return true to report success m_log_sinks.erase(it); return true; } ++it; } // not found, return false return false; } // destructor logger::~logger() { // while we have sinks while ( !m_log_sinks.empty() ) { // delete the last one delete m_log_sinks.back().second; // and pop it m_log_sinks.pop_back(); } } // console logging void log_console_sink::log( log_level level, const std::string& message ) { if (m_color) { #if NV_PLATFORM == NV_WINDOWS SetConsoleTextAttribute( m_handle, FOREGROUND_INTENSITY ); std::cout << timestamp() << " ["; SetConsoleTextAttribute( m_handle, log_color[ (level) / 10 ] ); std::cout << NV_LOG_LEVEL_NAME_PAD(level); SetConsoleTextAttribute( m_handle, FOREGROUND_INTENSITY ); std::cout << "] "; SetConsoleTextAttribute( m_handle, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE ); std::cout << message << std::endl; #else std::cout << "\33[30;1m" << timestamp() << " [" << log_color[ (level) / 10 ] << NV_LOG_LEVEL_NAME_PAD(level) << "\33[30;1m] \33[37;1m" << message << std::endl; #endif } else { std::cout << timestamp() << " [" << NV_LOG_LEVEL_NAME_PAD(level) << "] " << message << std::endl; } } // stream logging void log_stream_sink::log( log_level level, const std::string& message ) { // if flushing is enabled if ( m_flush ) { // write and flush using std::endl *m_stream << timestamp() << " [" << NV_LOG_LEVEL_NAME(level) << "] " << message << std::endl; } else { // write and end with "\n" (no flush) *m_stream << timestamp() << " [" << NV_LOG_LEVEL_NAME(level) << "] " << message << "\n"; } } // file logging log_file_sink::log_file_sink( const std::string file_name, bool flush_always /*= true */ ) : log_stream_sink( nullptr, flush_always ) { // create the stream manually std::ofstream* fstream = new std::ofstream( file_name ); // check if it's open if ( !fstream->is_open() ) { // throw if not open NV_THROW( runtime_error, "Could not open file \""+file_name+"\" for logging!" ); } m_stream = fstream; } // file logger destructor log_file_sink::~log_file_sink() { // close the file dynamic_cast< std::ofstream* >(m_stream)->close(); // dispose of the stream delete m_stream; } nv::log_console_sink::log_console_sink( bool coloring ) : m_color( coloring ) { #if NV_PLATFORM == NV_WINDOWS m_handle = GetStdHandle( STD_OUTPUT_HANDLE ); #else NV_UNUSED( m_handle ); #endif } const char* nv::log_sink::timestamp() const { std::clock_t time = std::clock(); unsigned int secs = (unsigned int)(time / CLOCKS_PER_SEC); unsigned int mm = (unsigned int)(time*100 / CLOCKS_PER_SEC) % 100; unsigned int h = (unsigned int)(secs / (60*60)); unsigned int m = (unsigned int)(secs / 60) % 60; unsigned int s = secs % 60; static char buffer[128]; #if NV_PLATFORM == NV_WINDOWS sprintf_s( buffer, 128, "%02d:%02d:%02d.%02d", h, m, s, mm ); #else sprintf( buffer, "%02d:%02d:%02d.%02d", h, m, s, mm ); #endif buffer[11] = '\0'; return buffer; } const char* nv::log_sink::level_name( log_level level ) const { return NV_LOG_LEVEL_NAME( level ); } const char* nv::log_sink::padded_level_name( log_level level ) const { return NV_LOG_LEVEL_NAME_PAD( level ); }