Introduce RetroShare logging helpers

Support for chaining like std::cerr
Support for compile-time optimization without #ifdef around
Support for Android native logging without depending on
  AndroidStdIOCatcher workaround
Support for automatic timestamp (on Android it's provided by Android
  native log)
Standardized messages categories
This commit is contained in:
Gioacchino Mazzurco 2019-04-28 02:27:12 +02:00
parent d113f60ca1
commit 79af081f84
No known key found for this signature in database
GPG Key ID: A1FBCA3872E87051

View File

@ -1,9 +1,8 @@
/*******************************************************************************
* libretroshare/src/util: rsdebug.h *
* RetroShare debugging utilities *
* *
* libretroshare: retroshare core library *
* *
* Copyright 2004-2008 by Robert Fernie <retroshare@lunamutt.com> *
* Copyright (C) 2004-2008 Robert Fernie <retroshare@lunamutt.com> *
* Copyright (C) 2019 Gioacchino Mazzurco <gio@eigenlab.org> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
@ -19,16 +18,211 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
/* Moved from pqi/ to util/ so it can be used more generally.
*/
#pragma once
#ifndef RS_LOG_DEBUG_H
#define RS_LOG_DEBUG_H
#include <ostream>
#ifdef __ANDROID__
# include <android/log.h>
# include <sstream>
# include <string>
enum class RsLoggerCategories
{
DEBUG = ANDROID_LOG_DEBUG,
INFO = ANDROID_LOG_INFO,
WARNING = ANDROID_LOG_WARN,
ERROR = ANDROID_LOG_ERROR,
FATAL = ANDROID_LOG_FATAL
};
template <RsLoggerCategories CATEGORY>
struct t_RsLogger
{
inline t_RsLogger() = default;
template<typename T>
inline t_RsLogger& operator<<(const T& val)
{ ostr << val; return *this; }
/// needed for manipulators and things like std::endl
t_RsLogger& operator<<(std::ostream& (*pf)(std::ostream&))
{
if(pf == static_cast<std::ostream& (*)(std::ostream&)>(
&std::endl< char, std::char_traits<char> > ))
{
__android_log_write(
static_cast<int>(CATEGORY),
"RetroShare", ostr.str().c_str() );
ostr.str() = "";
}
else ostr << pf;
return *this;
}
private:
std::ostringstream ostr;
};
#else // def __ANDROID__
#include <iostream>
#include <ctime>
enum class RsLoggerCategories
{
DEBUG = 'D',
INFO = 'I',
WARNING = 'W',
ERROR = 'E',
FATAL = 'F'
};
template <RsLoggerCategories CATEGORY>
struct t_RsLogger
{
inline t_RsLogger() = default;
template<typename T>
inline std::ostream& operator<<(const T& val)
{
return std::cerr << static_cast<char>(CATEGORY) << " " << time(nullptr)
<< " " << val;
}
};
#endif // def __ANDROID__
/**
* Comfortable debug message loggin, supports chaining like std::cerr but can
* be easly and selectively disabled at compile time to reduce generated binary
* size and performance impact without too many #ifdef around.
*
* To selectively debug your context you can just add something like this in
* in that context, as an example for a class you can just add a line like this
* inside class declaration:
@code{.cpp}
RS_SET_CONTEXT_DEBUG_LEVEL(2)
@endcode
* And the you can write debug messages around the code of the class like this:
@code{.cpp}
Dbg1() << "Level 1 debug message example, this will be compiled and "
<< "printed" << std::endl;
Dbg2() << "Level 2 debug message example, this will be compiled and "
<< "printed" << std::endl;
Dbg3() << "Level 3 debug message example, this will not be compiled and "
<< "printed, and without #ifdef around!!" << std::endl;
Dbg4() << "Level 4 debug message example, this will not be compiled and "
<< "printed, and without #ifdef around!!" << std::endl;
@endcode
* To change the debugging level, for example to completely disable debug
* messages you can change it to 0
@code{.cpp}
RS_SET_CONTEXT_DEBUG_LEVEL(0)
@endcode
* While to set it to maximim level you have to pass 4.
*/
using RsDbg = t_RsLogger<RsLoggerCategories::DEBUG>;
/**
* Comfortable log information reporting helper, supports chaining like
* std::cerr.
* To report an information message you can just write:
@code{.cpp}
RsInfo() << __PRETTY_FUNCTION__ << "My information message" << std::cerr;
@endcode
*/
using RsInfo = t_RsLogger<RsLoggerCategories::INFO>;
/// Similar to @see RsInfo but for warning messages
using RsWarn = t_RsLogger<RsLoggerCategories::WARNING>;
/// Similar to @see RsInfo but for error messages
using RsErr = t_RsLogger<RsLoggerCategories::ERROR>;
/** Similar to @see RsInfo but for fatal errors (the ones which cause RetroShare
* to terminate) messages */
using RsFatal = t_RsLogger<RsLoggerCategories::FATAL>;
/**
* Keeps compatible syntax with RsDbg but explicitely do nothing in a way that
* any modern compiler should be smart enough to optimize out all the function
* calls.
*/
struct RsNoDbg
{
inline RsNoDbg() = default;
/**
* This match most of the types, but might be not enough for templated
* types
*/
template<typename T>
inline RsNoDbg& operator<<(const T&) { return *this; }
/// needed for manipulators and things like std::endl
inline RsNoDbg& operator<<(std::ostream& (*/*pf*/)(std::ostream&))
{ return *this; }
};
/**
* Concatenate preprocessor tokens A and B without expanding macro definitions
* (however, if invoked from a macro, macro arguments are expanded).
*/
#define RS_CONCAT_MACRO_NX(A, B) A ## B
/// Concatenate preprocessor tokens A and B after macro-expanding them.
#define RS_CONCAT_MACRO(A, B) RS_CONCAT_MACRO_NX(A, B)
/**
* Set local context debug level.
* Avoid copy pasting boilerplate code around @see RsDbg for usage details
*/
#define RS_SET_CONTEXT_DEBUG_LEVEL(level) \
RS_CONCAT_MACRO(RS_SET_CONTEXT_DEBUG_LEVEL, level)
// A bunch of boilerplate, but just in one place
#define RS_SET_CONTEXT_DEBUG_LEVEL0 \
using Dbg1 = RsNoDbg; \
using Dbg2 = RsNoDbg; \
using Dbg3 = RsNoDbg; \
using Dbg4 = RsNoDbg;
#define RS_SET_CONTEXT_DEBUG_LEVEL1 \
using Dbg1 = RsDbg; \
using Dbg2 = RsNoDbg; \
using Dbg3 = RsNoDbg; \
using Dbg4 = RsNoDbg;
#define RS_SET_CONTEXT_DEBUG_LEVEL2 \
using Dbg1 = RsDbg; \
using Dbg2 = RsDbg; \
using Dbg3 = RsNoDbg; \
using Dbg4 = RsNoDbg;
#define RS_SET_CONTEXT_DEBUG_LEVEL3 \
using Dbg1 = RsDbg; \
using Dbg2 = RsDbg; \
using Dbg3 = RsDbg; \
using Dbg4 = RsNoDbg;
#define RS_SET_CONTEXT_DEBUG_LEVEL4 \
using Dbg1 = RsDbg; \
using Dbg2 = RsDbg; \
using Dbg3 = RsDbg; \
using Dbg4 = RsDbg;
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// All the following lines are DEPRECATED!!
#include <string>
#include "util/rsdeprecate.h"
namespace RsLog {
enum logLvl {
enum RS_DEPRECATED_FOR("RsErr, RsDbg, RsNoDbg") logLvl {
None = -1,
Default = 0,
Alert = 1,
@ -40,7 +234,7 @@ namespace RsLog {
};
// this struct must be provided by the caller (to rslog())
struct logInfo {
struct RS_DEPRECATED_FOR("RsErr, RsDbg, RsNoDbg") logInfo {
// module specific log lvl
logLvl lvl;
// module name (displayed in log)
@ -48,9 +242,16 @@ namespace RsLog {
};
}
RS_DEPRECATED_FOR("RsErr, RsDbg, RsNoDbg")
int setDebugCrashMode(const char *cfile);
RS_DEPRECATED_FOR("RsErr, RsDbg, RsNoDbg")
int setDebugFile(const char *fname);
RS_DEPRECATED_FOR("RsErr, RsDbg, RsNoDbg")
int setOutputLevel(RsLog::logLvl lvl);
RS_DEPRECATED_FOR("RsErr, RsDbg, RsNoDbg")
void rslog(const RsLog::logLvl lvl, RsLog::logInfo *info, const std::string &msg);
@ -76,7 +277,3 @@ void rslog(const RsLog::logLvl lvl, RsLog::logInfo *info, const std::string &msg
#define PQL_DEBUG_ALERT RSL_DEBUG_ALERT
#define PQL_DEBUG_BASIC RSL_DEBUG_BASIC
#define PQL_DEBUG_ALL RSL_DEBUG_ALL
#endif