[DEV] add a callback to get log in an other logger, et better log in file

This commit is contained in:
Edouard DUPIN 2016-09-27 21:55:42 +02:00
parent 2f469afbce
commit f6ed391fe6
11 changed files with 388 additions and 108 deletions

View File

@ -16,76 +16,55 @@ Declaring the list of macro {#elog_tutor
**debug.h**
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
#pragma once
#include <elog/log.h>
namespace appl {
int32_t getLogId();
};
#define APPL_BASE(info,data) ELOG_BASE(appl::getLogId(),info,data)
#define APPL_PRINT(data) APPL_BASE(-1, data)
#define APPL_CRITICAL(data) APPL_BASE(1, data)
#define APPL_ERROR(data) APPL_BASE(2, data)
#define APPL_WARNING(data) APPL_BASE(3, data)
#ifdef DEBUG
#define APPL_INFO(data) APPL_BASE(4, data)
#define APPL_DEBUG(data) APPL_BASE(5, data)
#define APPL_VERBOSE(data) APPL_BASE(6, data)
#define APPL_TODO(data) APPL_BASE(4, "TODO : " << data)
#else
#define APPL_INFO(data) do { } while(false)
#define APPL_DEBUG(data) do { } while(false)
#define APPL_VERBOSE(data) do { } while(false)
#define APPL_TODO(data) do { } while(false)
#endif
#define APPL_ASSERT(cond,data) \
do { \
if (!(cond)) { \
APPL_CRITICAL(data); \
assert(!#cond); \
} \
} while (0)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@include sample/debug.h
**debug.cpp**
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
#include "debug.h"
@include sample/debug.cpp
int32_t appl::getLogId() {
static int32_t g_val = elog::registerInstance("your application name");
return g_val;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- on your main application:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
#include <elog/elog.h>
int main(int _argc, const char *_argv[]) {
elog::init(_argc, _argv);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@snippet sample/main.cpp elog_sample_main_include
@snippet sample/main.cpp elog_sample_main_base
Using it {#elog_tutorial_01_using_it}
========
You just need to call the macro whe you want to add debug log:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
APPL_INFO("Hello, how are you?");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@snippet sample/main.cpp elog_sample_main_log
Specification of logs
=====================
---------------------
- *_CRITICAL(***); This will log the data and asert just after (display backtrace if possible)
- *_PRINT(***); display on console (can not be removed with the log-level)
Log in an external logger {#elog_tutorial_01_external}
=========================
You must specify an external function that is receiving the logs:
@snippet sample/main.cpp elog_sample_main_callback_DECLARATION
Now you must connect it on the elog backend:
@snippet sample/main.cpp elog_sample_main_callback_link
```{.cpp}
elog::setCallbackLog(&myExternalLogCallback);
```
The full code of the callback:
@snippet sample/main.cpp elog_sample_main_callback
you can test the program:
```{.sh}
lutin -C -P -mdebug elog-sample?build?run:--elog-level=2
```

View File

@ -3,15 +3,15 @@ Elog Tutorial: Runtime use {#elog_tutorial_02}
When you build your application you can access some log configuration:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sh}
```{.sh}
./yourApplication --help
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
you might have an output for elog:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sh}
```{.sh}
[P] elog - help :
[P] yourApplication [options]
[P] --elog-level= Change the default log level (set all Log level):
[P] --elog-level= Change the default log level (set all Log level):
[P] 0: debug None (default in release)
[P] 1: debug Critical
[P] 2: debug Error
@ -19,34 +19,43 @@ you might have an output for elog:
[P] 4: debug Info (default in debug)
[P] 5: debug Debug
[P] 6: debug Verbose
[P] --elog-lib=name:X Set a library specific level:
[P] --elog-lib=name:X Set a library specific level:
[P] name Name of the library
[P] X Log level to set [0..6]
[P] --elog-color Enable color in log (default in Linux/debug)
[P] --elog-no-color Disable color in log (default in Linux/release and Other)
[P] --elog-config= Configure the Log interface
[P] note: ':' can be replace with '/' or '+'
[P] --elog-file=pathToFile File to store the logs: (disable console logs)
[P] --elog-color Enable color in log (default in Linux/debug)
[P] --elog-no-color Disable color in log (default in Linux/release and Other)
[P] --elog-config= Configure the Log interface
[P] t: diplay time
[P] T: diplay thread id
[P] N: diplay thread name
[P] L: diplay line number
[P] l: diplay lib name
[P] f: diplay function name
[P] -h/--help: this help
[P] -h/--help: Display this help
[P] example:
[P] yourApplication --elog-color --elog-level=2 --elog-lib=etk:5 --elog-lib=appl:6 --elog-config=NLlf
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
Then you can simply select the log level with:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sh}
```{.sh}
./yourApplication --elog-level=5
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
Or select a sub-element log level:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sh}
```{.sh}
./yourApplication --elog-lib=elog:1 --elog-lib=appl:6
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
Store in a file:
----------------
```{.sh}
./yourApplication --elog-file=log.txt
```

View File

@ -14,6 +14,9 @@ def create(target, module_name):
module_name,
"doc",
])
my_module.add_sample_path([
"sample",
])
my_module.add_exclude_symbols([
'*operator<<*',
])

View File

@ -64,6 +64,12 @@ static std::vector<std::string> split(const std::string& _input, char _val) {
void elog::init(int _argc, const char** _argv) {
ELOG_INFO("E-log system init (BEGIN)");
// retrive application Name:
std::string applName = _argv[0];
int lastSlash = applName.rfind('/');
applName = &applName[lastSlash+1];
// get name: applName
bool userSpecifyLogFile = false;
for (int32_t iii=0; iii<_argc ; ++iii) {
std::string data = _argv[iii];
if (startWith(data, "--elog-level=")) {
@ -73,6 +79,14 @@ void elog::init(int _argc, const char** _argv) {
elog::setColor(true);
} else if (startWith(data, "--elog-no-color")) {
elog::setColor(false);
} else if (startWith(data, "--elog-file=")) {
std::string value(data.begin()+12, data.end());
if (value.size() == 0) {
elog::unsetLogInFile();
} else {
elog::setLogInFile(value);
}
userSpecifyLogFile = true;
} else if (startWith(data, "--elog-config=")) {
std::string value(data.begin()+14, data.end());
elog::setTime(false);
@ -117,7 +131,7 @@ void elog::init(int _argc, const char** _argv) {
|| data == "--help") {
ELOG_PRINT("elog - help : ");
ELOG_PRINT(" " << _argv[0] << " [options]");
ELOG_PRINT(" --elog-level= Change the default log level (set all Log level):");
ELOG_PRINT(" --elog-level= Change the default log level (set all Log level):");
ELOG_PRINT(" 0: debug None (default in release)");
ELOG_PRINT(" 1: debug Critical");
ELOG_PRINT(" 2: debug Error");
@ -129,9 +143,10 @@ void elog::init(int _argc, const char** _argv) {
ELOG_PRINT(" name Name of the library");
ELOG_PRINT(" X Log level to set [0..6]");
ELOG_PRINT(" note: ':' can be replace with '/' or '+'");
ELOG_PRINT(" --elog-color Enable color in log (default in Linux/debug)");
ELOG_PRINT(" --elog-no-color Disable color in log (default in Linux/release and Other)");
ELOG_PRINT(" --elog-config= Configure the Log interface");
ELOG_PRINT(" --elog-file=pathToFile File to store the logs: (disable console logs)");
ELOG_PRINT(" --elog-color Enable color in log (default in Linux/debug)");
ELOG_PRINT(" --elog-no-color Disable color in log (default in Linux/release and Other)");
ELOG_PRINT(" --elog-config= Configure the Log interface");
ELOG_PRINT(" t: diplay time");
#ifdef ELOG_BUILD_ETHREAD
ELOG_PRINT(" T: diplay thread id");
@ -140,13 +155,30 @@ void elog::init(int _argc, const char** _argv) {
ELOG_PRINT(" L: diplay line number");
ELOG_PRINT(" l: diplay lib name");
ELOG_PRINT(" f: diplay function name");
ELOG_PRINT(" -h/--help: this help");
ELOG_PRINT(" -h/--help: Dispplay this help");
ELOG_PRINT(" example:");
ELOG_PRINT(" " << _argv[0] << " --elog-color --elog-level=2 --elog-lib=etk:5 --elog-lib=appl:6 --elog-config=NLlf");
} else if (startWith(data, "--elog")) {
} else if (startWith(data, "--elog") == true) {
ELOG_ERROR("Can not parse the argument : '" << data << "'");
}
}
if (userSpecifyLogFile == false) {
#ifdef DEBUG
#if defined(__TARGET_OS__Windows)
elog::setLogInFile("log.txt");
#endif
#else
#if defined(__TARGET_OS__Linux)
//elog::setLogInFile("/var/log/elog_" +applName + ".log");
elog::setLogInFile("/tmp/elog_" +applName + ".log");
#elif defined(__TARGET_OS__MacOs)
elog::setLogInFile(applName + ".log");
#elif defined(__TARGET_OS__Windows)
elog::setLogInFile(applName + ".log");
#endif
#endif
}
ELOG_INFO("E-LOG system init (END)");
}

View File

@ -83,7 +83,7 @@
#define DEFAULT_LOG_TIME true
#define DEFAULT_LOG_LIB_NAME true
#else
#define DEFAULT_LOG_LEVEL elog::level_none
#define DEFAULT_LOG_LEVEL elog::level_error
#define DEFAULT_LOG_COLOR false
#define DEFAULT_LOG_LINE false
#define DEFAULT_LOG_THREAD_ID false
@ -262,6 +262,45 @@ static void getDisplayTime(char* data) {
#endif
}
static std::mutex g_lock;
static elog::callbackLog callbackUserLog(nullptr);
static FILE*& getLogFile() {
static FILE* file=nullptr;
return file;
}
void elog::setLogInFile(const std::string& _filename) {
elog::unsetLogInFile();
ELOG_PRINT("Log in file: '" << _filename << "'");
g_lock.lock();
FILE*& file = getLogFile();
file = fopen(_filename.c_str(), "w");
g_lock.unlock();
if (file == nullptr) {
ELOG_ERROR("Can not open file: '" << _filename << "'");
}
}
void elog::unsetLogInFile() {
g_lock.lock();
FILE*& file = getLogFile();
// close file only if needed ...
if (file != nullptr) {
fflush(file);
fclose(file);
file = nullptr;
}
g_lock.unlock();
}
void elog::setCallbackLog(const elog::callbackLog& _callback) {
// TODO : Check atomicity ...
g_lock.lock();
callbackUserLog = _callback;
g_lock.unlock();
}
//regular colors
#define ETK_BASH_COLOR_BLACK "\e[0;30m"
#define ETK_BASH_COLOR_RED "\e[0;31m"
@ -297,7 +336,19 @@ static void getDisplayTime(char* data) {
#define LENGHT_MAX_LOG (2048)
void elog::logChar(int32_t _id, int32_t _level, int32_t _ligne, const char* _funcName, const char* _log) {
static std::mutex g_lock;
// special callback mode:
if (callbackUserLog != nullptr) {
const char* libName = "";
if (_id >= 0) {
libName = getList()[_id].first.c_str();
}
g_lock.lock();
if (callbackUserLog != nullptr) {
callbackUserLog(libName, elog::level(_level), _ligne, _funcName, _log);
}
g_lock.unlock();
return;
}
char handle[LENGHT_MAX_LOG] = "";
memset(handle, ' ', LENGHT_MAX_LOG);
handle[0] = '\0';
@ -480,6 +531,26 @@ void elog::logChar(int32_t _id, int32_t _level, int32_t _ligne, const char* _fun
}
g_lock.lock();
{
FILE*& file = getLogFile();
// close file only if needed ...
if (file != nullptr) {
*pointer++ = '\n';
*pointer = '\0';
fprintf(file, handle);
switch(_level) {
default:
break;
case elog::level_critical:
case elog::level_error:
fflush(file);
break;
}
// if we log in file, we have no need to log otherwise ... just "tail -f log.txt"
g_lock.unlock();
return;
}
}
#if defined(__TARGET_OS__Android)
switch(_level) {
default:
@ -509,22 +580,6 @@ void elog::logChar(int32_t _id, int32_t _level, int32_t _ligne, const char* _fun
}
#elif defined(__TARGET_OS__IOs)
iosNSLog(handle);
#elif defined(__TARGET_OS__Windows)
{
static FILE* fileNode = fopen("log.txt", "w");
*pointer++ = '\n';
*pointer = '\0';
fprintf(fileNode, handle);
switch(_level) {
default:
break;
case elog::level_critical:
case elog::level_error:
case elog::level_warning:
fflush(fileNode);
break;
}
}
#else
std::cout << handle << std::endl;
#endif

View File

@ -10,10 +10,12 @@
#include <sstream>
#include <ostream>
#include <vector>
#include <functional>
namespace elog {
/**
* @brief Log level is a simple list of all log availlable. This enum is used when setting a log and when user chose the level of log displayed.
* @note: I use a "enum" and not an "enum class" because it create some problem on the macro declaration (not easy to convert number in an "enum class"
*/
enum level {
level_print = -1, //!< basic print for Help or result (never filtered)
@ -35,6 +37,9 @@ namespace elog {
* @brief Set the log level of a specific instance
* @param[in] _name Name of the intance
* @param[in] _level New level to set on the instance
* @code
* elog::setLevel("ewol", elog::level_critical);
* @endcode
*/
void setLevel(const std::string& _name, enum elog::level _level);
/**
@ -46,6 +51,9 @@ namespace elog {
/**
* @brief Set global debug level
* @param[in] _level New level to set on the instance
* @code
* elog::setLevel(elog::level_verbose);
* @endcode
*/
void setLevel(enum elog::level _level);
/**
@ -132,6 +140,30 @@ namespace elog {
* @param[in] _removeElement Number of element remove in the stack before display (permit to remove log function call)
*/
void displayBacktrace(bool _breakAtEnd = false, int32_t _removeElement=0);
/**
* @brief function definition of an external log function
* @param[in] _libName pounter on a string containing the library name
* @param[in] _level Level of the log (can be filter with @ref setLevel)
* @param[in] _ligne Line of the log
* @param[in] _funcName Full function name: 'void etk::MyClass::functionName(...)'
* @param[in] _log String containing the Log
*/
using callbackLog = std::function<void (const char* _libName, enum elog::level _level, int32_t _ligne, const char* _funcName, const char* _log)>;
/**
* @brief Set a callback to display all log in an other framework
* @param[in] _callback Function to call when log arrive
*/
void setCallbackLog(const elog::callbackLog& _callback);
/**
* @brief Set log done in a specific file
* @param[in] _filename Name of the file to log (if not set the log system select alone the log file)
* @note in release the log is automatically store in a file in the system. (windows log is done in file automatically)
*/
void setLogInFile(const std::string& _filename="");
/**
* @brief Disable log in a file
*/
void unsetLogInFile();
};
/**

44
lutin_elog-sample.py Normal file
View File

@ -0,0 +1,44 @@
#!/usr/bin/python
import lutin.module as module
import lutin.tools as tools
def get_type():
return "BINARY"
def get_sub_type():
return "SAMPLE"
def get_desc():
return "elog sample"
def get_licence():
return "APACHE-2"
def get_compagny_type():
return "com"
def get_compagny_name():
return "atria-soft"
def get_maintainer():
return "authors.txt"
def get_version():
return "version.txt"
def create(target, module_name):
my_module = module.Module(__file__, module_name, get_type())
my_module.add_extra_flags()
# add the file to compile:
my_module.add_src_file([
'sample/debug.cpp',
'sample/main.cpp'
])
# build in C++ mode
my_module.compile_version("c++", 2011)
my_module.add_depend('elog')
my_module.add_path('sample')
return my_module

View File

@ -1,19 +0,0 @@
#!/usr/bin/python
import monkModule
import monkTools as tools
import os
def get_desc():
return "EWOL log library (simple log interface)"
def create():
# module name is 'ewol' and type binary.
myModule = monkModule.Module(__file__, 'elog', 'LIBRARY')
# enable doculentation :
myModule.set_website("http://atria-soft.github.io/elog/")
myModule.set_website_sources("http://github.com/atria-soft/elog/")
myModule.set_path(os.path.join(tools.get_current_path(__file__), "elog"))
myModule.set_path_general_doc(os.path.join(tools.get_current_path(__file__), "doc"))
# add the currrent module at the
return myModule

14
sample/debug.cpp Normal file
View File

@ -0,0 +1,14 @@
/** @file
* @author Edouard DUPIN
*
* @copyright 2016, Edouard DUPIN, all right reserved
*
* @license APACHE v2.0 (see license file)
*/
#include "debug.h"
int32_t appl::getLogId() {
static int32_t g_val = elog::registerInstance("your application name");
return g_val;
}

41
sample/debug.h Normal file
View File

@ -0,0 +1,41 @@
/** @file
* @author Edouard DUPIN
*
* @copyright 2016, Edouard DUPIN, all right reserved
*
* @license APACHE v2.0 (see license file)
*/
#pragma once
#include <elog/log.h>
namespace appl {
int32_t getLogId();
};
#define APPL_BASE(info,data) ELOG_BASE(appl::getLogId(),info,data)
#define APPL_PRINT(data) APPL_BASE(-1, data)
#define APPL_CRITICAL(data) APPL_BASE(1, data)
#define APPL_ERROR(data) APPL_BASE(2, data)
#define APPL_WARNING(data) APPL_BASE(3, data)
#ifdef DEBUG
#define APPL_INFO(data) APPL_BASE(4, data)
#define APPL_DEBUG(data) APPL_BASE(5, data)
#define APPL_VERBOSE(data) APPL_BASE(6, data)
#define APPL_TODO(data) APPL_BASE(4, "TODO : " << data)
#else
#define APPL_INFO(data) do { } while(false)
#define APPL_DEBUG(data) do { } while(false)
#define APPL_VERBOSE(data) do { } while(false)
#define APPL_TODO(data) do { } while(false)
#endif
#define APPL_ASSERT(cond,data) \
do { \
if (!(cond)) { \
APPL_CRITICAL(data); \
assert(!#cond); \
} \
} while (0)

90
sample/main.cpp Normal file
View File

@ -0,0 +1,90 @@
/** @file
* @author Edouard DUPIN
*
* @copyright 2016, Edouard DUPIN, all right reserved
*
* @license APACHE v2.0 (see license file)
*/
//! [elog_sample_main_include]
#include "debug.h"
#include <elog/elog.h>
//! [elog_sample_main_include]
#include <iostream>
//! [elog_sample_main_callback]
//! [elog_sample_main_callback_DECLARATION]
static void myExternalLogCallback(const char* _libName, enum elog::level _level, int32_t _ligne, const char* _funcName, const char* _log) {
//! [elog_sample_main_callback_DECLARATION]
switch(_level) {
default:
std::cout << "[?] ";
break;
case elog::level_print:
std::cout << "[P] ";
break;
case elog::level_critical:
std::cout << "[C] ";
break;
case elog::level_error:
std::cout << "[E] ";
break;
case elog::level_warning:
std::cout << "[W] ";
break;
case elog::level_info:
std::cout << "[I] ";
break;
case elog::level_debug:
std::cout << "[D] ";
break;
case elog::level_verbose:
std::cout << "[V] ";
break;
}
std::cout << _libName << " (" << _ligne << ") " << _funcName << " | " << _log << std::endl;
}
//! [elog_sample_main_callback]
//! [elog_sample_main]
/**
* @brief Main of the program (This can be set in every case, but it is not used in Andoid...).
* @param std IO
* @return std IO
*/
//! [elog_sample_main_base]
int main(int _argc, const char *_argv[]) {
// if you use etk/ewol/gale, elog init in contain in it.
elog::init(_argc, _argv);
//! [elog_sample_main_base]
//! [elog_sample_main_log]
APPL_VERBOSE("VERBOSE display");
APPL_DEBUG("DEBUG display");
APPL_INFO("INFO display");
APPL_WARNING("WARNING display");
APPL_ERROR("ERROR display");
APPL_PRINT("PRINT display");
//APPL_CRITICAL("CRITICAL display"); // Disable critical because it create an assert ...
//! [elog_sample_main_log]
// Change the global log level:
elog::setLevel(elog::level_verbose);
//! [elog_sample_main_callback_link]
// Set a callback:
elog::setCallbackLog(&myExternalLogCallback);
//! [elog_sample_main_callback_link]
// try again new logs:
APPL_VERBOSE("VERBOSE display");
APPL_DEBUG("DEBUG display");
APPL_INFO("INFO display");
APPL_WARNING("WARNING display");
APPL_ERROR("ERROR display");
APPL_PRINT("PRINT display");
return 0;
}
//! [elog_sample_main]