Merge pull request #1522
release-v0.4.0.15833d66f
Change logging to easylogging++ (moneromooo-monero)dc98019b
easylogging++: fix logging with static const header only data members (moneromooo-monero)3b46617b
easylogging++: add ELPP_DISABLE_CHECK_MACROS (moneromooo-monero)6fe39d90
easylogging++: allow clipping a common filename prefix (moneromooo-monero)43abf6ff
easylogging++: add file-only logs (moneromooo-monero)c313bea4
eayslogging++: Fix bad memory access before opening any files (moneromooo-monero)0af5d168
easylogging++: avoid creating directory/filename for the builtin default log file (moneromooo-monero)28362847
easylogging++: allow setting thread names (moneromooo-monero)ec71ce8d
easylogging++: Print thread ID in a nicer way (moneromooo-monero)2a0bf783
easylogging++: Add logging categories (moneromooo-monero)c50bbbfe
easylogging++: import upstream (moneromooo-monero)
commit
ac7df193ca
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,319 @@
|
||||
// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Andrey N. Sabelnikov nor the
|
||||
// names of its contributors may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
|
||||
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
|
||||
|
||||
#ifndef _MLOG_H_
|
||||
#define _MLOG_H_
|
||||
|
||||
#include <atomic>
|
||||
#include "misc_log_ex.h"
|
||||
|
||||
INITIALIZE_EASYLOGGINGPP
|
||||
|
||||
//#define MLOG_BASE_FORMAT "%datetime{%Y-%M-%d %H:%m:%s.%g}\t%thread\t%level\t%logger\t%fbase:%line\t%msg"
|
||||
#define MLOG_BASE_FORMAT "%datetime{%Y-%M-%d %H:%m:%s.%g}\t%thread\t%level\t%logger\t%loc\t%msg"
|
||||
|
||||
using namespace epee;
|
||||
|
||||
static std::string generate_log_filename(const char *base)
|
||||
{
|
||||
std::string filename(base);
|
||||
char tmp[200];
|
||||
struct tm tm;
|
||||
time_t now = time(NULL);
|
||||
if (!gmtime_r(&now, &tm))
|
||||
strcpy(tmp, "unknown");
|
||||
else
|
||||
strftime(tmp, sizeof(tmp), "%Y-%m-%d-%H-%M-%S", &tm);
|
||||
filename += "-";
|
||||
filename += tmp;
|
||||
return filename;
|
||||
}
|
||||
|
||||
std::string mlog_get_default_log_path(const char *default_filename)
|
||||
{
|
||||
std::string process_name = epee::string_tools::get_current_module_name();
|
||||
std::string default_log_folder = epee::string_tools::get_current_module_folder();
|
||||
std::string default_log_file = process_name;
|
||||
std::string::size_type a = default_log_file.rfind('.');
|
||||
if ( a != std::string::npos )
|
||||
default_log_file.erase( a, default_log_file.size());
|
||||
if ( ! default_log_file.empty() )
|
||||
default_log_file += ".log";
|
||||
else
|
||||
default_log_file = default_filename;
|
||||
|
||||
return (boost::filesystem::path(default_log_folder) / boost::filesystem::path(default_log_file)).string();
|
||||
}
|
||||
|
||||
static void mlog_set_common_prefix()
|
||||
{
|
||||
static const char * const expected_filename = "contrib/epee/src/mlog.cpp";
|
||||
const char *path = __FILE__, *expected_ptr = strstr(path, expected_filename);
|
||||
if (!expected_ptr)
|
||||
return;
|
||||
el::Loggers::setFilenameCommonPrefix(std::string(path, expected_ptr - path));
|
||||
}
|
||||
|
||||
void mlog_configure(const std::string &filename_base, bool console)
|
||||
{
|
||||
el::Configurations c;
|
||||
c.setGlobally(el::ConfigurationType::Filename, filename_base);
|
||||
c.setGlobally(el::ConfigurationType::ToFile, "true");
|
||||
c.setGlobally(el::ConfigurationType::Format, MLOG_BASE_FORMAT);
|
||||
c.setGlobally(el::ConfigurationType::ToStandardOutput, console ? "true" : "false");
|
||||
c.setGlobally(el::ConfigurationType::MaxLogFileSize, "104850000"); // 100 MB - 7600 bytes
|
||||
el::Loggers::setDefaultConfigurations(c, true);
|
||||
|
||||
el::Loggers::addFlag(el::LoggingFlag::HierarchicalLogging);
|
||||
el::Loggers::addFlag(el::LoggingFlag::CreateLoggerAutomatically);
|
||||
el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog);
|
||||
el::Loggers::addFlag(el::LoggingFlag::ColoredTerminalOutput);
|
||||
el::Loggers::addFlag(el::LoggingFlag::StrictLogFileSizeCheck);
|
||||
el::Helpers::installPreRollOutCallback([&filename_base](const char *name, size_t){
|
||||
std::string rname = generate_log_filename(filename_base.c_str());
|
||||
rename(name, rname.c_str());
|
||||
});
|
||||
mlog_set_common_prefix();
|
||||
const char *monero_log = getenv("MONERO_LOGS");
|
||||
if (!monero_log)
|
||||
{
|
||||
monero_log = "*:WARNING,net*:FATAL,global:INFO,verify:FATAL";
|
||||
}
|
||||
mlog_set_categories(monero_log);
|
||||
}
|
||||
|
||||
void mlog_set_categories(const char *categories)
|
||||
{
|
||||
el::Loggers::setCategories(categories);
|
||||
MINFO("Mew log categories: " << categories);
|
||||
}
|
||||
|
||||
// maps epee style log level to new logging system
|
||||
void mlog_set_log_level(int level)
|
||||
{
|
||||
const char *settings = NULL;
|
||||
switch (level)
|
||||
{
|
||||
case 0:
|
||||
settings = "*:FATAL,net*:FATAL,global:INFO,verify:FATAL";
|
||||
break;
|
||||
case 1:
|
||||
settings = "*:WARNING,global:INFO";
|
||||
break;
|
||||
case 2:
|
||||
settings = "*:INFO";
|
||||
break;
|
||||
case 3:
|
||||
settings = "*:DEBUG";
|
||||
break;
|
||||
case 4:
|
||||
settings = "*:TRACE";
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
el::Loggers::setCategories(settings);
|
||||
MINFO("Mew log categories: " << settings);
|
||||
}
|
||||
|
||||
void mlog_set_log(const char *log)
|
||||
{
|
||||
long level;
|
||||
char *ptr = NULL;
|
||||
|
||||
level = strtoll(log, &ptr, 10);
|
||||
if (ptr && *ptr)
|
||||
{
|
||||
mlog_set_categories(log);
|
||||
}
|
||||
else if (level >= 0 && level <= 4)
|
||||
{
|
||||
mlog_set_log_level(level);
|
||||
}
|
||||
else
|
||||
{
|
||||
MERROR("Invalid numerical log level: " << log);
|
||||
}
|
||||
}
|
||||
|
||||
namespace epee
|
||||
{
|
||||
|
||||
bool is_stdout_a_tty()
|
||||
{
|
||||
static std::atomic<bool> initialized(false);
|
||||
static std::atomic<bool> is_a_tty(false);
|
||||
|
||||
if (!initialized.load(std::memory_order_acquire))
|
||||
{
|
||||
#if defined(WIN32)
|
||||
is_a_tty.store(0 != _isatty(_fileno(stdout)), std::memory_order_relaxed);
|
||||
#else
|
||||
is_a_tty.store(0 != isatty(fileno(stdout)), std::memory_order_relaxed);
|
||||
#endif
|
||||
initialized.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
return is_a_tty.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void set_console_color(int color, bool bright)
|
||||
{
|
||||
if (!is_stdout_a_tty())
|
||||
return;
|
||||
|
||||
switch(color)
|
||||
{
|
||||
case console_color_default:
|
||||
{
|
||||
#ifdef WIN32
|
||||
HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE| (bright ? FOREGROUND_INTENSITY:0));
|
||||
#else
|
||||
if(bright)
|
||||
std::cout << "\033[1;37m";
|
||||
else
|
||||
std::cout << "\033[0m";
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case console_color_white:
|
||||
{
|
||||
#ifdef WIN32
|
||||
HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | (bright ? FOREGROUND_INTENSITY:0));
|
||||
#else
|
||||
if(bright)
|
||||
std::cout << "\033[1;37m";
|
||||
else
|
||||
std::cout << "\033[0;37m";
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case console_color_red:
|
||||
{
|
||||
#ifdef WIN32
|
||||
HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | (bright ? FOREGROUND_INTENSITY:0));
|
||||
#else
|
||||
if(bright)
|
||||
std::cout << "\033[1;31m";
|
||||
else
|
||||
std::cout << "\033[0;31m";
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case console_color_green:
|
||||
{
|
||||
#ifdef WIN32
|
||||
HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | (bright ? FOREGROUND_INTENSITY:0));
|
||||
#else
|
||||
if(bright)
|
||||
std::cout << "\033[1;32m";
|
||||
else
|
||||
std::cout << "\033[0;32m";
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case console_color_blue:
|
||||
{
|
||||
#ifdef WIN32
|
||||
HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
SetConsoleTextAttribute(h_stdout, FOREGROUND_BLUE | FOREGROUND_INTENSITY);//(bright ? FOREGROUND_INTENSITY:0));
|
||||
#else
|
||||
if(bright)
|
||||
std::cout << "\033[1;34m";
|
||||
else
|
||||
std::cout << "\033[0;34m";
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case console_color_cyan:
|
||||
{
|
||||
#ifdef WIN32
|
||||
HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | FOREGROUND_BLUE | (bright ? FOREGROUND_INTENSITY:0));
|
||||
#else
|
||||
if(bright)
|
||||
std::cout << "\033[1;36m";
|
||||
else
|
||||
std::cout << "\033[0;36m";
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case console_color_magenta:
|
||||
{
|
||||
#ifdef WIN32
|
||||
HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
SetConsoleTextAttribute(h_stdout, FOREGROUND_BLUE | FOREGROUND_RED | (bright ? FOREGROUND_INTENSITY:0));
|
||||
#else
|
||||
if(bright)
|
||||
std::cout << "\033[1;35m";
|
||||
else
|
||||
std::cout << "\033[0;35m";
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case console_color_yellow:
|
||||
{
|
||||
#ifdef WIN32
|
||||
HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | (bright ? FOREGROUND_INTENSITY:0));
|
||||
#else
|
||||
if(bright)
|
||||
std::cout << "\033[1;33m";
|
||||
else
|
||||
std::cout << "\033[0;33m";
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void reset_console_color() {
|
||||
if (!is_stdout_a_tty())
|
||||
return;
|
||||
|
||||
#ifdef WIN32
|
||||
HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||
#else
|
||||
std::cout << "\033[0m";
|
||||
std::cout.flush();
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //_MLOG_H_
|
@ -1,18 +0,0 @@
|
||||
cmake_minimum_required (VERSION 2.6)
|
||||
project (otshell CXX)
|
||||
|
||||
# Add executable
|
||||
|
||||
if(APPLE AND POLICY CMP0042)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
endif()
|
||||
|
||||
file(GLOB otshell_utils_sources # All files in directory:
|
||||
"*.h"
|
||||
"*.hpp"
|
||||
"*.cpp"
|
||||
)
|
||||
|
||||
add_library (otshell_utils ${otshell_utils_sources})
|
||||
set_target_properties (otshell_utils PROPERTIES OUTPUT_NAME "otshell_utils")
|
||||
#target_link_libraries (upnpc-static ${LDLIBS}) # to add used libs
|
@ -1,21 +0,0 @@
|
||||
|
||||
This are some files also from OpenTransactions / otshell project,
|
||||
developed thanks to the awesome OpenTransaction project, organization and developers :)
|
||||
|
||||
Parts of code here was also developed thanks to the excellent Monero project,
|
||||
thanks to Monero project, organization and developers :)
|
||||
|
||||
[Some] files/code here (in external/otshell_utils) are under licence defined in
|
||||
src/doc/LICENCE-otshell.txt ;
|
||||
Others are from monero, with licence in src/doc/LICENCE-monero.txt ;
|
||||
|
||||
For me (rfree) the licence seem compatbile so no problem, personally (as author of many parts of the code,
|
||||
possibly not all) I do not worry who uses it how; I'am not a lawyer.
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Please share :-) This licence can be used e.g. for parts of code that are usable in both open-source FOSS project
|
||||
Monero and Open Transactions, to share and develop both faster.
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
|
@ -1,116 +0,0 @@
|
||||
#include "ccolor.hpp"
|
||||
#include <cstdarg>
|
||||
|
||||
// from http://stackoverflow.com/questions/2616906/how-do-i-output-coloured-text-to-a-linux-terminal
|
||||
// from http://wiznet.gr/src/ccolor.zip
|
||||
// edited by rfree - as part of https://github.com/rfree/Open-Transactions/
|
||||
|
||||
using namespace std;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
#define snprintf c99_snprintf
|
||||
|
||||
inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) {
|
||||
int count = -1;
|
||||
if (size != 0)
|
||||
count = _vsnprintf_s(str, size, _TRUNCATE, format, ap);
|
||||
if (count == -1)
|
||||
count = _vscprintf(format, ap);
|
||||
return count;
|
||||
}
|
||||
|
||||
inline int c99_snprintf(char* str, size_t size, const char* format, ...) {
|
||||
int count;
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
count = c99_vsnprintf(str, size, format, ap);
|
||||
va_end(ap);
|
||||
return count;
|
||||
}
|
||||
#endif // _MSC_VER
|
||||
|
||||
#define CC_CONSOLE_COLOR_DEFAULT "\033[0m"
|
||||
#define CC_FORECOLOR(C) "\033[" #C "m"
|
||||
#define CC_BACKCOLOR(C) "\033[" #C "m"
|
||||
#define CC_ATTR(A) "\033[" #A "m"
|
||||
|
||||
namespace zkr
|
||||
{
|
||||
enum Color
|
||||
{
|
||||
Black,
|
||||
Red,
|
||||
Green,
|
||||
Yellow,
|
||||
Blue,
|
||||
Magenta,
|
||||
Cyan,
|
||||
White,
|
||||
Default = 9
|
||||
};
|
||||
|
||||
enum Attributes
|
||||
{
|
||||
Reset,
|
||||
Bright,
|
||||
Dim,
|
||||
Underline,
|
||||
Blink,
|
||||
Reverse,
|
||||
Hidden
|
||||
};
|
||||
|
||||
char * cc::color(int attr, int fg, int bg)
|
||||
{
|
||||
static const int size = 20;
|
||||
static char command[size];
|
||||
|
||||
/* Command is the control command to the terminal */
|
||||
snprintf(command, size, "%c[%d;%d;%dm", 0x1B, attr, fg + 30, bg + 40);
|
||||
return command;
|
||||
}
|
||||
|
||||
|
||||
const char *cc::console = CC_CONSOLE_COLOR_DEFAULT;
|
||||
const char *cc::underline = CC_ATTR(4);
|
||||
const char *cc::bold = CC_ATTR(1);
|
||||
|
||||
const char *cc::fore::black = CC_FORECOLOR(30);
|
||||
const char *cc::fore::blue = CC_FORECOLOR(34);
|
||||
const char *cc::fore::red = CC_FORECOLOR(31);
|
||||
const char *cc::fore::magenta = CC_FORECOLOR(35);
|
||||
const char *cc::fore::green = CC_FORECOLOR(92);
|
||||
const char *cc::fore::cyan = CC_FORECOLOR(36);
|
||||
const char *cc::fore::yellow = CC_FORECOLOR(33);
|
||||
const char *cc::fore::white = CC_FORECOLOR(37);
|
||||
const char *cc::fore::console = CC_FORECOLOR(39);
|
||||
|
||||
const char *cc::fore::lightblack = CC_FORECOLOR(90);
|
||||
const char *cc::fore::lightblue = CC_FORECOLOR(94);
|
||||
const char *cc::fore::lightred = CC_FORECOLOR(91);
|
||||
const char *cc::fore::lightmagenta = CC_FORECOLOR(95);
|
||||
const char *cc::fore::lightgreen = CC_FORECOLOR(92);
|
||||
const char *cc::fore::lightcyan = CC_FORECOLOR(96);
|
||||
const char *cc::fore::lightyellow = CC_FORECOLOR(93);
|
||||
const char *cc::fore::lightwhite = CC_FORECOLOR(97);
|
||||
|
||||
const char *cc::back::black = CC_BACKCOLOR(40);
|
||||
const char *cc::back::blue = CC_BACKCOLOR(44);
|
||||
const char *cc::back::red = CC_BACKCOLOR(41);
|
||||
const char *cc::back::magenta = CC_BACKCOLOR(45);
|
||||
const char *cc::back::green = CC_BACKCOLOR(42);
|
||||
const char *cc::back::cyan = CC_BACKCOLOR(46);
|
||||
const char *cc::back::yellow = CC_BACKCOLOR(43);
|
||||
const char *cc::back::white = CC_BACKCOLOR(47);
|
||||
const char *cc::back::console = CC_BACKCOLOR(49);
|
||||
|
||||
const char *cc::back::lightblack = CC_BACKCOLOR(100);
|
||||
const char *cc::back::lightblue = CC_BACKCOLOR(104);
|
||||
const char *cc::back::lightred = CC_BACKCOLOR(101);
|
||||
const char *cc::back::lightmagenta = CC_BACKCOLOR(105);
|
||||
const char *cc::back::lightgreen = CC_BACKCOLOR(102);
|
||||
const char *cc::back::lightcyan = CC_BACKCOLOR(106);
|
||||
const char *cc::back::lightyellow = CC_BACKCOLOR(103);
|
||||
const char *cc::back::lightwhite = CC_BACKCOLOR(107);
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
// ccolor.hpp
|
||||
|
||||
// from http://stackoverflow.com/questions/2616906/how-do-i-output-coloured-text-to-a-linux-terminal
|
||||
// from http://wiznet.gr/src/ccolor.zip
|
||||
// edited by rfree - as part of https://github.com/rfree/Open-Transactions/
|
||||
|
||||
#ifndef INCLUDE_OT_ccolor
|
||||
#define INCLUDE_OT_ccolor
|
||||
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
|
||||
namespace zkr
|
||||
{
|
||||
class cc
|
||||
{
|
||||
public:
|
||||
|
||||
class fore
|
||||
{
|
||||
public:
|
||||
static const char *black;
|
||||
static const char *blue;
|
||||
static const char *red;
|
||||
static const char *magenta;
|
||||
static const char *green;
|
||||
static const char *cyan;
|
||||
static const char *yellow;
|
||||
static const char *white;
|
||||
static const char *console;
|
||||
|
||||
static const char *lightblack;
|
||||
static const char *lightblue;
|
||||
static const char *lightred;
|
||||
static const char *lightmagenta;
|
||||
static const char *lightgreen;
|
||||
static const char *lightcyan;
|
||||
static const char *lightyellow;
|
||||
static const char *lightwhite;
|
||||
};
|
||||
|
||||
class back
|
||||
{
|
||||
public:
|
||||
static const char *black;
|
||||
static const char *blue;
|
||||
static const char *red;
|
||||
static const char *magenta;
|
||||
static const char *green;
|
||||
static const char *cyan;
|
||||
static const char *yellow;
|
||||
static const char *white;
|
||||
static const char *console;
|
||||
|
||||
static const char *lightblack;
|
||||
static const char *lightblue;
|
||||
static const char *lightred;
|
||||
static const char *lightmagenta;
|
||||
static const char *lightgreen;
|
||||
static const char *lightcyan;
|
||||
static const char *lightyellow;
|
||||
static const char *lightwhite;
|
||||
};
|
||||
|
||||
static char *color(int attr, int fg, int bg);
|
||||
static const char *console;
|
||||
static const char *underline;
|
||||
static const char *bold;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,53 +0,0 @@
|
||||
/* See other files here for the LICENCE that applies here. */
|
||||
|
||||
|
||||
#ifndef INCLUDE_OT_NEWCLI_COMMON1
|
||||
#define INCLUDE_OT_NEWCLI_COMMON1
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <set>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/thread/recursive_mutex.hpp>
|
||||
#include <boost/thread/lock_guard.hpp>
|
||||
|
||||
|
||||
// list of thigs from libraries that we pull into namespace nOT::nNewcli
|
||||
// we might still need to copy/paste it in few places to make IDEs pick it up correctly
|
||||
#define INJECT_OT_COMMON_USING_NAMESPACE_COMMON_1 \
|
||||
using std::string; \
|
||||
using std::vector; \
|
||||
using std::vector; \
|
||||
using std::list; \
|
||||
using std::set; \
|
||||
using std::map; \
|
||||
using std::ostream; \
|
||||
using std::istream; \
|
||||
using std::cin; \
|
||||
using std::cerr; \
|
||||
using std::cout; \
|
||||
using std::cerr; \
|
||||
using std::endl; \
|
||||
using std::function; \
|
||||
using std::unique_ptr; \
|
||||
using std::shared_ptr; \
|
||||
using std::weak_ptr; \
|
||||
using std::enable_shared_from_this; \
|
||||
using boost::lock_guard; \
|
||||
|
||||
#endif
|
||||
|
@ -1,69 +0,0 @@
|
||||
/* See other files here for the LICENCE that applies here. */
|
||||
/* See header file .hpp for info */
|
||||
|
||||
#include "runoptions.hpp"
|
||||
|
||||
#include "lib_common1.hpp"
|
||||
|
||||
namespace nOT {
|
||||
|
||||
INJECT_OT_COMMON_USING_NAMESPACE_COMMON_1 // <=== namespaces
|
||||
|
||||
// (no debug - this is the default)
|
||||
// +nodebug (no debug)
|
||||
// +debug ...... --asdf
|
||||
// +debug +debugcerr .... --asfs
|
||||
// +debug +debugfile .... --asfs
|
||||
|
||||
cRunOptions::cRunOptions()
|
||||
: mRunMode(eRunModeCurrent), mDebug(false), mDebugSendToFile(false), mDebugSendToCerr(false)
|
||||
,mDoRunDebugshow(false)
|
||||
{ }
|
||||
|
||||
vector<string> cRunOptions::ExecuteRunoptionsAndRemoveThem(const vector<string> & args) {
|
||||
vector<string> arg_clear; // will store only the arguments that are not removed
|
||||
|
||||
for (auto arg : args) {
|
||||
bool thisIsRunoption=false;
|
||||
|
||||
if (arg.size()>0) {
|
||||
if (arg.at(0) == '+') thisIsRunoption=true;
|
||||
}
|
||||
|
||||
if (thisIsRunoption) Exec(arg); // ***
|
||||
if (! thisIsRunoption) arg_clear.push_back(arg);
|
||||
}
|
||||
|
||||
Normalize();
|
||||
|
||||
return arg_clear;
|
||||
}
|
||||
|
||||
void cRunOptions::Exec(const string & runoption) { // eg: Exec("+debug");
|
||||
if (runoption == "+nodebug") { mDebug=false; }
|
||||
else if (runoption == "+debug") { mDebug=true; }
|
||||
else if (runoption == "+debugcerr") { mDebug=true; mDebugSendToCerr=true; }
|
||||
else if (runoption == "+debugfile") { mDebug=true; mDebugSendToFile=true; }
|
||||
else if (runoption == "+demo") { mRunMode=eRunModeDemo; }
|
||||
else if (runoption == "+normal") { mRunMode=eRunModeNormal; }
|
||||
else if (runoption == "+current") { mRunMode=eRunModeCurrent; }
|
||||
else if (runoption == "+debugshow") { mDebug=true; mDebugSendToCerr=true; mDoRunDebugshow=true; }
|
||||
else {
|
||||
cerr << "Unknown runoption in Exec: '" << runoption << "'" << endl;
|
||||
throw std::runtime_error("Unknown runoption");
|
||||
}
|
||||
// cerr<<"debug="<<mDebug<<endl;
|
||||
}
|
||||
|
||||
void cRunOptions::Normalize() {
|
||||
if (mDebug) {
|
||||
if (!( mDebugSendToFile || mDebugSendToCerr )) mDebugSendToCerr=true; // if debug is on then send to something, e.g. to cerr
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cRunOptions gRunOptions; // (extern)
|
||||
|
||||
} // namespace OT
|
||||
|
||||
|
@ -1,58 +0,0 @@
|
||||
/* See other files here for the LICENCE that applies here. */
|
||||
/*
|
||||
Template for new files, replace word "template" and later delete this line here.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_OT_NEWCLI_runoptions_hpp
|
||||
#define INCLUDE_OT_NEWCLI_runoptions_hpp
|
||||
|
||||
#include "lib_common1.hpp"
|
||||
|
||||
namespace nOT {
|
||||
|
||||
INJECT_OT_COMMON_USING_NAMESPACE_COMMON_1 // <=== namespaces
|
||||
|
||||
/** Global options to run this program main() Eg used for developer's special options like +setdemo +setdebug.
|
||||
This is NOT for all the other options that are parsed and executed by program. */
|
||||
class cRunOptions {
|
||||
public:
|
||||
enum tRunMode { ///< Type of run mode - is this normal, or demonstration etc.
|
||||
eRunModeCurrent=1, ///< currently developed version
|
||||
eRunModeDemo, ///< best currently available Demo of something nice
|
||||
eRunModeNormal, ///< do the normal things that the program should do
|
||||
};
|
||||
|
||||
private:
|
||||
tRunMode mRunMode; ///< selected run mode
|
||||
|
||||
bool mDebug; // turn debug on, Eg: +debug without it probably nothing will be written to debug (maybe just error etc)
|
||||
bool mDebugSendToFile; // send to file, Eg: for +debugfile ; also turns on debug
|
||||
bool mDebugSendToCerr; // send to cerr, Eg: for +debugcerr ; also turns on debug
|
||||
// if debug is set but not any other DebugSend* then we will default to sending to debugcerr
|
||||
|
||||
bool mDoRunDebugshow;
|
||||
|
||||
public:
|
||||
tRunMode getTRunMode() const { return mRunMode; }
|
||||
bool getDebug() const { return mDebug; }
|
||||
bool getDebugSendToFile() const { return mDebugSendToFile; }
|
||||
bool getDebugSendToCerr() const { return mDebugSendToCerr; }
|
||||
bool getDoRunDebugshow() const { return mDoRunDebugshow; }
|
||||
|
||||
cRunOptions();
|
||||
|
||||
vector<string> ExecuteRunoptionsAndRemoveThem(const vector<string> & args);
|
||||
void Exec(const string & runoption); // eg: Exec("+debug");
|
||||
|
||||
void Normalize();
|
||||
};
|
||||
|
||||
extern cRunOptions gRunOptions;
|
||||
|
||||
|
||||
} // namespace nOT
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -1,806 +0,0 @@
|
||||
/// @file
|
||||
/// @author rfree (current maintainer in monero.cc project)
|
||||
/// @brief various general utils taken from (and relate to) otshell project, including loggiang/debug
|
||||
|
||||
/* See other files here for the LICENCE that applies here. */
|
||||
/* See header file .hpp for info */
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <cctype>
|
||||
#include <locale>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <chrono>
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
#include "ccolor.hpp"
|
||||
|
||||
#include "lib_common1.hpp"
|
||||
|
||||
#include "runoptions.hpp"
|
||||
|
||||
#if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined (WIN64)
|
||||
#define OS_TYPE_WINDOWS
|
||||
#elif defined(__unix__) || defined(__posix) || defined(__linux) || defined(__darwin) || defined(__APPLE__) || defined(__clang__)
|
||||
#define OS_TYPE_POSIX
|
||||
#else
|
||||
#warning "Compiler/OS platform is not recognized. Just assuming it will work as POSIX then"
|
||||
#define OS_TYPE_POSIX
|
||||
#endif
|
||||
|
||||
#if defined(OS_TYPE_WINDOWS)
|
||||
#include <windows.h>
|
||||
#include <process.h>
|
||||
#elif defined(OS_TYPE_POSIX)
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#error "Compiler/OS platform detection failed - not supported"
|
||||
#endif
|
||||
|
||||
|
||||
namespace nOT {
|
||||
namespace nUtils {
|
||||
|
||||
INJECT_OT_COMMON_USING_NAMESPACE_COMMON_1 // <=== namespaces
|
||||
|
||||
// ====================================================================
|
||||
|
||||
// Numerical values of the debug levels - see hpp
|
||||
const int _debug_level_nr_dbg3=20;
|
||||
const int _debug_level_nr_dbg2=30;
|
||||
const int _debug_level_nr_dbg1=40;
|
||||
const int _debug_level_nr_info=50;
|
||||
const int _debug_level_nr_note=60;
|
||||
const int _debug_level_nr_fact=75;
|
||||
const int _debug_level_nr_mark=80;
|
||||
const int _debug_level_nr_warn=90;
|
||||
const int _debug_level_nr_erro=100;
|
||||
|
||||
// ====================================================================
|
||||
|
||||
myexception::myexception(const char * what)
|
||||
: std::runtime_error(what)
|
||||
{ }
|
||||
|
||||
myexception::myexception(const std::string &what)
|
||||
: std::runtime_error(what)
|
||||
{ }
|
||||
|
||||
void myexception::Report() const {
|
||||
_erro("Error: " << what());
|
||||
}
|
||||
|
||||
//myexception::~myexception() { }
|
||||
|
||||
// ====================================================================
|
||||
|
||||
// text trimming
|
||||
// http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
|
||||
std::string & ltrim(std::string &s) {
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string & rtrim(std::string &s) {
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string & trim(std::string &s) {
|
||||
return ltrim(rtrim(s));
|
||||
}
|
||||
|
||||
std::string get_current_time() {
|
||||
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
|
||||
time_t time_now = std::chrono::system_clock::to_time_t(now);
|
||||
std::chrono::high_resolution_clock::duration duration = now.time_since_epoch();
|
||||
int64_t micro = std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
|
||||
|
||||
// std::localtime() - This function may not be thread-safe.
|
||||
#ifdef OS_TYPE_WINDOWS
|
||||
struct tm * tm_pointer = std::localtime( &time_now ); // thread-safe on mingw-w64 (thread local variable) and on MSVC btw
|
||||
// http://stackoverflow.com/questions/18551409/localtime-r-support-on-mingw
|
||||
// tm_pointer points to thread-local data, memory is owned/managed by the system/library
|
||||
#else
|
||||
// linux, freebsd, have this
|
||||
struct tm tm_object; // automatic storage duration http://en.cppreference.com/w/cpp/language/storage_duration
|
||||
struct tm * tm_pointer = & tm_object; // just point to our data
|
||||
auto x = localtime_r( &time_now , tm_pointer ); // modifies our own (this thread) data in tm_object, this is safe http://linux.die.net/man/3/localtime_r
|
||||
if (x != tm_pointer) return "(internal error in get_current_time)"; // redundant check in case of broken implementation of localtime_r
|
||||
#endif
|
||||
// tm_pointer now points to proper time data, and that memory is automatically managed
|
||||
if (!tm_pointer) return "(internal error in get_current_time - NULL)"; // redundant check in case of broken implementation of used library methods
|
||||
|
||||
std::stringstream stream;
|
||||
stream << std::setfill('0')
|
||||
<< std::setw(2) << tm_pointer->tm_year+1900
|
||||
<< '-' << std::setw(2) << tm_pointer->tm_mon+1
|
||||
<< '-' << std::setw(2) << tm_pointer->tm_mday
|
||||
<< ' ' << std::setw(2) << tm_pointer->tm_hour
|
||||
<< ':' << std::setw(2) << tm_pointer->tm_min
|
||||
<< ':' << std::setw(2) << tm_pointer->tm_sec
|
||||
<< '.' << std::setw(6) << (micro%1000000); // 6 because microseconds
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
cNullstream g_nullstream; // extern a stream that does nothing (eats/discards data)
|
||||
|
||||
boost::recursive_mutex gLoggerGuard; // extern
|
||||
std::atomic<int> gLoggerGuardDepth; // extern
|
||||
|
||||
std::atomic<int> & gLoggerGuardDepth_Get() {
|
||||
// TODO std::once would be nicer here
|
||||
|
||||
static bool once=0;
|
||||
|
||||
if (!once) { // initialize it once
|
||||
once=1;
|
||||
gLoggerGuardDepth=0;
|
||||
}
|
||||
|
||||
return gLoggerGuardDepth; // global, atomic counter
|
||||
}
|
||||
|
||||
|
||||
// ====================================================================
|
||||
|
||||
namespace nDetail {
|
||||
|
||||
const char* DbgShortenCodeFileName(const char *s) {
|
||||
const char *p = s;
|
||||
const char *a = s;
|
||||
|
||||
bool inc=1;
|
||||
while (*p) {
|
||||
++p;
|
||||
if (inc && ('\0' != * p)) { a=p; inc=false; } // point to the current character (if valid) becasue previous one was slash
|
||||
if ((*p)=='/') { a=p; inc=true; } // point at current slash (but set inc to try to point to next character)
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// a workaround for MSVC compiler; e.g. see https://bugs.webkit.org/show_bug.cgi?format=multiple&id=125795
|
||||
#ifndef _MSC_VER
|
||||
template<typename T, typename ...Args>
|
||||
std::unique_ptr<T> make_unique( Args&& ...args )
|
||||
{
|
||||
return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
|
||||
}
|
||||
#else
|
||||
using std::make_unique;
|
||||
#endif
|
||||
// ====================================================================
|
||||
|
||||
char cFilesystemUtils::GetDirSeparatorSys() {
|
||||
// TODO nicer os detection?
|
||||
#if defined(OS_TYPE_POSIX)
|
||||
return '/';
|
||||
#elif defined(OS_TYPE_WINDOWS)
|
||||
return '\\';
|
||||
#else
|
||||
#error "Do not know how to compile this for your platform."
|
||||
#endif
|
||||
}
|
||||
|
||||
char cFilesystemUtils::GetDirSeparatorInter() {
|
||||
return '/';
|
||||
}
|
||||
|
||||
string cFilesystemUtils::FileInternalToSystem(const std::string &name) {
|
||||
string ret;
|
||||
ret.resize(name.size());
|
||||
std::replace_copy(name.begin(), name.end(), ret.begin(),
|
||||
GetDirSeparatorInter() , GetDirSeparatorSys());
|
||||
return ret;
|
||||
}
|
||||
|
||||
string cFilesystemUtils::FileSystemToInternal(const std::string &name) {
|
||||
string ret;
|
||||
ret.reserve(name.size());
|
||||
std::replace_copy(name.begin(), name.end(), ret.begin(),
|
||||
GetDirSeparatorSys() , GetDirSeparatorInter());
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool cFilesystemUtils::CreateDirTree(const std::string & dir, bool only_below) {
|
||||
const bool dbg=false;
|
||||
//struct stat st;
|
||||
const char dirchS = cFilesystemUtils::GetDirSeparatorSys();
|
||||
const char dirchI = cFilesystemUtils::GetDirSeparatorInter();
|
||||
std::istringstream iss(dir);
|
||||
string partI; // current par is in internal format (though it should not matter since it doesn't contain any slashes). eg "bar"
|
||||
string sofarS=""; // sofarS - the so far created dir part is in SYSTEM format. eg "foo/bar"
|
||||
if (dir.size()<1) return false; // illegal name
|
||||
// dir[0] is valid from here
|
||||
if ( only_below && ((dir[0]==dirchS) || (dir[0]==dirchI))) return false; // no jumping to top (on any os)
|
||||
|
||||
while (getline(iss,partI,dirchI)) { // get new component eg "bar" into part
|
||||
if (dbg) cout << '['<<partI<<']' << endl;
|
||||
sofarS += partI;
|
||||
if (partI.size()<1) return false; // bad format?
|
||||
if ((only_below) && (partI=="..")) return false; // trying to go up
|
||||
|
||||
if (dbg) cout << "test ["<<sofarS<<"]"<<endl;
|
||||
// TODO nicer os detection?
|
||||
#if defined(OS_TYPE_POSIX)
|
||||
struct stat st;
|
||||
bool exists = stat(sofarS.c_str() ,&st) == 0; // *
|
||||
if (exists) {
|
||||
if (! S_ISDIR(st.st_mode)) {
|
||||
// std::cerr << "This exists, but as a file: [" << sofar << "]" << (size_t)st.st_ino << endl;
|
||||
return false; // exists but is a file nor dir
|
||||
}
|
||||
}
|
||||
#elif defined(OS_TYPE_WINDOWS)
|
||||
DWORD dwAttrib = GetFileAttributesA(sofarS.c_str());
|
||||
bool exists = (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
|
||||
#else
|
||||
#error "Do not know how to compile this for your platform."
|
||||
#endif
|
||||
|
||||
if (!exists) {
|
||||
if (dbg) cout << "mkdir ["<<sofarS<<"]"<<endl;
|
||||
#if defined(OS_TYPE_POSIX)
|
||||
bool ok = 0== mkdir(sofarS.c_str(), 0700); // ***
|
||||
#elif defined(OS_TYPE_WINDOWS)
|
||||
bool ok = (bool) CreateDirectoryA(sofarS.c_str(), NULL); // TODO use -W() after conversion to unicode UTF16
|
||||
#else
|
||||
#error "Do not know how to compile this for your platform."
|
||||
#endif
|
||||
if (!ok) return false;
|
||||
}
|
||||
sofarS += dirchS;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// ====================================================================
|
||||
|
||||
namespace nDetail {
|
||||
|
||||
struct channel_use_info { ///< feedback information about using (e.g. opening) given debug channel - used internally by logging system
|
||||
/// TODO not yet used in code
|
||||
/// e.g. used to write into channel net/in/all that given message was a first logged message from never-before-logged thread or PID etc
|
||||
bool m_was_interesting; ///< anything interesting happened when using the channel?
|
||||
std::vector<std::string> m_extra_msg; ///< any additional messages about this channel use
|
||||
};
|
||||
|
||||
cDebugScopeGuard::cDebugScopeGuard() : mLevel(-1) {
|
||||
}
|
||||
|
||||
cDebugScopeGuard::~cDebugScopeGuard() {
|
||||
if (mLevel != -1) {
|
||||
gCurrentLogger.write_stream(mLevel,mChan) << mMsg << " ... end" << gCurrentLogger.endline() << std::flush;
|
||||
}
|
||||
}
|
||||
|
||||
void cDebugScopeGuard::Assign(const string &chan, const int level, const string &msg) {
|
||||
mChan=chan;
|
||||
mLevel=level;
|
||||
mMsg=msg;
|
||||
}
|
||||
|
||||
} // namespace nDetail
|
||||
|
||||
// ====================================================================
|
||||
|
||||
cLogger::cLogger() :
|
||||
mStream(NULL),
|
||||
mStreamBrokenDebug(NULL),
|
||||
mIsBroken(true), // before constructor finishes
|
||||
mLevel(_debug_level_nr_warn),
|
||||
mThread2Number_Biggest(0), // the CURRENT biggest value (no thread yet in map)
|
||||
mPid2Number_Biggest(0)
|
||||
{
|
||||
mStream = & std::cout;
|
||||
mStreamBrokenDebug = & std::cerr; // the backup stream
|
||||
*mStreamBrokenDebug << "Creating the logger system" << endl;
|
||||
mIsBroken=false; // ok, constr. succeeded, so string is not broken now
|
||||
|
||||
// this is here, because it could be using logging itself to log creation of first thread/PID etc
|
||||
Thread2Number( boost::this_thread::get_id() ); // convert current id to short number, useful to reserve a number so that main thread is usually called 1
|
||||
Pid2Number( getpid() ); // add this proces ID as first one
|
||||
}
|
||||
|
||||
cLogger::~cLogger() {
|
||||
for (auto pair : mChannels) {
|
||||
std::ofstream *ptr = pair.second;
|
||||
delete ptr;
|
||||
pair.second=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void cLogger::SetStreamBroken() {
|
||||
SetStreamBroken("(no additional details about this problem)");
|
||||
}
|
||||
|
||||
void cLogger::SetStreamBroken(const std::string &msg) {
|
||||
_dbg_dbg("Stream is broken (msg: " << msg << ")");
|
||||
if (!mIsBroken) { // if not already marked as broken
|
||||
_dbg_dbg("(It was not broken before)");
|
||||
std::cerr << OT_CODE_STAMP << "WARNING: due to a problem in the debug/logging system itself ("<<msg<<") - we are switching back to fallback stream (e.g. cerr)" << std::endl;
|
||||
if (mStreamBrokenDebug == nullptr) {
|
||||
std::cerr << OT_CODE_STAMP << " ERROR: in addition, while reporting this problem, mStreamBrokenDebug stream is NULL: " << mStreamBrokenDebug << std::endl;
|
||||
} else {
|
||||
(*mStreamBrokenDebug) << OT_CODE_STAMP << "WARNING: due to debug stream problem ("<<msg<<") - switching back to fallback stream (e.g. cerr)" << std::endl;
|
||||
}
|
||||
mIsBroken = true;
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream & cLogger::write_stream(int level) {
|
||||
return write_stream(level,"");
|
||||
}
|
||||
|
||||
std::ostream & cLogger::write_stream(int level, const std::string & channel ) {
|
||||
_dbg_dbg("level="<<level<<" channel="<<channel);
|
||||
if (level >= mLevel) {
|
||||
if (mStream) { // TODO now disabling mStream also disables writting to any channel
|
||||
_dbg_dbg("Selecting output...");
|
||||
ostream & output = SelectOutput(level,channel);
|
||||
_dbg_dbg("Selecting output... done, output=" << (void*)(&output));
|
||||
#if defined(OS_TYPE_WINDOWS)
|
||||
output << windows_stream(level);
|
||||
#endif
|
||||
output << icon(level) << ' ';
|
||||
boost::thread::id this_id = boost::this_thread::get_id();
|
||||
output << "{" << Thread2Number(this_id) << "}";
|
||||
auto nicePid = Pid2Number(getpid());
|
||||
if (nicePid>0) output << " {p" << nicePid << "}";
|
||||
output << ' ';
|
||||
return output; // <--- return
|
||||
} else _dbg_dbg("Not writting: No mStream");
|
||||
} else _dbg_dbg("Not writting: Too low level level="<<level<<" not >= mLevel="<<mLevel);
|
||||
return g_nullstream;
|
||||
}
|
||||
|
||||
std::string cLogger::GetLogBaseDir() const {
|
||||
return "log";
|
||||
}
|
||||
|
||||
void cLogger::OpenNewChannel(const std::string & channel) noexcept {
|
||||
try {
|
||||
_dbg_dbg("Openning channel for channel="<<channel);
|
||||
OpenNewChannel_(channel);
|
||||
}
|
||||
catch (const std::exception &except) {
|
||||
SetStreamBroken(OT_CODE_STAMP + " Got exception when opening debug channel: " + ToStr(except.what()));
|
||||
}
|
||||
catch (...) {
|
||||
SetStreamBroken(OT_CODE_STAMP + " Got not-standard exception when opening debug channel.");
|
||||
}
|
||||
}
|
||||
|
||||
void cLogger::OpenNewChannel_(const std::string & channel) { // channel=="net/sleep"
|
||||
_dbg_dbg("Openning channel for channel="<<channel);
|
||||
size_t last_split = channel.find_last_of(cFilesystemUtils::GetDirSeparatorInter());
|
||||
|
||||
string fname_system; // the full file name in system format
|
||||
|
||||
if (last_split==string::npos) { // The channel name has no directory, eg channel=="test"
|
||||
string dir = GetLogBaseDir();
|
||||
string basefile = channel + ".log";
|
||||
string fname = dir + cFilesystemUtils::GetDirSeparatorInter() + basefile;
|
||||
fname_system = cFilesystemUtils::FileInternalToSystem(fname); // <-
|
||||
}
|
||||
else { // there is a directory eg channel=="net/sleep"
|
||||
// net/sleep
|
||||
// ^----- last_split
|
||||
string dir = GetLogBaseDir() + cFilesystemUtils::GetDirSeparatorInter() + channel.substr(0, last_split);
|
||||
string basefile = channel.substr(last_split+1) + ".log";
|
||||
string fname = dir + cFilesystemUtils::GetDirSeparatorInter() + basefile;
|
||||
fname_system = cFilesystemUtils::FileInternalToSystem(fname); // <-
|
||||
bool dirok = cFilesystemUtils::CreateDirTree(dir);
|
||||
if (!dirok) { string err = "In logger failed to open directory (" + dir +") for channel (" + channel +")"; throw std::runtime_error(err); }
|
||||
}
|
||||
|
||||
_dbg_dbg("Openning fname_system="<<fname_system);
|
||||
std::ofstream * thefile = new std::ofstream( fname_system.c_str() ); // file system
|
||||
*thefile << "====== Log opened: " << fname_system << " (in " << ((void*)thefile) << ") ======" << endl;
|
||||
// cerr << "====== Log opened: " << fname_system << " (in " << ((void*)thefile) << ") ======" << endl;
|
||||
_dbg_dbg( "====== Log opened: " << fname_system << " (in " << ((void*)thefile) << ") ======" );
|
||||
mChannels.insert( std::pair<string,std::ofstream*>(channel , thefile ) ); // <- created the channel mapping
|
||||
}
|
||||
|
||||
std::ostream & cLogger::SelectOutput(int level, const std::string & channel) noexcept {
|
||||
try {
|
||||
if (mIsBroken) {
|
||||
_dbg_dbg("The stream is broken mIsBroken="<<mIsBroken<<" so will return backup stream");
|
||||
return *mStreamBrokenDebug;
|
||||
}
|
||||
if (channel=="") {
|
||||
_dbg_dbg("No channel given (channel="<<channel<<") so will return main stream");
|
||||
return *mStream;
|
||||
}
|
||||
|
||||
auto obj = mChannels.find(channel);
|
||||
if (obj == mChannels.end()) { // not found - need to make new channel
|
||||
_dbg_dbg("No stream openened for channel="<<channel<<" so will create it now");
|
||||
OpenNewChannel(channel); // <- create channel
|
||||
obj = mChannels.find(channel); // find again
|
||||
if (obj == mChannels.end()) { // still not found! something is wrong
|
||||
SetStreamBroken( OT_CODE_STAMP + " WARNING: can not get stream for channel="+ToStr(channel)+" level="+ToStr(channel) );
|
||||
return *mStreamBrokenDebug;
|
||||
}
|
||||
}
|
||||
auto the_stream_ptr = obj->second;
|
||||
_dbg_dbg("Found the stream file for channel="<<channel<<" as the_stream_ptr="<<the_stream_ptr);
|
||||
ASRT(the_stream_ptr);
|
||||
return *the_stream_ptr; // <--- RETURN
|
||||
}
|
||||
catch (std::exception &except) {
|
||||
SetStreamBroken( OT_CODE_STAMP + " Got exception: " + ToStr(except.what()) );
|
||||
_dbg_dbg("Exception! Returning broken stream");
|
||||
return *mStreamBrokenDebug;
|
||||
}
|
||||
catch (...) {
|
||||
SetStreamBroken( OT_CODE_STAMP + " Got not-standard exception.");
|
||||
_dbg_dbg("Exception! Returning broken stream");
|
||||
return *mStreamBrokenDebug;
|
||||
}
|
||||
|
||||
// dead code
|
||||
}
|
||||
|
||||
void cLogger::setOutStreamFile(const string &fname) { // switch to using this file
|
||||
_mark("WILL SWITCH DEBUG NOW to file: " << fname);
|
||||
mOutfile = make_unique<std::ofstream>(fname);
|
||||
mStream = & (*mOutfile);
|
||||
_mark("Started new debug, to file: " << fname);
|
||||
}
|
||||
|
||||
void cLogger::setOutStreamFromGlobalOptions() {
|
||||
if ( gRunOptions.getDebug() ) {
|
||||
if ( gRunOptions.getDebugSendToFile() ) {
|
||||
mOutfile = make_unique<std::ofstream> ("debuglog.txt");
|
||||
mStream = & (*mOutfile);
|
||||
}
|
||||
else if ( gRunOptions.getDebugSendToCerr() ) {
|
||||
mStream = & std::cerr;
|
||||
}
|
||||
else {
|
||||
mStream = & g_nullstream;
|
||||
}
|
||||
}
|
||||
else {
|
||||
mStream = & g_nullstream;
|
||||
}
|
||||
}
|
||||
|
||||
void cLogger::setDebugLevel(int level) {
|
||||
bool note_before = (mLevel > level); // report the level change before or after the change? (on higher level)
|
||||
if (note_before) _note("Setting debug level to "<<level);
|
||||
mLevel = level;
|
||||
if (!note_before) _note("Setting debug level to "<<level);
|
||||
}
|
||||
|
||||
std::string cLogger::icon(int level) const {
|
||||
// TODO replan to avoid needles converting back and forth char*, string etc
|
||||
|
||||
using namespace zkr;
|
||||
#if defined(OS_TYPE_POSIX)
|
||||
if (level >= 100) return cc::back::lightred + ToStr(cc::fore::lightyellow) + ToStr("ERROR ") + ToStr(cc::fore::lightyellow) + " " ;
|
||||
if (level >= 90) return cc::back::lightyellow + ToStr(cc::fore::black) + ToStr("Warn ") + ToStr(cc::fore::red)+ " " ;
|
||||
if (level >= 80) return cc::back::lightmagenta + ToStr(cc::fore::black) + ToStr("MARK "); //+ zkr::cc::console + ToStr(cc::fore::lightmagenta)+ " ";
|
||||
if (level >= 75) return cc::back::lightyellow + ToStr(cc::fore::black) + ToStr("FACT ") + zkr::cc::console + ToStr(cc::fore::lightyellow)+ " ";
|
||||
if (level >= 70) return cc::fore::green + ToStr("Note ");
|
||||
if (level >= 50) return cc::fore::cyan + ToStr("info ");
|
||||
if (level >= 40) return cc::fore::lightwhite + ToStr("dbg ");
|
||||
if (level >= 30) return cc::fore::lightblue + ToStr("dbg ");
|
||||
if (level >= 20) return cc::fore::blue + ToStr("dbg ");
|
||||
|
||||
#elif defined(OS_TYPE_WINDOWS)
|
||||
if (level >= 100) return ToStr("ERROR ");
|
||||
if (level >= 90) return ToStr("Warn ");
|
||||
if (level >= 80) return ToStr("MARK ");
|
||||
if (level >= 75) return ToStr("FACT ");
|
||||
if (level >= 70) return ToStr("Note ");
|
||||
if (level >= 50) return ToStr("info ");
|
||||
if (level >= 40) return ToStr("dbg ");
|
||||
if (level >= 30) return ToStr("dbg ");
|
||||
if (level >= 20) return ToStr("dbg ");
|
||||
#endif
|
||||
|
||||
return " ";
|
||||
}
|
||||
|
||||
std::string cLogger::endline() const {
|
||||
#if defined(OS_TYPE_POSIX)
|
||||
return ToStr("") + zkr::cc::console + ToStr("\n"); // TODO replan to avoid needles converting back and forth char*, string etc
|
||||
#elif defined(OS_TYPE_WINDOWS)
|
||||
return ToStr("\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
int cLogger::Thread2Number(const boost::thread::id id) {
|
||||
auto found = mThread2Number.find( id );
|
||||
if (found == mThread2Number.end()) { // new one
|
||||
mThread2Number_Biggest++;
|
||||
mThread2Number[id] = mThread2Number_Biggest;
|
||||
_info_c("dbg/main", "This is a new thread (used in debug), thread id="<<id); // can cause some recursion
|
||||
return mThread2Number_Biggest;
|
||||
} else {
|
||||
return mThread2Number[id];
|
||||
}
|
||||
}
|
||||
|
||||
int cLogger::Pid2Number(const t_anypid id) {
|
||||
auto found = mPid2Number.find( id );
|
||||
if (found == mPid2Number.end()) { // new one
|
||||
mPid2Number_Biggest++;
|
||||
mPid2Number[id] = mPid2Number_Biggest;
|
||||
_info_c("dbg/main", "This is a new process (used in debug), process pid="<<id); // can cause some recursion
|
||||
return mPid2Number_Biggest;
|
||||
} else {
|
||||
return mPid2Number[id];
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// object gCurrentLogger is defined later - in global namespace below
|
||||
|
||||
|
||||
// ====================================================================
|
||||
// vector debug
|
||||
|
||||
void DisplayStringEndl(std::ostream & out, const std::string text) {
|
||||
out << text;
|
||||
out << std::endl;
|
||||
}
|
||||
|
||||
std::string SpaceFromEscape(const std::string &s) {
|
||||
std::ostringstream newStr;
|
||||
for(size_t i = 0; i < s.length();i++) {
|
||||
if(s[i] == '\\' && s[i+1] ==32)
|
||||
newStr<<"";
|
||||
else
|
||||
newStr<<s[i];
|
||||
}
|
||||
return newStr.str();
|
||||
}
|
||||
|
||||
std::string EscapeFromSpace(const std::string &s) {
|
||||
std::ostringstream newStr;
|
||||
for(size_t i = 0; i < s.length();i++) {
|
||||
if(s[i] == 32)
|
||||
newStr << "\\" << " ";
|
||||
else
|
||||
newStr << s[i];
|
||||
}
|
||||
return newStr.str();
|
||||
}
|
||||
|
||||
|
||||
std::string EscapeString(const std::string &s) {
|
||||
std::ostringstream newStr;
|
||||
for(size_t i = 0; i < s.length();i++) {
|
||||
if(s[i] >=32 && s[i] <= 126)
|
||||
newStr<<s[i];
|
||||
else
|
||||
newStr<<"\\"<< (int) s[i];
|
||||
}
|
||||
|
||||
return newStr.str();
|
||||
}
|
||||
|
||||
|
||||
bool CheckIfBegins(const std::string & beggining, const std::string & all) {
|
||||
if (all.compare(0, beggining.length(), beggining) == 0) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool CheckIfEnds (std::string const & ending, std::string const & all){
|
||||
if (all.length() >= ending.length()) {
|
||||
return (0 == all.compare (all.length() - ending.length(), ending.length(), ending));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
vector<string> WordsThatMatch(const std::string & sofar, const vector<string> & possib) {
|
||||
vector<string> ret;
|
||||
for ( auto rec : possib) { // check of possibilities
|
||||
if (CheckIfBegins(sofar,rec)) {
|
||||
rec = EscapeFromSpace(rec);
|
||||
ret.push_back(rec); // this record matches
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
char GetLastChar(const std::string & str) { // TODO unicode?
|
||||
auto s = str.length();
|
||||
if (s==0) throw std::runtime_error("Getting last character of empty string (" + ToStr(s) + ")" + OT_CODE_STAMP);
|
||||
return str.at( s - 1);
|
||||
}
|
||||
|
||||
std::string GetLastCharIf(const std::string & str) { // TODO unicode?
|
||||
auto s = str.length();
|
||||
if (s==0) return ""; // empty string signalizes ther is nothing to be returned
|
||||
return std::string( 1 , str.at( s - 1) );
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
|
||||
// ASRT - assert. Name like ASSERT() was too long, and ASS() was just... no.
|
||||
// Use it like this: ASRT( x>y ); with the semicolon at end, a clever trick forces this syntax :)
|
||||
|
||||
void Assert(bool result, const std::string &stamp, const std::string &condition) {
|
||||
if (!result) {
|
||||
_erro("Assert failed at "+stamp+": ASSERT( " << condition << ")");
|
||||
throw std::runtime_error("Assert failed at "+stamp+": ASSERT( " + condition + ")");
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// advanced string
|
||||
|
||||
const std::string GetMultiline(string endLine) {
|
||||
std::string result(""); // Taken from OT_CLI_ReadUntilEOF
|
||||
while (true) {
|
||||
std::string input_line("");
|
||||
if (std::getline(std::cin, input_line, '\n'))
|
||||
{
|
||||
input_line += "\n";
|
||||
if (input_line[0] == '~')
|
||||
break;
|
||||
result += input_line;
|
||||
}
|
||||
if (std::cin.eof() )
|
||||
{
|
||||
std::cin.clear();
|
||||
break;
|
||||
}
|
||||
if (std::cin.fail() )
|
||||
{
|
||||
std::cin.clear();
|
||||
break;
|
||||
}
|
||||
if (std::cin.bad())
|
||||
{
|
||||
std::cin.clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<string> SplitString(const string & str){
|
||||
std::istringstream iss(str);
|
||||
vector<string> vec { std::istream_iterator<string>{iss}, std::istream_iterator<string>{} };
|
||||
return vec;
|
||||
}
|
||||
|
||||
bool checkPrefix(const string & str, char prefix) {
|
||||
if (str.at(0) == prefix)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// operation on files
|
||||
|
||||
|
||||
#ifdef __unix
|
||||
|
||||
void cEnvUtils::GetTmpTextFile() {
|
||||
// TODO make this name configurable (depending on project)
|
||||
char filename[] = "/tmp/otshellutils_text.XXXXXX";
|
||||
fd = mkstemp(filename);
|
||||
if (fd == -1) {
|
||||
_erro("Can't create the file: " << filename);
|
||||
return;
|
||||
}
|
||||
mFilename = filename;
|
||||
}
|
||||
|
||||
void cEnvUtils::CloseFile() {
|
||||
close(fd);
|
||||
unlink( mFilename.c_str() );
|
||||
}
|
||||
|
||||
void cEnvUtils::OpenEditor() {
|
||||
char* editor = std::getenv("OT_EDITOR"); //TODO Read editor from configuration file
|
||||
if (editor == NULL)
|
||||
editor = std::getenv("VISUAL");
|
||||
if (editor == NULL)
|
||||
editor = std::getenv("EDITOR");
|
||||
|
||||
string command;
|
||||
if (editor != NULL)
|
||||
command = ToStr(editor) + " " + mFilename;
|
||||
else
|
||||
command = "/usr/bin/editor " + mFilename;
|
||||
_dbg3("Opening editor with command: " << command);
|
||||
if ( system( command.c_str() ) == -1 )
|
||||
_erro("Cannot execute system command: " << command);
|
||||
}
|
||||
|
||||
const string cEnvUtils::ReadFromTmpFile() {
|
||||
std::ifstream ifs(mFilename);
|
||||
string msg((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
||||
return msg;
|
||||
}
|
||||
|
||||
const string cEnvUtils::Compose() {
|
||||
GetTmpTextFile();
|
||||
OpenEditor();
|
||||
string input = ReadFromTmpFile();
|
||||
CloseFile();
|
||||
return input;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
const string cEnvUtils::ReadFromFile(const string path) {
|
||||
std::ifstream ifs(path);
|
||||
string msg((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
||||
return msg;
|
||||
}
|
||||
|
||||
void hintingToTxt(std::fstream & file, string command, vector<string> &commands) {
|
||||
if(file.good()) {
|
||||
file<<command<<"~"<<endl;
|
||||
for (auto a: commands) {
|
||||
file <<a<< " ";
|
||||
file.flush();
|
||||
}
|
||||
file<<endl;
|
||||
}
|
||||
}
|
||||
|
||||
string stringToColor(const string &hash) {
|
||||
// Generete vector with all possible light colors
|
||||
vector <string> lightColors;
|
||||
using namespace zkr;
|
||||
lightColors.push_back(cc::fore::lightblue);
|
||||
lightColors.push_back(cc::fore::lightred);
|
||||
lightColors.push_back(cc::fore::lightmagenta);
|
||||
lightColors.push_back(cc::fore::lightgreen);
|
||||
lightColors.push_back(cc::fore::lightcyan);
|
||||
lightColors.push_back(cc::fore::lightyellow);
|
||||
lightColors.push_back(cc::fore::lightwhite);
|
||||
|
||||
int sum=0;
|
||||
|
||||
for (auto ch : hash) sum+=ch;
|
||||
auto color = sum%(lightColors.size()-1);
|
||||
|
||||
return lightColors.at( color );
|
||||
}
|
||||
|
||||
|
||||
// ====================================================================
|
||||
// algorthms
|
||||
|
||||
|
||||
} // namespace nUtil
|
||||
|
||||
|
||||
} // namespace OT
|
||||
|
||||
// global namespace
|
||||
|
||||
const extern int _dbg_ignore = 0; // see description in .hpp
|
||||
|
||||
std::string GetObjectName() {
|
||||
//static std::string * name=nullptr;
|
||||
//if (!name) name = new std::string("(global)");
|
||||
return "";
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
|
||||
nOT::nUtils::cLogger gCurrentLogger;
|
||||
|
@ -1,532 +0,0 @@
|
||||
/// @file
|
||||
/// @author rfree (current maintainer in monero.cc project)
|
||||
/// @brief various general utils taken from (and relate to) otshell project, including loggiang/debug
|
||||
|
||||
/* See other files here for the LICENCE that applies here. */
|
||||
|
||||
#include "ccolor.hpp"
|
||||
#ifndef INCLUDE_OT_NEWCLI_UTILS
|
||||
#define INCLUDE_OT_NEWCLI_UTILS
|
||||
|
||||
#include "lib_common1.hpp"
|
||||
#ifdef __unix
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include"windows_stream.h"
|
||||
#endif
|
||||
|
||||
#ifndef CFG_WITH_TERMCOLORS
|
||||
//#error "You requested to turn off terminal colors (CFG_WITH_TERMCOLORS), however currently they are hardcoded (this option to turn them off is not yet implemented)."
|
||||
#endif
|
||||
|
||||
///Macros related to automatic deduction of class name etc;
|
||||
#define MAKE_CLASS_NAME(NAME) private: static std::string GetObjectName() { return #NAME; }
|
||||
#define MAKE_STRUCT_NAME(NAME) private: static std::string GetObjectName() { return #NAME; } public:
|
||||
|
||||
// define this to debug the debug system itself:
|
||||
// #define opt_debug_debug
|
||||
|
||||
#ifdef opt_debug_debug
|
||||
#define _dbg_dbg(X) do { std::cerr<<"_dbg_dbg: " << OT_CODE_STAMP << " {thread=" << boost::this_thread::get_id()<<"} " \
|
||||
<< " {pid="<<getpid()<<"} " << ": " << X << std::endl; } while(0)
|
||||
#else
|
||||
#define _dbg_dbg(X) do { } while(0)
|
||||
#endif
|
||||
|
||||
namespace nOT {
|
||||
|
||||
namespace nUtils {
|
||||
|
||||
/// @brief general based for my runtime errors
|
||||
class myexception : public std::runtime_error {
|
||||
public:
|
||||
myexception(const char * what);
|
||||
myexception(const std::string &what);
|
||||
//virtual ~myexception();
|
||||
virtual void Report() const;
|
||||
};
|
||||
|
||||
/// @macro Use this macro INJECT_OT_COMMON_USING_NAMESPACE_COMMON_1 as a shortcut for various using std::string etc.
|
||||
INJECT_OT_COMMON_USING_NAMESPACE_COMMON_1 // <=== namespaces
|
||||
|
||||
// ======================================================================================
|
||||
/// text trimming functions (they do mutate the passes string); they trim based on std::isspace. also return it's reference again
|
||||
/// http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
|
||||
std::string & trim(std::string &s); ///< trim text http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
|
||||
std::string & ltrim(std::string &s); ///< left trim
|
||||
std::string & rtrim(std::string &s); ///< right trim
|
||||
|
||||
// ======================================================================================
|
||||
|
||||
std::string get_current_time();
|
||||
|
||||
// string conversions
|
||||
template <class T>
|
||||
std::string ToStr(const T & obj) {
|
||||
std::ostringstream oss;
|
||||
oss << obj;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
struct cNullstream : std::ostream {
|
||||
cNullstream() : std::ios(0), std::ostream(0) {}
|
||||
};
|
||||
extern cNullstream g_nullstream; // a stream that does nothing (eats/discards data)
|
||||
// ========== debug ==========
|
||||
// _dbg_ignore is moved to global namespace (on purpose)
|
||||
|
||||
// TODO make _dbg_ignore thread-safe everywhere
|
||||
|
||||
extern boost::recursive_mutex gLoggerGuard; // the mutex guarding logging/debugging code e.g. protecting streams, files, etc
|
||||
|
||||
std::atomic<int> & gLoggerGuardDepth_Get(); // getter for the global singleton of counter (it guarantees initializing it to 0). This counter shows the current recursion (re-entrant) level of debug macros.
|
||||
|
||||
// TODO more debug of the debug system:
|
||||
// detect lock() error e.g. recursive limit
|
||||
// detect stream e.g. operator<< error
|
||||
|
||||
#define _debug_level(LEVEL,VAR) do { if (_dbg_ignore< LEVEL) { \
|
||||
_dbg_dbg("WRITE DEBUG: LEVEL="<<LEVEL<<" VAR: " << VAR ); \
|
||||
auto level=LEVEL; short int part=0; \
|
||||
try { \
|
||||
boost::lock_guard<boost::recursive_mutex> mutex_guard( nOT::nUtils::gLoggerGuard ); \
|
||||
part=1; \
|
||||
try { \
|
||||
++nOT::nUtils::gLoggerGuardDepth_Get(); \
|
||||
/* int counter = nOT::nUtils::gLoggerGuardDepth_Get(); if (counter!=1) gCurrentLogger.write_stream(100,"")<<"DEBUG-ERROR: recursion, counter="<<counter<<gCurrentLogger.endline(); */ \
|
||||
gCurrentLogger.write_stream(LEVEL,"") << nOT::nUtils::get_current_time() << ' ' << OT_CODE_STAMP << ' ' << VAR << gCurrentLogger.endline() << std::flush; \
|
||||
part=9; \
|
||||
} catch(...) { \
|
||||
gCurrentLogger.write_stream(std::max(level,90),"") << nOT::nUtils::get_current_time() << ' ' << OT_CODE_STAMP << ' ' << "(ERROR IN DEBUG)" << gCurrentLogger.endline(); \
|
||||
--nOT::nUtils::gLoggerGuardDepth_Get(); throw ; \
|
||||
} \
|
||||
--nOT::nUtils::gLoggerGuardDepth_Get(); \
|
||||
} catch(...) { if (part<8) gCurrentLogger.write_stream(100,"")<<"DEBUG-ERROR: problem in debug mechanism e.g. in locking." <<gCurrentLogger.endline(); throw ; } \
|
||||
} } while(0)
|
||||
|
||||
// info for code below: oss object is normal stack variable, using it does not need lock protection
|
||||
#define _debug_level_c(CHANNEL,LEVEL,VAR) do { if (_dbg_ignore< LEVEL) { \
|
||||
_dbg_dbg("WRITE DEBUG: LEVEL="<<LEVEL<<" CHANNEL="<<CHANNEL<<" VAR: " << VAR ); \
|
||||
auto level=LEVEL; short int part=0; \
|
||||
try { \
|
||||
boost::lock_guard<boost::recursive_mutex> mutex_guard( nOT::nUtils::gLoggerGuard ); \
|
||||
part=1; \
|
||||
try { \
|
||||
++nOT::nUtils::gLoggerGuardDepth_Get(); \
|
||||
std::ostringstream oss; \
|
||||
oss << nOT::nUtils::get_current_time() << ' ' << OT_CODE_STAMP << ' ' << VAR << gCurrentLogger.endline() << std::flush; \
|
||||
std::string as_string = oss.str(); \
|
||||
_dbg_dbg("START will write to log LEVEL="<<LEVEL<<" to CHANNEL="<<CHANNEL<<" as_string="<<as_string); \
|
||||
/* int counter = nOT::nUtils::gLoggerGuardDepth_Get(); if (counter!=1) gCurrentLogger.write_stream(100,"")<<"DEBUG-ERROR: recursion, counter="<<counter<<gCurrentLogger.endline(); */ \
|
||||
gCurrentLogger.write_stream(LEVEL,"" ) << as_string << gCurrentLogger.endline() << std::flush; \
|
||||
gCurrentLogger.write_stream(LEVEL,CHANNEL) << as_string << gCurrentLogger.endline() << std::flush; \
|
||||
_dbg_dbg("DONE will write to log LEVEL="<<LEVEL<<" to CHANNEL="<<CHANNEL<<" as_string="<<as_string); \
|
||||
part=9; \
|
||||
} catch(...) { \
|
||||
gCurrentLogger.write_stream(std::max(level,90),CHANNEL) << nOT::nUtils::get_current_time() << ' ' << OT_CODE_STAMP << ' ' << "(ERROR IN DEBUG)" << gCurrentLogger.endline(); \
|
||||
--nOT::nUtils::gLoggerGuardDepth_Get(); throw ; \
|
||||
} \
|
||||
--nOT::nUtils::gLoggerGuardDepth_Get(); \
|
||||
} catch(...) { if (part<8) gCurrentLogger.write_stream(100,CHANNEL)<<"DEBUG-ERROR: problem in debug mechanism e.g. in locking." <<gCurrentLogger.endline(); throw ; } \
|
||||
} } while(0)
|
||||
|
||||
// Numerical values of the debug levels - are defined here as const ints. Full name (with namespace) given for clarity.
|
||||
extern const int _debug_level_nr_dbg3;
|
||||
extern const int _debug_level_nr_dbg2;
|
||||
extern const int _debug_level_nr_dbg1;
|
||||
extern const int _debug_level_nr_info;
|
||||
extern const int _debug_level_nr_note;
|
||||
extern const int _debug_level_nr_fact;
|
||||
extern const int _debug_level_nr_mark;
|
||||
extern const int _debug_level_nr_warn;
|
||||
extern const int _debug_level_nr_erro;
|
||||
|
||||
#define _dbg3(VAR) _debug_level( nOT::nUtils::_debug_level_nr_dbg3,VAR) // details - most detailed
|
||||
#define _dbg2(VAR) _debug_level( nOT::nUtils::_debug_level_nr_dbg2,VAR) // details - a bit more important
|
||||
#define _dbg1(VAR) _debug_level( nOT::nUtils::_debug_level_nr_dbg1,VAR) // details - more important
|
||||
#define _info(VAR) _debug_level( nOT::nUtils::_debug_level_nr_info,VAR) // information
|
||||
#define _note(VAR) _debug_level( nOT::nUtils::_debug_level_nr_note,VAR) // more interesting information
|
||||
#define _fact(VAR) _debug_level( nOT::nUtils::_debug_level_nr_fact,VAR) // interesting events that could be interesting even for user, for logical/business things
|
||||
#define _mark(VAR) _debug_level( nOT::nUtils::_debug_level_nr_mark,VAR) // marked actions
|
||||
#define _warn(VAR) _debug_level( nOT::nUtils::_debug_level_nr_warn,VAR) // some problems
|
||||
#define _erro(VAR) _debug_level( nOT::nUtils::_debug_level_nr_erro,VAR) // errors
|
||||
|
||||
#define _dbg3_c(C,VAR) _debug_level_c(C, nOT::nUtils::_debug_level_nr_dbg3, VAR) // details - most detailed
|
||||
#define _dbg2_c(C,VAR) _debug_level_c(C, nOT::nUtils::_debug_level_nr_dbg2, VAR) // details - a bit more important
|
||||
#define _dbg1_c(C,VAR) _debug_level_c(C, nOT::nUtils::_debug_level_nr_dbg1, VAR) // details - more important
|
||||
#define _info_c(C,VAR) _debug_level_c(C, nOT::nUtils::_debug_level_nr_info, VAR) // information
|
||||
#define _note_c(C,VAR) _debug_level_c(C, nOT::nUtils::_debug_level_nr_note, VAR) // more interesting information
|
||||
#define _fact_c(C,VAR) _debug_level_c(C, nOT::nUtils::_debug_level_nr_fact, VAR) // interesting events that could be interesting even for user, for logical/business things
|
||||
#define _mark_c(C,VAR) _debug_level_c(C, nOT::nUtils::_debug_level_nr_mark, VAR) // marked actions
|
||||
#define _warn_c(C,VAR) _debug_level_c(C, nOT::nUtils::_debug_level_nr_warn, VAR) // some problems
|
||||
#define _erro_c(C,VAR) _debug_level_c(C, nOT::nUtils::_debug_level_nr_erro, VAR) // errors
|
||||
|
||||
// lock // because of VAR
|
||||
#define _scope_debug_level_c(CHANNEL,LEVEL,VAR) \
|
||||
std::ostringstream debug_detail_oss; \
|
||||
nOT::nUtils::gLoggerGuard.lock(); \
|
||||
debug_detail_oss << OT_CODE_STAMP << ' ' << VAR ; \
|
||||
nOT::nUtils::nDetail::cDebugScopeGuard debugScopeGuard; \
|
||||
if (_dbg_ignore<LEVEL) debugScopeGuard.Assign(CHANNEL,LEVEL, debug_detail_oss.str()); \
|
||||
if (_dbg_ignore<LEVEL) _debug_level_c(CHANNEL,LEVEL,debug_detail_oss.str() + " ... begin"); \
|
||||
nOT::nUtils::gLoggerGuard.unlock();
|
||||
#define _scope_debug_level(LEVEL,VAR) _scope_debug_level_c("",LEVEL,VAR)
|
||||
|
||||
#define _scope_dbg1(VAR) _scope_debug_level( _debug_level_nr_dbg3, VAR)
|
||||
#define _scope_dbg2(VAR) _scope_debug_level( _debug_level_nr_dbg2, VAR)
|
||||
#define _scope_dbg3(VAR) _scope_debug_level( _debug_level_nr_dbg1, VAR)
|
||||
#define _scope_info(VAR) _scope_debug_level( _debug_level_nr_info, VAR)
|
||||
#define _scope_note(VAR) _scope_debug_level( _debug_level_nr_note, VAR)
|
||||
#define _scope_fact(VAR) _scope_debug_level( _debug_level_nr_fact, VAR)
|
||||
#define _scope_mark(VAR) _scope_debug_level( _debug_level_nr_mark, VAR)
|
||||
#define _scope_warn(VAR) _scope_debug_level( _debug_level_nr_warn, VAR)
|
||||
#define _scope_erro(VAR) _scope_debug_level( _debug_level_nr_erro, VAR)
|
||||
|
||||
/***
|
||||
@brief do not use this namespace directly, it is implementation detail.
|
||||
*/
|
||||
namespace nDetail {
|
||||
|
||||
/***
|
||||
@brief a Debug scope-guard, to log a debug message when current scope is left. Do NOT use this directly,
|
||||
only use it via the macros like _scope_dbg1 etc.
|
||||
*/
|
||||
class cDebugScopeGuard {
|
||||
protected:
|
||||
string mMsg;
|
||||
int mLevel;
|
||||
string mChan;
|
||||
public:
|
||||
cDebugScopeGuard();
|
||||
~cDebugScopeGuard();
|
||||
void Assign(const string &chan, const int level, const string &msg);
|
||||
};
|
||||
|
||||
const char* DbgShortenCodeFileName(const char *s); ///< Returns a pointer to some part of the string that was given, skipping directory names, for log/debug
|
||||
|
||||
} // namespace nDetail
|
||||
|
||||
// ========== logger ==========
|
||||
|
||||
namespace nDetail {
|
||||
struct channel_use_info;
|
||||
} // namespace nDetail
|
||||
|
||||
/***
|
||||
@brief Class to write debug into. Used it by calling the debug macros _dbg1(...) _info(...) _erro(...) etc, NOT directly!
|
||||
@author rfree (maintainer)
|
||||
@thread this class is NOT thread safe and must used only by one thread at once (use it via ot_debug_macros like _info macro they do proper locking)
|
||||
*/
|
||||
class cLogger {
|
||||
public:
|
||||
cLogger();
|
||||
~cLogger();
|
||||
std::ostream & write_stream(int level); ///< starts a new message on given level (e.g. writes out the icon/tag) and returns stream to output to
|
||||
std::ostream & write_stream(int level, const std::string & channel); ///< the same but with name of the debug channel
|
||||
|
||||
void setOutStreamFromGlobalOptions(); // set debug level, file etc - according to global Options
|
||||
void setOutStreamFile(const std::string &fname); // switch to using this file
|
||||
void setDebugLevel(int level); // change the debug level e.g. to mute debug from now
|
||||
|
||||
std::string icon(int level) const; ///< returns "icon" for given debug level. It is text, might include color controll characters
|
||||
std::string endline() const; ///< returns string to be written at end of message
|
||||
|
||||
protected:
|
||||
typedef long int t_anypid; // a portable representation of PID. long int should cover all platforms
|
||||
|
||||
void SetStreamBroken(); ///< call in case of internal error in logger (e.g. can not open a file)
|
||||
void SetStreamBroken(const std::string &msg); ///< same but with error message
|
||||
|
||||
unique_ptr<std::ofstream> mOutfile;
|
||||
std::ostream * mStream; ///< pointing only! can point to our own mOutfile, or maye to global null stream
|
||||
std::ostream * mStreamBrokenDebug; ///< pointing only! this is a pointer to some stream that should be used when normal debugging is broken eg std::cerr
|
||||
bool mIsBroken; ///< is the debugging system broken (this should be set when internal problems occur and should cause fallback to std::cerr)
|
||||
|
||||
std::map< std::string , std::ofstream * > mChannels; // the ofstream objects are owned by this class
|
||||
|
||||
int mLevel; ///< current debug level
|
||||
|
||||
std::ostream & SelectOutput(int level, const std::string & channel) noexcept; ///< returns a proper stream for this level and channel (always usable string)
|
||||
void OpenNewChannel(const std::string & channel) noexcept; ///< tries to prepare this channel. does NOT guarantee to created mChannels[] entry!
|
||||
void OpenNewChannel_(const std::string & channel); ///< internal function, will throw in case of problems
|
||||
std::string GetLogBaseDir() const;
|
||||
|
||||
std::map< boost::thread::id , int > mThread2Number; ///< change long thread IDs into a short nice number to show
|
||||
int mThread2Number_Biggest; ///< current biggest value held there (biggest key) - works as growing-only counter basically
|
||||
int Thread2Number(const boost::thread::id id); ///< convert the system's thread id into a nice short our id; make one if new thread
|
||||
|
||||
std::map< t_anypid , int > mPid2Number; ///< change long proces PID into a short nice number to show
|
||||
int mPid2Number_Biggest; ///< current biggest value held there (biggest key) - works as growing-only counter basically
|
||||
int Pid2Number(const t_anypid id); ///< convert the system's PID id into a nice short our id; make one if new thread
|
||||
};
|
||||
|
||||
|
||||
|
||||
// ====================================================================
|
||||
// vector debug
|
||||
|
||||
template <class T>
|
||||
std::string vectorToStr(const T & v) {
|
||||
std::ostringstream oss;
|
||||
for(auto rec: v) {
|
||||
oss << rec <<",";
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DisplayVector(std::ostream & out, const std::vector<T> &v, const std::string &delim=" ") {
|
||||
std::copy( v.begin(), v.end(), std::ostream_iterator<T>(out, delim.c_str()) );
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void EndlDisplayVector(std::ostream & out, const std::vector<T> &v, const std::string &delim=" ") {
|
||||
out << std::endl;
|
||||
DisplayVector(out,v,delim);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DisplayVectorEndl(std::ostream & out, const std::vector<T> &v, const std::string &delim=" ") {
|
||||
DisplayVector(out,v,delim);
|
||||
out << std::endl;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DbgDisplayVector(const std::vector<T> &v, const std::string &delim=" ") {
|
||||
std::cerr << "[";
|
||||
std::copy( v.begin(), v.end(), std::ostream_iterator<T>(std::cerr, delim.c_str()) );
|
||||
std::cerr << "]";
|
||||
}
|
||||
|
||||
string stringToColor(const string &hash);
|
||||
template <class T, class T2>
|
||||
void DisplayMap(std::ostream & out, const std::map<T, T2> &m, const std::string &delim=" ") {
|
||||
auto *no_color = zkr::cc::fore::console;
|
||||
for(auto var : m) {
|
||||
out << stringToColor(var.first) << var.first << delim << var.second << no_color << endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <class T, class T2>
|
||||
void EndlDisplayMap(std::ostream & out, const std::map<T, T2> &m, const std::string &delim=" ") {
|
||||
out << endl;
|
||||
for(auto var : m) {
|
||||
out << var.first << delim << var.second << endl;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, class T2>
|
||||
void DbgDisplayMap(const std::map<T, T2> &m, const std::string &delim=" ") {
|
||||
for(auto var : m) {
|
||||
std::cerr << var.first << delim << var.second << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
void DbgDisplayVectorEndl(const std::vector<T> &v, const std::string &delim=" ") {
|
||||
DbgDisplayVector(v,delim);
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
|
||||
void DisplayStringEndl(std::ostream & out, const std::string text);
|
||||
|
||||
bool CheckIfBegins(const std::string & beggining, const std::string & all);
|
||||
bool CheckIfEnds (std::string const & ending, std::string const & all);
|
||||
std::string SpaceFromEscape(const std::string &s);
|
||||
std::string EscapeFromSpace(const std::string &s);
|
||||
vector<string> WordsThatMatch(const std::string & sofar, const vector<string> & possib);
|
||||
char GetLastChar(const std::string & str);
|
||||
std::string GetLastCharIf(const std::string & str); // TODO unicode?
|
||||
std::string EscapeString(const std::string &s);
|
||||
|
||||
|
||||
template <class T>
|
||||
std::string DbgVector(const std::vector<T> &v, const std::string &delim="|") {
|
||||
std::ostringstream oss;
|
||||
oss << "[";
|
||||
bool first=true;
|
||||
for(auto vElement : v) { if (!first) oss<<delim; first=false; oss <<vElement ; }
|
||||
oss << "]";
|
||||
//std::copy( v.begin(), v.end(), std::ostream_iterator<T>(oss, delim.c_str()) );
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::ostream & operator<<(std::ostream & os, const map< T, vector<T> > & obj){
|
||||
os << "[";
|
||||
for(auto const & elem : obj) {
|
||||
os << " [" << elem.first << "=" << DbgVector(elem.second) << "] ";
|
||||
}
|
||||
os << "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
template <class T, class T2>
|
||||
std::string DbgMap(const map<T, T2> & map) {
|
||||
std::ostringstream oss;
|
||||
oss << map;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// assert
|
||||
|
||||
// ASRT - assert. Name like ASSERT() was too long, and ASS() was just... no.
|
||||
// Use it like this: ASRT( x>y ); with the semicolon at end, a clever trick forces this syntax :)
|
||||
#define ASRT(x) do { if (!(x)) nOT::nUtils::Assert(false, OT_CODE_STAMP, #x); } while(0)
|
||||
|
||||
void Assert(bool result, const std::string &stamp, const std::string &condition);
|
||||
|
||||
// ====================================================================
|
||||
// advanced string
|
||||
|
||||
const std::string GetMultiline(string endLine = "~");
|
||||
vector<string> SplitString(const string & str);
|
||||
|
||||
bool checkPrefix(const string & str, char prefix = '^');
|
||||
|
||||
// ====================================================================
|
||||
// nUse utils
|
||||
|
||||
enum class eSubjectType {Account, Asset, User, Server, Unknown};
|
||||
|
||||
string SubjectType2String(const eSubjectType & type);
|
||||
eSubjectType String2SubjectType(const string & type);
|
||||
|
||||
// ====================================================================
|
||||
// operation on files
|
||||
|
||||
/// @brief tools related to filesystem
|
||||
/// @author rfree (maintainer)
|
||||
class cFilesystemUtils { // if we do not want to use boost in given project (or we could optionally write boost here later)
|
||||
public:
|
||||
static bool CreateDirTree(const std::string & dir, bool only_below=false);
|
||||
static char GetDirSeparatorSys(); /// < eg '/' or '\'
|
||||
static char GetDirSeparatorInter(); /// < internal is '/'
|
||||
static string FileInternalToSystem(const std::string &name); ///< converts from internal file name string to system file name string
|
||||
static string FileSystemToInternal(const std::string &name); ///< converts from system file name string to internal file name string
|
||||
};
|
||||
|
||||
|
||||
/// @brief utils to e.g. edit a file from console
|
||||
/// @author rfree (maintainer)
|
||||
class cEnvUtils {
|
||||
int fd;
|
||||
string mFilename;
|
||||
|
||||
void GetTmpTextFile();
|
||||
void CloseFile();
|
||||
void OpenEditor();
|
||||
const string ReadFromTmpFile();
|
||||
public:
|
||||
const string Compose();
|
||||
const string ReadFromFile(const string path);
|
||||
};
|
||||
void hintingToTxt(std::fstream & file, string command, vector<string> &commands);
|
||||
void generateQuestions (std::fstream & file, string command);
|
||||
void generateAnswers (std::fstream & file, string command, vector<string> &completions);
|
||||
|
||||
// ====================================================================
|
||||
|
||||
namespace nOper { // nOT::nUtils::nOper
|
||||
// cool shortcut operators, like vector + vecotr operator working same as string (appending)
|
||||
// isolated to namespace because it's unorthodox ide to implement this
|
||||
|
||||
using namespace std;
|
||||
|
||||
// TODO use && and move?
|
||||
template <class T>
|
||||
vector<T> operator+(const vector<T> &a, const vector<T> &b) {
|
||||
vector<T> ret = a;
|
||||
ret.insert( ret.end() , b.begin(), b.end() );
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
vector<T> operator+(const T &a, const vector<T> &b) {
|
||||
vector<T> ret(1,a);
|
||||
ret.insert( ret.end() , b.begin(), b.end() );
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
vector<T> operator+(const vector<T> &a, const T &b) {
|
||||
vector<T> b_vector(1,a);
|
||||
return a + b_vector;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
vector<T>& operator+=(vector<T> &a, const vector<T> &b) {
|
||||
a.insert( a.end() , b.begin(), b.end() );
|
||||
return a;
|
||||
}
|
||||
|
||||
// map
|
||||
template <class TK,class TV>
|
||||
map<TK,TV> operator+(const map<TK,TV> &a, const map<TK,TV> &b) {
|
||||
map<TK,TV> ret = a;
|
||||
for (const auto & elem : b) {
|
||||
ret.insert(elem);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
} // nOT::nUtils::nOper
|
||||
|
||||
// ====================================================================
|
||||
|
||||
// ====================================================================
|
||||
|
||||
// Algorithms
|
||||
|
||||
// ====================================================================
|
||||
// ====================================================================
|
||||
|
||||
|
||||
/**
|
||||
@brief Special type that on creation will be initialized to have value INIT given as template argument.
|
||||
Might be usefull e.g. to express in the declaration of class what will be the default value of member variable
|
||||
See also http://www.boost.org/doc/libs/1_56_0/libs/utility/value_init.htm
|
||||
Probably not needed when using boost in your project.
|
||||
*/
|
||||
template <class T, T INIT>
|
||||
class value_init {
|
||||
private:
|
||||
T data;
|
||||
public:
|
||||
value_init();
|
||||
|
||||
T& operator=(const T& v) { data=v; return *this; }
|
||||
operator T const &() const { return data; }
|
||||
operator T&() { return data; }
|
||||
};
|
||||
|
||||
template <class T, T INIT>
|
||||
value_init<T, INIT>::value_init() : data(INIT) { }
|
||||
|
||||
} // namespace nUtils
|
||||
|
||||
} // namespace nOT
|
||||
|
||||
|
||||
// global namespace
|
||||
extern nOT::nUtils::cLogger gCurrentLogger; ///< The current main logger. Usually do not use it directly, instead use macros like _dbg1 etc
|
||||
|
||||
std::string GetObjectName(); ///< Method to return name of current object; To use in debug; Can be shadowed in your classes. (Might be not used currently)
|
||||
|
||||
const extern int _dbg_ignore; ///< the global _dbg_ignore, but local code (blocks, classes etc) you could shadow it in your code blocks,
|
||||
// to override debug compile-time setting for given block/class, e.g. to disable debug in one of your methods or increase it there.
|
||||
// Or to make it runtime by providing a class normal member and editing it in runtime
|
||||
|
||||
#define OT_CODE_STAMP ( nOT::nUtils::ToStr("[") + nOT::nUtils::nDetail::DbgShortenCodeFileName(__FILE__) + nOT::nUtils::ToStr("+") + nOT::nUtils::ToStr(__LINE__) + nOT::nUtils::ToStr(" ") + (GetObjectName()) + nOT::nUtils::ToStr("::") + nOT::nUtils::ToStr(__FUNCTION__) + nOT::nUtils::ToStr("]"))
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -1,64 +0,0 @@
|
||||
#if defined(_WIN32)
|
||||
#include "windows_stream.h"
|
||||
#include <windows.h>
|
||||
|
||||
windows_stream::windows_stream(unsigned int pLevel)
|
||||
:
|
||||
mLevel(pLevel)
|
||||
{
|
||||
}
|
||||
|
||||
std::ostream& operator << (std::ostream &stream, windows_stream const& object)
|
||||
{
|
||||
HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
if (object.mLevel >= 100)
|
||||
{
|
||||
SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_INTENSITY);
|
||||
return stream;
|
||||
}
|
||||
if (object.mLevel >= 90)
|
||||
{
|
||||
SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_INTENSITY | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY);
|
||||
return stream;
|
||||
}
|
||||
if (object.mLevel >= 80)
|
||||
{
|
||||
SetConsoleTextAttribute(h_stdout, BACKGROUND_BLUE | BACKGROUND_RED | BACKGROUND_INTENSITY);
|
||||
return stream;
|
||||
}
|
||||
if (object.mLevel >= 75)
|
||||
{
|
||||
SetConsoleTextAttribute(h_stdout, BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_INTENSITY);
|
||||
return stream;
|
||||
}
|
||||
if (object.mLevel >= 70)
|
||||
{
|
||||
SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
|
||||
return stream;
|
||||
}
|
||||
if (object.mLevel >= 50)
|
||||
{
|
||||
SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||
return stream;
|
||||
}
|
||||
if (object.mLevel >= 40)
|
||||
{
|
||||
SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY);
|
||||
return stream;
|
||||
}
|
||||
if (object.mLevel >= 30)
|
||||
{
|
||||
SetConsoleTextAttribute(h_stdout, FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||
return stream;
|
||||
}
|
||||
if (object.mLevel >= 20)
|
||||
{
|
||||
SetConsoleTextAttribute(h_stdout, FOREGROUND_BLUE);
|
||||
return stream;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
#endif
|
@ -1,20 +0,0 @@
|
||||
#ifndef WINDOWS_STREAM_H
|
||||
#define WINDOWS_STREAM_H
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
class windows_stream
|
||||
{
|
||||
public:
|
||||
windows_stream(unsigned int pLevel);
|
||||
friend std::ostream& operator<<(std::ostream &stream, windows_stream const& object);
|
||||
private:
|
||||
unsigned int mLevel = 0;
|
||||
};
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
#endif // WINDOWS_STREAM_H
|
File diff suppressed because it is too large
Load Diff
@ -1,202 +0,0 @@
|
||||
// Copyright (c) 2014-2016, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "data_logger.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/chrono.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
#include <chrono>
|
||||
#include "../../contrib/otshell_utils/utils.hpp"
|
||||
|
||||
namespace epee
|
||||
{
|
||||
namespace net_utils
|
||||
{
|
||||
data_logger &data_logger::get_instance() {
|
||||
boost::call_once(m_singleton,
|
||||
[] {
|
||||
_info_c("dbg/data","Creating singleton of data_logger");
|
||||
if (m_state != data_logger_state::state_before_init) { _erro_c("dbg/data","Internal error in singleton"); throw std::runtime_error("data_logger singleton"); }
|
||||
m_state = data_logger_state::state_during_init;
|
||||
m_obj.reset(new data_logger());
|
||||
m_state = data_logger_state::state_ready_to_use;
|
||||
}
|
||||
);
|
||||
|
||||
if (m_state != data_logger_state::state_ready_to_use) {
|
||||
_erro ("trying to use not working data_logger");
|
||||
throw std::runtime_error("data_logger ctor state");
|
||||
}
|
||||
|
||||
return * m_obj;
|
||||
}
|
||||
|
||||
data_logger::data_logger() {
|
||||
_note_c("dbg/data","Starting data logger (for graphs data)");
|
||||
if (m_state != data_logger_state::state_during_init) { _erro_c("dbg/data","Singleton ctor state"); throw std::runtime_error("data_logger ctor state"); }
|
||||
boost::lock_guard<boost::mutex> lock(mMutex); // lock
|
||||
|
||||
// prepare all the files for given data channels:
|
||||
mFilesMap["peers"] = data_logger::fileData("log/dr-monero/peers.data");
|
||||
mFilesMap["download"] = data_logger::fileData("log/dr-monero/net/in-all.data");
|
||||
mFilesMap["upload"] = data_logger::fileData("log/dr-monero/net/out-all.data");
|
||||
mFilesMap["request"] = data_logger::fileData("log/dr-monero/net/req-all.data");
|
||||
mFilesMap["sleep_down"] = data_logger::fileData("log/dr-monero/down_sleep_log.data");
|
||||
mFilesMap["sleep_up"] = data_logger::fileData("log/dr-monero/up_sleep_log.data");
|
||||
mFilesMap["calc_time"] = data_logger::fileData("log/dr-monero/get_objects_calc_time.data");
|
||||
mFilesMap["blockchain_processing_time"] = data_logger::fileData("log/dr-monero/blockchain_log.data");
|
||||
mFilesMap["block_processing"] = data_logger::fileData("log/dr-monero/block_proc.data");
|
||||
|
||||
mFilesMap["peers_limit"] = data_logger::fileData("log/dr-monero/peers_limit.info");
|
||||
mFilesMap["download_limit"] = data_logger::fileData("log/dr-monero/limit_down.info");
|
||||
mFilesMap["upload_limit"] = data_logger::fileData("log/dr-monero/limit_up.info");
|
||||
|
||||
mFilesMap["peers_limit"].mLimitFile = true;
|
||||
mFilesMap["download_limit"].mLimitFile = true;
|
||||
mFilesMap["upload_limit"].mLimitFile = true;
|
||||
|
||||
// do NOT modify mFilesMap below this point, since there is no locking for this used (yet)
|
||||
|
||||
_info_c("dbg/data","Creating thread for data logger"); // create timer thread
|
||||
m_thread_maybe_running=true;
|
||||
std::shared_ptr<boost::thread> logger_thread(new boost::thread([&]() {
|
||||
_info_c("dbg/data","Inside thread for data logger");
|
||||
while (m_state == data_logger_state::state_during_init) { // wait for creation to be done (in other thread, in singleton) before actually running
|
||||
boost::this_thread::sleep_for(boost::chrono::seconds(1));
|
||||
}
|
||||
_info_c("dbg/data","Inside thread for data logger - going into main loop");
|
||||
while (m_state == data_logger_state::state_ready_to_use) { // run as long as we are not closing the single object
|
||||
boost::this_thread::sleep_for(boost::chrono::seconds(1));
|
||||
saveToFile(); // save all the pending data
|
||||
}
|
||||
_info_c("dbg/data","Inside thread for data logger - done the main loop");
|
||||
m_thread_maybe_running=false;
|
||||
}));
|
||||
logger_thread->detach();
|
||||
_info_c("dbg/data","Data logger constructed");
|
||||
}
|
||||
|
||||
data_logger::~data_logger() noexcept(false) {
|
||||
_note_c("dbg/data","Destructor of the data logger");
|
||||
{
|
||||
boost::lock_guard<boost::mutex> lock(mMutex);
|
||||
m_state = data_logger_state::state_dying;
|
||||
}
|
||||
_info_c("dbg/data","State was set to dying");
|
||||
while(m_thread_maybe_running) { // wait for the thread to exit
|
||||
boost::this_thread::sleep_for(boost::chrono::seconds(1));
|
||||
_info_c("dbg/data","Waiting for background thread to exit");
|
||||
}
|
||||
_info_c("dbg/data","Thread exited");
|
||||
}
|
||||
|
||||
void data_logger::kill_instance() {
|
||||
m_state = data_logger_state::state_dying;
|
||||
m_obj.reset();
|
||||
}
|
||||
|
||||
void data_logger::add_data(std::string filename, unsigned int data) {
|
||||
boost::lock_guard<boost::mutex> lock(mMutex);
|
||||
if (m_state != data_logger_state::state_ready_to_use) { _info_c("dbg/data","Data logger is not ready, returning."); return; }
|
||||
|
||||
if (mFilesMap.find(filename) == mFilesMap.end()) { // no such file/counter
|
||||
_erro_c("dbg/data","Trying to use not opened data file filename="<<filename);
|
||||
_erro_c("dbg/data","Disabling saving of graphs due to error");
|
||||
m_save_graph=false; // <--- disabling saving graphs
|
||||
return;
|
||||
}
|
||||
|
||||
if (mFilesMap[filename].mLimitFile) { // this holds a number (that is not additive) - e.g. the limit setting
|
||||
mFilesMap[filename].mDataToSave = data;
|
||||
} else {
|
||||
mFilesMap[filename].mDataToSave += data; // this holds a number that should be sum of all accumulated samples
|
||||
}
|
||||
}
|
||||
|
||||
bool data_logger::is_dying() {
|
||||
if (m_state == data_logger_state::state_dying) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void data_logger::saveToFile() {
|
||||
_dbg2_c("dbg/data","saving to files");
|
||||
boost::lock_guard<boost::mutex> lock(mMutex);
|
||||
if (m_state != data_logger_state::state_ready_to_use) { _info_c("dbg/data","Data logger is not ready, returning."); return; }
|
||||
nOT::nUtils::cFilesystemUtils::CreateDirTree("log/dr-monero/net/");
|
||||
for (auto &element : mFilesMap)
|
||||
{
|
||||
element.second.save();
|
||||
if (!element.second.mLimitFile) element.second.mDataToSave = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// the inner class:
|
||||
|
||||
double data_logger::fileData::get_current_time() {
|
||||
#if defined(__APPLE__)
|
||||
auto point = std::chrono::system_clock::now();
|
||||
#else
|
||||
auto point = std::chrono::steady_clock::now();
|
||||
#endif
|
||||
auto time_from_epoh = point.time_since_epoch();
|
||||
auto ms = std::chrono::duration_cast< std::chrono::milliseconds >( time_from_epoh ).count();
|
||||
double ms_f = ms;
|
||||
return ms_f / 1000.;
|
||||
}
|
||||
|
||||
data_logger::fileData::fileData(std::string pFile) {
|
||||
_dbg3_c("dbg/data","opening data file named pFile="<<pFile<<" for this="<<this);
|
||||
mFile = std::make_shared<std::ofstream> (pFile);
|
||||
_dbg1_c("dbg/data","opened data file named pFile="<<pFile<<" in mFile="<<mFile<<" for this="<<this);
|
||||
mPath = pFile;
|
||||
}
|
||||
|
||||
void data_logger::fileData::save() {
|
||||
if (!data_logger::m_save_graph) return; // <--- disabled
|
||||
_dbg2_c("dbg/data","saving to the file now, mFile="<<mFile);
|
||||
mFile->open(mPath, std::ios::app);
|
||||
*mFile << static_cast<int>(get_current_time()) << " " << mDataToSave << std::endl;
|
||||
mFile->close();
|
||||
}
|
||||
|
||||
|
||||
data_logger_state data_logger::m_state(data_logger_state::state_before_init); ///< (static) state of the singleton object
|
||||
std::atomic<bool> data_logger::m_save_graph(false); // (static)
|
||||
std::atomic<bool> data_logger::m_thread_maybe_running(false); // (static)
|
||||
boost::once_flag data_logger::m_singleton; // (static)
|
||||
std::unique_ptr<data_logger> data_logger::m_obj; // (static)
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
@ -1,105 +0,0 @@
|
||||
// Copyright (c) 2014-2016, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef INCLUDED_p2p_data_logger_hpp
|
||||
#define INCLUDED_p2p_data_logger_hpp
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/thread/once.hpp>
|
||||
#include <atomic>
|
||||
|
||||
namespace epee
|
||||
{
|
||||
namespace net_utils
|
||||
{
|
||||
|
||||
enum class data_logger_state { state_before_init, state_during_init, state_ready_to_use, state_dying };
|
||||
|
||||
/***
|
||||
@note: use it ONLY via singleton! It will be spawned then, and will auto destruct on program exit.
|
||||
@note: do call ::kill_instance() before exiting main, at end of main. But before make sure no one else (e.g. no other threads) will try to use this/singleton
|
||||
@note: it is not allowed to use this class from code "runnig before or after main", e.g. from ctors of static objects, because of static-creation-order races
|
||||
@note: on creation (e.g. from singleton), it spawns a thread that saves all data in background
|
||||
*/
|
||||
class data_logger {
|
||||
public:
|
||||
static data_logger &get_instance(); ///< singleton
|
||||
static void kill_instance(); ///< call this before ending main to allow more gracefull shutdown of the main singleton and it's background thread
|
||||
~data_logger() noexcept(false); ///< destr, will be called when singleton is killed when global m_obj dies. will kill theads etc
|
||||
|
||||
private:
|
||||
data_logger(); ///< constructor is private, use only via singleton get_instance
|
||||
|
||||
public:
|
||||
data_logger(const data_logger &ob) = delete; // use only one per program
|
||||
data_logger(data_logger &&ob) = delete;
|
||||
data_logger & operator=(const data_logger&) = delete;
|
||||
data_logger & operator=(data_logger&&) = delete;
|
||||
|
||||
void add_data(std::string filename, unsigned int data); ///< use this to append data here. Use it only the singleton. It locks itself.
|
||||
|
||||
static std::atomic<bool> m_save_graph; ///< global setting flag, should we save all the data or not (can disable logging graphs data)
|
||||
static bool is_dying();
|
||||
|
||||
private:
|
||||
static boost::once_flag m_singleton; ///< to guarantee singleton creates the object exactly once
|
||||
static data_logger_state m_state; ///< state of the singleton object
|
||||
static std::atomic<bool> m_thread_maybe_running; ///< is the background thread (more or less) running, or is it fully finished
|
||||
static std::unique_ptr<data_logger> m_obj; ///< the singleton object. Only use it via get_instance(). Can be killed by kill_instance()
|
||||
|
||||
/***
|
||||
* one graph/file with data
|
||||
*/
|
||||
class fileData {
|
||||
public:
|
||||
fileData() = default;
|
||||
fileData(const fileData &ob) = delete;
|
||||
fileData(std::string pFile);
|
||||
|
||||
std::shared_ptr<std::ofstream> mFile;
|
||||
long int mDataToSave = 0; ///< sum of the data (in current interval, will be counted from 0 on next interval)
|
||||
static double get_current_time();
|
||||
void save();
|
||||
std::string mPath;
|
||||
bool mLimitFile = false; ///< this holds a number (that is not additive) - e.g. the limit setting
|
||||
};
|
||||
|
||||
std::map<std::string, fileData> mFilesMap;
|
||||
boost::mutex mMutex;
|
||||
void saveToFile(); ///< write data to the target files. do not use this directly
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue