From b0521e8165389515b6f8290a652d7db241f84af8 Mon Sep 17 00:00:00 2001 From: Vadim Troshchinskiy Date: Thu, 22 Dec 2016 09:57:47 +0100 Subject: New logging implementation Features: * Works without ifdefs * Configurable with commandline arguments * Log level configurable per file * Thread safe --- nxcomp/src/Log.cpp | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 nxcomp/src/Log.cpp (limited to 'nxcomp/src/Log.cpp') diff --git a/nxcomp/src/Log.cpp b/nxcomp/src/Log.cpp new file mode 100644 index 000000000..3ff47aea8 --- /dev/null +++ b/nxcomp/src/Log.cpp @@ -0,0 +1,121 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder */ +/* Copyright (c) 2014-2016 Ulrich Sibiller */ +/* Copyright (c) 2014-2016 Mihai Moldovan */ +/* Copyright (c) 2011-2016 Mike Gabriel */ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + + +#include +#include +#include +#include +#include + +#include "Log.h" + +NXLog nx_log; + + +bool NXLog::will_log() const +{ + std::map::const_iterator item = per_file_levels_.find(current_file()); + + if ( item != per_file_levels_.end() ) + { + return current_level() <= item->second; + } + else + { + return current_level() <= level(); + } +} + + +std::string NXLog::stamp_to_string(const NXLogStamp& stamp) const +{ + std::ostringstream oss; + + static const char* level_names[] = { + "FATAL", + "ERROR", + "WARN ", + "INFO ", + "DEBUG" + }; + + if ( log_level() ) + oss << ((stamp.level() >=0 && stamp.level() < NXLOG_LEVEL_COUNT ) ? level_names[stamp.level()] : "???") << " "; + + if ( log_time() ) + { + struct timeval timestamp = stamp.timestamp(); + struct tm timeinfo; + + localtime_r(×tamp.tv_sec, &timeinfo); + + if ( log_unix_time() ) + { + oss << timestamp.tv_sec; + } + else + { + #if __cplusplus >= 201103L && (!defined(__GNUC__) || __GNUC__ >= 5) + oss << " " << std::put_time(&timeinfo, "%Y/%m/%d %H:%M:%S"); + #else + oss << timestamp.tv_sec; + #endif + } + + oss << "." << std::setw(3) << std::setfill('0') << (int)(timestamp.tv_usec / 1000) << " "; + } + + if ( log_location() ) + oss << stamp.file() << "/" << stamp.function() << ":" << stamp.line() << " "; + + if ( log_thread_id() ) + { + if ( thread_name().empty() ) + oss << pthread_self() << " "; + else + oss << "[" << thread_name() << "] "; + } + + return oss.str(); +} + +NXLog& operator<< (NXLog& out, const NXLogStamp& value) +{ + out.current_level( value.level() ); + out.current_file( value.file() ); + + // Writing an NXLogStamp to the stream indicates the start of a new entry. + // If there's any content in the buffer, we flush it to finalize the previous + // log entry. + if ( out.synchronized() ) + out.flush(); + + + out << out.stamp_to_string(value); + + return out; +} + -- cgit v1.2.3 From fc4a18d3398ec360b28802dff990e2c1c8d368b5 Mon Sep 17 00:00:00 2001 From: Mihai Moldovan Date: Fri, 30 Jun 2017 23:32:45 +0200 Subject: nxcomp/{configure.ac,Log.cpp}: implement configure-time std::put_time check and use macro value in Log.cpp. --- nxcomp/configure.ac | 17 +++++++++++++++++ nxcomp/src/Log.cpp | 3 ++- 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'nxcomp/src/Log.cpp') diff --git a/nxcomp/configure.ac b/nxcomp/configure.ac index 454ee6a80..a61db32c1 100644 --- a/nxcomp/configure.ac +++ b/nxcomp/configure.ac @@ -74,6 +74,23 @@ AC_ARG_ENABLE([cxx11], [AS_IF([test x$enableval = xyes], [AX_CXX_COMPILE_STDCXX_11([], [mandatory])])]) +# Check if std::put_time is available. +AC_MSG_CHECKING([if std::put_time is available]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM( +[[ +#include +#include +]], +[[ +std::time_t t = std::time(NULL); +std::tm tm = *std::localtime(&t); +(void) std::put_time(&tm, "%c"); +]])], + [AC_MSG_RESULT([yes]) + AC_DEFINE(HAVE_STD_PUT_TIME, [1], + [Use std::put_time to format times, must be made available by the compiler if turned on.])], + [AC_MSG_RESULT([no])]) + AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [enable to get info session log output (disabled by default)])], diff --git a/nxcomp/src/Log.cpp b/nxcomp/src/Log.cpp index 3ff47aea8..66ae1bd78 100644 --- a/nxcomp/src/Log.cpp +++ b/nxcomp/src/Log.cpp @@ -31,6 +31,7 @@ #include #include "Log.h" +#include "config.h" NXLog nx_log; @@ -78,7 +79,7 @@ std::string NXLog::stamp_to_string(const NXLogStamp& stamp) const } else { - #if __cplusplus >= 201103L && (!defined(__GNUC__) || __GNUC__ >= 5) + #if HAVE_STD_PUT_TIME oss << " " << std::put_time(&timeinfo, "%Y/%m/%d %H:%M:%S"); #else oss << timestamp.tv_sec; -- cgit v1.2.3 From 09586d760833dac680ba8837b0b695ed3900c96d Mon Sep 17 00:00:00 2001 From: Mihai Moldovan Date: Sat, 30 Sep 2017 15:30:53 +0200 Subject: nxcomp/src/Log.{cpp,h}: port to std::stack as internal buffer structure. This has one drawback: after flushing log data to its underlying output, a new NXLogStamp object MUST be written to the NXLog object in order to create a new entry within the stack. This can be changed if necessary. For now I'd like to keep it as-is. --- nxcomp/src/Log.cpp | 8 +++----- nxcomp/src/Log.h | 52 ++++++++++++++++++++++++++++++++++------------------ 2 files changed, 37 insertions(+), 23 deletions(-) (limited to 'nxcomp/src/Log.cpp') diff --git a/nxcomp/src/Log.cpp b/nxcomp/src/Log.cpp index 66ae1bd78..83e11b98a 100644 --- a/nxcomp/src/Log.cpp +++ b/nxcomp/src/Log.cpp @@ -109,14 +109,12 @@ NXLog& operator<< (NXLog& out, const NXLogStamp& value) out.current_file( value.file() ); // Writing an NXLogStamp to the stream indicates the start of a new entry. - // If there's any content in the buffer, we flush it to finalize the previous - // log entry. + // If there's any content in the buffer, create a new entry in the output + // queue. if ( out.synchronized() ) - out.flush(); - + out.new_stack_entry(); out << out.stamp_to_string(value); return out; } - diff --git a/nxcomp/src/Log.h b/nxcomp/src/Log.h index 44c43a47b..ecc6ea9ed 100644 --- a/nxcomp/src/Log.h +++ b/nxcomp/src/Log.h @@ -35,6 +35,8 @@ #include #include #include +#include +#include /** Log severity level */ enum NXLogLevel @@ -138,11 +140,11 @@ class NXLog typedef struct per_thread_data_s { - NXLogLevel current_level; - std::string* current_file; - std::string* thread_name; - std::stringstream* buffer; - NXLog* log_obj; + NXLogLevel current_level; + std::string* current_file; + std::string* thread_name; + std::stack buffer; + NXLog* log_obj; } per_thread_data; @@ -160,7 +162,11 @@ class NXLog delete pdt->current_file; delete pdt->thread_name; - delete pdt->buffer; + + while (!pdt->buffer.empty()) { + (void) pdt->buffer.pop (); + } + delete pdt; } @@ -172,7 +178,6 @@ class NXLog { ret = new per_thread_data; ret->current_level = NXDEBUG; - ret->buffer = new std::stringstream(); ret->current_file = new std::string(); ret->thread_name = new std::string(); ret->log_obj = const_cast(this); @@ -195,6 +200,12 @@ class NXLog /** Convert NXLogStamp to string according to the current configuration */ std::string stamp_to_string(const NXLogStamp& stamp) const; + void new_stack_entry() + { + per_thread_data *pdt = get_data(); + pdt->buffer.push(new std::stringstream()); + } + /** * Internal flush function * @@ -207,15 +218,18 @@ class NXLog */ void flush(per_thread_data *pdt) { - const std::string str = pdt->buffer->str(); + if (!pdt->buffer.empty ()) { + const std::string str = pdt->buffer.top()->str(); - if (!str.empty()) - { - pthread_mutex_lock(&output_lock_); - (*stream()) << str; - pthread_mutex_unlock(&output_lock_); - pdt->buffer->str(std::string()); - pdt->buffer->clear(); + if (!str.empty()) + { + pthread_mutex_lock(&output_lock_); + (*stream()) << str; + pthread_mutex_unlock(&output_lock_); + } + + /* Remove from stack. */ + pdt->buffer.pop(); } } @@ -404,7 +418,8 @@ class NXLog if ( synchronized() ) { per_thread_data *pdt = get_data(); - (*pdt->buffer) << F; + assert (!pdt->buffer.empty ()); + (*pdt->buffer.top()) << F; flush(); } else @@ -484,9 +499,10 @@ NXLog& operator<<(NXLog& out, const T& value) // gets full. Then we dump the whole thing at once to the output stream, synchronizing // with a mutex. NXLog::per_thread_data *pdt = out.get_data(); - (*pdt->buffer) << value; + assert (!pdt->buffer.empty ()); + (*pdt->buffer.top()) << value; - if ( ss_length(pdt->buffer) >= out.thread_buffer_size_ || has_newline(value) ) + if ( ss_length(pdt->buffer.top()) >= out.thread_buffer_size_ || has_newline(value) ) out.flush(); } -- cgit v1.2.3 From 6c3cf54ba84e9e31a5be62ab58391106d35eaff6 Mon Sep 17 00:00:00 2001 From: Mihai Moldovan Date: Fri, 27 Oct 2017 08:32:14 +0200 Subject: nxcomp/src/Log.cpp: add PID to thread ID output if requested. The default function we use (if a thread name is not specified explicitly) is pthread_self(). This function returns a number that is guaranteed to be unique for each thread within a process, but this assertion doesn't hold globally. Hence only using the thread ID is ambiguous when logging from multiple processes. --- nxcomp/src/Log.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nxcomp/src/Log.cpp') diff --git a/nxcomp/src/Log.cpp b/nxcomp/src/Log.cpp index 83e11b98a..951c10a04 100644 --- a/nxcomp/src/Log.cpp +++ b/nxcomp/src/Log.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include "Log.h" #include "config.h" @@ -95,7 +96,7 @@ std::string NXLog::stamp_to_string(const NXLogStamp& stamp) const if ( log_thread_id() ) { if ( thread_name().empty() ) - oss << pthread_self() << " "; + oss << getpid() << "/" << pthread_self() << " "; else oss << "[" << thread_name() << "] "; } -- cgit v1.2.3