Commit 9259af53 authored by Guillaume Roguez's avatar Guillaume Roguez Committed by Andreas Traczyk

logger: API refactoring

This refactoring of logger brings a definitive C++ orientation
of the code.

A Logger class is added with the support of two logging fashions:
* legacy printf style (format string + optional arguments)
* the iostream C++ style using << operator

This patch doesn't break retro-compatility, but change a bit the
logging output of filename/line context.
This change is needed to not increase the overhead
compared to situation before the patch and let us open to chose how
to display this information without the need to re-build the code.

Also change some macros where RING_xxx are used conjointly with
__VA_ARGS__ to make them work on VisualStudio (UWP) with this new API.

Change-Id: Iaea118460cbb31d0eeada9476d4576f675a9b056
Reviewed-by: Andreas Traczyk's avatarAndreas Traczyk <andreas.traczyk@savoirfairelinux.com>
parent 94e12bda
......@@ -19,11 +19,10 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <cstdio>
#include <cstring>
#include <cerrno>
#include <ctime>
#include <ciso646> // fix windows compiler bug
#include "client/ring_signal.h"
......@@ -40,6 +39,7 @@
#include <ios>
#include <mutex>
#include <thread>
#include <array>
#include "logger.h"
......@@ -49,8 +49,10 @@
#include <sys/syscall.h>
#endif // __linux__
#ifdef _WIN32
#include "winsyslog.h"
#ifdef __ANDROID__
#ifndef APP_NAME
#define APP_NAME "libdring"
#endif /* APP_NAME */
#endif
#define BLACK "\033[22;30m"
......@@ -80,22 +82,41 @@
#define LIGHT_GREEN FOREGROUND_GREEN + 0x0008
#endif // _WIN32
#define LOGFILE "dring"
#ifdef RING_UWP
static constexpr auto ENDL = "";
#else
static constexpr auto ENDL = "\n";
#endif
static int consoleLog;
static int debugMode;
static std::mutex logMutex;
// extract the last component of a pathname (extract a filename from its dirname)
static const char*
stripDirName(const char* path)
{
#ifdef RING_UWP
return strrchr(path, '\\') ? strrchr(path, '\\') + 1 : path;
#else
return strrchr(path, '/') ? strrchr(path, '/') + 1 : path;
#endif
}
static std::string
getHeader(const char* ctx)
contextHeader(const char* const file, int line)
{
#ifdef __linux__
auto tid = syscall(__NR_gettid) & 0xffff;
#else
auto tid = std::this_thread::get_id();
#endif // __linux__
// Timestamp
unsigned int secs, milli;
struct timeval tv;
if (!gettimeofday(&tv, NULL)) {
secs = tv.tv_sec;
milli = tv.tv_usec / 1000; // suppose that milli < 1000
......@@ -112,55 +133,98 @@ getHeader(const char* ctx)
out.fill(prev_fill);
// Context
if (ctx){
if (file) {
#ifdef RING_UWP
out << "|" << std::setw(32) << ctx;
constexpr auto width = 26;
#else
out << "|" << std::setw(24) << ctx;
constexpr auto width = 18;
#endif
out << "|"
<< std::setw(width) << stripDirName(file)
<< ":"
<< std::setw(5) << std::setfill(' ') << line;
}
out << "] ";
return out.str();
}
#ifndef _WIN32
void
setConsoleLog(int c)
{
if (c)
::closelog();
else {
#ifdef _WIN32
::openlog(LOGFILE, WINLOG_PID, WINLOG_MAIL);
#else
::openlog(LOGFILE, LOG_NDELAY, LOG_USER);
#endif /* _WIN32 */
}
consoleLog = c;
}
void
logger(const int level, const char* format, ...)
setDebugMode(int d)
{
if (!debugMode && level == LOG_DEBUG)
return;
debugMode = d;
}
va_list ap;
va_start(ap, format);
vlogger(level, format, ap);
va_end(ap);
int
getDebugMode(void)
{
return debugMode;
}
void
strErr(void)
{
#ifdef __GLIBC__
RING_ERR("%m");
#else
char buf[1000];
const char* errstr;
switch (strerror_r(errno, buf, sizeof(buf))) {
case 0:
errstr = buf;
break;
case ERANGE: /* should never happen */
errstr = "unknown (too big to display)";
break;
default:
errstr = "unknown (invalid error number)";
break;
}
RING_ERR("%s", errstr);
#endif
}
namespace ring {
void
wlogger(const int level, const char* file, const char* format, ...)
Logger::log(int level, const char* file, int line, bool linefeed, const char* const format, ...)
{
if (!debugMode && level == LOG_DEBUG)
return;
const char* file_name_only = FILE_NAME_ONLY(file);
std::string buffer(file_name_only);
buffer.append(format);
va_list ap;
va_start(ap, format);
vlogger(level, buffer.c_str(), ap);
#ifdef __ANDROID__
__android_log_vprint(level, APP_NAME, format, ap);
#else
Logger::vlog(level, file, line, linefeed, format, ap);
#endif
va_end(ap);
}
#endif
void
vlogger(const int level, const char *format, va_list ap)
Logger::vlog(const int level, const char* file, int line, bool linefeed,
const char* format, va_list ap)
{
if (!debugMode && level == LOG_DEBUG)
return;
......@@ -173,7 +237,6 @@ vlogger(const int level, const char *format, va_list ap)
#ifndef _WIN32
const char* color_header = CYAN;
const char* color_prefix = "";
#else
WORD color_prefix = LIGHT_GREEN;
WORD color_header = CYAN;
......@@ -188,6 +251,7 @@ vlogger(const int level, const char *format, va_list ap)
case LOG_ERR:
color_prefix = RED;
break;
case LOG_WARNING:
color_prefix = YELLOW;
break;
......@@ -200,21 +264,11 @@ vlogger(const int level, const char *format, va_list ap)
saved_attributes = consoleInfo.wAttributes;
SetConsoleTextAttribute(hConsole, color_header);
#endif
std::string ctx;
// WARNING : the '|' exists only with ring logs, not other logs like thus from OpenDHT
// WARNING : PLEASE DO NOT DROP THIS TEST !!!! (or die)
auto sep = strchr(format, '|');
if (sep) {
ctx = std::string(format, sep - format);
format = sep + 2;
fputs(getHeader(ctx.c_str()).c_str(), stderr);
}
fputs(contextHeader(file, line).c_str(), stderr);
#ifdef RING_UWP
char tmp[4096];
vsprintf(tmp, format, ap);
ring::emitSignal<DRing::DebugSignal::MessageSend>(getHeader(ctx.c_str()).c_str() + std::string(tmp));
std::array<char, 4096> tmp;
vsnprintf(tmp.data(), tmp.size(), format, ap);
ring::emitSignal<DRing::DebugSignal::MessageSend>(contextHeader(file, line) + tmp.data());
#endif
#ifndef _WIN32
fputs(END_COLOR, stderr);
......@@ -223,11 +277,9 @@ vlogger(const int level, const char *format, va_list ap)
SetConsoleTextAttribute(hConsole, saved_attributes);
SetConsoleTextAttribute(hConsole, color_prefix);
#endif
vfprintf(stderr, format, ap);
// WARING: this one also! see above
if (not sep)
if (linefeed)
fputs(ENDL, stderr);
#ifndef _WIN32
......@@ -235,61 +287,9 @@ vlogger(const int level, const char *format, va_list ap)
#elif !defined(RING_UWP)
SetConsoleTextAttribute(hConsole, saved_attributes);
#endif
} else {
vsyslog(level, format, ap);
::vsyslog(level, format, ap);
}
}
void
setConsoleLog(int c)
{
if (c)
closelog();
else {
#ifdef _WIN32
openlog(LOGFILE, WINLOG_PID, WINLOG_MAIL);
#else
openlog(LOGFILE, LOG_NDELAY, LOG_USER);
#endif /* _WIN32 */
}
consoleLog = c;
}
void
setDebugMode(int d)
{
debugMode = d;
}
int
getDebugMode(void)
{
return debugMode;
}
void
strErr(void)
{
#ifdef __GLIBC__
RING_ERR("%m");
#else
char buf[1000];
const char *errstr;
switch (strerror_r(errno, buf, sizeof(buf))) {
case 0:
errstr = buf;
break;
case ERANGE: /* should never happen */
errstr = "unknown (too big to display)";
break;
default:
errstr = "unknown (invalid error number)";
break;
}
RING_ERR("%s", errstr);
#endif
}
} // namespace ring;
......@@ -21,31 +21,17 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
//#define __STDC_FORMAT_MACROS 1
#include <cinttypes> // for PRIx64
#include <cstdarg>
#include <stdarg.h>
#define __STDC_FORMAT_MACROS 1
#include <inttypes.h>
#include <sstream>
#include <string>
#include "string_utils.h" // to_string
#define LOGFILE "dring"
/**
* Print something, coloring it depending on the level
*/
#ifdef _WIN32
void wlogger(const int level, const char* file, const char* format, ...)
#else
void logger(const int level, const char* format, ...)
#endif
#if defined(_WIN32) && !defined(RING_UWP)
__attribute__((format(gnu_printf, 3, 4)))
#elif defined(__GNUC__)
__attribute__((format(printf, 2, 3)))
#ifdef __cplusplus
extern "C" {
#endif
;
void vlogger(const int level, const char* format, va_list);
/**
* Allow writing on the console
......@@ -67,83 +53,108 @@ int getDebugMode(void);
*/
void strErr();
#define STR(EXP) #EXP
#define XSTR(X) STR(X)
#ifdef RING_UWP
#define FILE_NAME_ONLY(X) (strrchr(X, '\\') ? strrchr(X, '\\') + 1 : X)
#elif defined(_WIN32)
#define FILE_NAME_ONLY(X) (strrchr(X, '/') ? strrchr(X, '/') + 1 : X)
#endif
// Line return char in a string
#ifdef RING_UWP
#define ENDL " "
#else
#define ENDL "\n"
#endif
// Do not remove the "| " in following without modifying vlogger() code
#ifndef _WIN32
#define LOG_FORMAT(M, ...) FILE_NAME ":" XSTR(__LINE__) "| " M, ##__VA_ARGS__
#else
#define LOG_FORMAT(M, ...) ":" XSTR(__LINE__) "| " M, ##__VA_ARGS__
#ifdef __cplusplus
}
#endif
#ifdef __ANDROID__
#include <android/log.h>
#ifndef APP_NAME
#define APP_NAME "libdring"
#endif /* APP_NAME */
#undef LOG_FORMAT
#define LOG_FORMAT(M, ...) "%s:%d | " M, FILE_NAME, __LINE__, ##__VA_ARGS__
// Avoid printing whole path on android
#define FILE_NAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
// because everyone likes reimplementing the wheel
#define LOG_ERR ANDROID_LOG_ERROR
#define LOG_WARNING ANDROID_LOG_WARN
#define LOG_INFO ANDROID_LOG_INFO
#define LOG_DEBUG ANDROID_LOG_DEBUG
#define LOGGER(M, LEVEL, ...) __android_log_print(LEVEL, APP_NAME, LOG_FORMAT(M, ##__VA_ARGS__))
#elif defined(_WIN32)
#include "winsyslog.h"
#define LOG_ERR EVENTLOG_ERROR_TYPE
#define LOG_WARNING EVENTLOG_WARNING_TYPE
#define LOG_INFO EVENTLOG_INFORMATION_TYPE
#define LOG_DEBUG EVENTLOG_SUCCESS
#define FILE_NAME __FILE__
#define LOGGER(M, LEVEL, ...) wlogger(LEVEL, FILE_NAME,LOG_FORMAT(M, ##__VA_ARGS__))
#else
#include <syslog.h>
#define FILE_NAME __FILE__
#include <syslog.h> // Defines LOG_XXXX
#define LOGGER(M, LEVEL, ...) logger(LEVEL, LOG_FORMAT(M, ##__VA_ARGS__))
#endif /* __ANDROID__ / _WIN32 */
#endif /* __ANDROID__ _WIN32 */
#define RING_ERR(M, ...) LOGGER(M ENDL, LOG_ERR, ##__VA_ARGS__)
#define RING_WARN(M, ...) LOGGER(M ENDL, LOG_WARNING, ##__VA_ARGS__)
#define RING_INFO(M, ...) LOGGER(M ENDL, LOG_INFO, ##__VA_ARGS__)
#define RING_DBG(M, ...) LOGGER(M ENDL, LOG_DEBUG, ##__VA_ARGS__)
#define RING_XERR(M, ...) LOGGER(M, LOG_ERR, ##__VA_ARGS__)
#define RING_XWARN(M, ...) LOGGER(M, LOG_WARNING, ##__VA_ARGS__)
#define RING_XINFO(M, ...) LOGGER(M, LOG_INFO, ##__VA_ARGS__)
#define RING_XDBG(M, ...) LOGGER(M, LOG_DEBUG, ##__VA_ARGS__)
#ifdef __cplusplus
}
#if defined(_WIN32) && !defined(RING_UWP)
#define PRINTF_ATTRIBUTE(a, b) __attribute__((format(gnu_printf, a, b)))
#elif defined(__GNUC__)
#define PRINTF_ATTRIBUTE(a, b) __attribute__((format(printf, a, b)))
#else
#define PRINTF_ATTRIBUTE(a, b)
#endif
namespace ring {
///
/// Level-driven logging class that support printf and C++ stream logging fashions.
///
class Logger
{
public:
Logger(int level, const char* file, int line, bool linefeed)
: level_ {level}
, file_ {file}
, line_ {line}
, linefeed_ {linefeed} {}
Logger() = delete;
Logger(const Logger&) = default;
Logger(Logger&&) = default;
~Logger() {
log(level_, file_, line_, linefeed_, "%s", os_.str().c_str());
}
template <typename T>
inline Logger& operator<<(const T& value) {
os_ << value;
return *this;
}
///
/// Printf fashion logging.
///
/// Example: RING_DBG("%s", "Hello, World!")
///
static void log(int level, const char* file, int line, bool linefeed,
const char* const fmt, ...) PRINTF_ATTRIBUTE(5, 6);
///
/// Printf fashion logging (using va_list parameters)
///
static void vlog(const int level, const char* file, int line, bool linefeed,
const char* format, va_list);
///
/// Stream fashion logging.
///
/// Example: RING_DBG() << "Hello, World!"
///
static Logger log(int level, const char* file, int line, bool linefeed) {
return {level, file, line, linefeed};
}
private:
int level_; ///< LOG_XXXX values
const char* const file_; ///< contextual filename (printed as header)
const int line_; ///< contextual line number (printed as header)
bool linefeed_ {true}; ///< true if a '\n' (or any platform equivalent) has to be put at line end in consoleMode
std::ostringstream os_; ///< string stream used with C++ stream style (stream operator<<)
};
// We need to use macros for contextual information
#define RING_INFO(...) ::ring::Logger::log(LOG_INFO, __FILE__, __LINE__, true, ## __VA_ARGS__)
#define RING_DBG(...) ::ring::Logger::log(LOG_DEBUG, __FILE__, __LINE__, true, ## __VA_ARGS__)
#define RING_WARN(...) ::ring::Logger::log(LOG_WARNING, __FILE__, __LINE__, true, ## __VA_ARGS__)
#define RING_ERR(...) ::ring::Logger::log(LOG_ERR, __FILE__, __LINE__, true, ## __VA_ARGS__)
#define RING_XINFO(...) ::ring::Logger::log(LOG_INFO, __FILE__, __LINE__, false, ## __VA_ARGS__)
#define RING_XDBG(...) ::ring::Logger::log(LOG_DEBUG, __FILE__, __LINE__, false, ## __VA_ARGS__)
#define RING_XWARN(...) ::ring::Logger::log(LOG_WARNING, __FILE__, __LINE__, false, ## __VA_ARGS__)
#define RING_XERR(...) ::ring::Logger::log(LOG_ERR, __FILE__, __LINE__, false, ## __VA_ARGS__)
} // namespace ring
......@@ -2163,9 +2163,9 @@ RingAccount::doRegister_()
if (dht_log_level > 0) {
static auto silent = [](char const* /*m*/, va_list /*args*/) {};
#ifndef RING_UWP
static auto log_error = [](char const* m, va_list args) { vlogger(LOG_ERR, m, args); };
static auto log_warn = [](char const* m, va_list args) { vlogger(LOG_WARNING, m, args); };
static auto log_debug = [](char const* m, va_list args) { vlogger(LOG_DEBUG, m, args); };
static auto log_error = [](char const* m, va_list args) { Logger::vlog(LOG_ERR, nullptr, 0, true, m, args); };
static auto log_warn = [](char const* m, va_list args) { Logger::vlog(LOG_WARNING, nullptr, 0, true, m, args); };
static auto log_debug = [](char const* m, va_list args) { Logger::vlog(LOG_DEBUG, nullptr, 0, true, m, args); };
dht_.setLoggers(
log_error,
(dht_log_level > 1) ? log_warn : silent,
......
......@@ -44,7 +44,7 @@
#include <sstream>
#include <algorithm>
#define RETURN_IF_FAIL(A, VAL, M, ...) if (!(A)) { RING_ERR(M, ##__VA_ARGS__); return (VAL); }
#define RETURN_IF_FAIL(A, VAL, ...) if (!(A)) { RING_ERR(__VA_ARGS__); return (VAL); }
namespace ring {
......
......@@ -1240,11 +1240,11 @@ SIPVoIPLink::resolveSrvName(const std::string &name, pjsip_transport_type_e type
pjsip_endpt_resolve(endpt_, pool_.get(), &host_info, (void*)token, resolver_callback);
}
#define RETURN_IF_NULL(A, M, ...) \
if ((A) == NULL) { RING_WARN(M, ##__VA_ARGS__); return; }
#define RETURN_IF_NULL(A, ...) \
if ((A) == NULL) { RING_WARN(__VA_ARGS__); return; }
#define RETURN_FALSE_IF_NULL(A, M, ...) \
if ((A) == NULL) { RING_WARN(M, ##__VA_ARGS__); return false; }
#define RETURN_FALSE_IF_NULL(A, ...) \
if ((A) == NULL) { RING_WARN(__VA_ARGS__); return false; }
void
SIPVoIPLink::findLocalAddressFromTransport(pjsip_transport* transport,
......
......@@ -32,8 +32,8 @@ namespace ring {
// FIXME: this is ugly
// If condition A is false, print the error message in M and exit thread
#define EXIT_IF_FAIL(A, M, ...) if (!(A)) { \
RING_ERR(M, ##__VA_ARGS__); loop_.exit(); }
#define EXIT_IF_FAIL(A, ...) if (!(A)) { \
RING_ERR(__VA_ARGS__); loop_.exit(); }
struct ThreadLoopException : public std::runtime_error {
ThreadLoopException() : std::runtime_error("ThreadLoopException") {}
......
......@@ -64,8 +64,15 @@
#define strerror_r(errno,buf,len) strerror_s(buf,len,errno)
#ifdef __cplusplus
extern "C" {
#endif
extern void closelog(void);
extern void openlog(const char *, int, int);
extern void vsyslog(int, const char *, va_list);
#ifdef __cplusplus
}
#endif
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment