Closed RedsAnDev closed 7 years ago
Puoi usare la utility di logo che uso io nei miei programmi. E' stupida e semplice, ma definisce bene il concetto. Ignora pure le funzione "bdebug, bfinest, bfiner, bfine, binfo, bwarning, berror e bcritical": danno una profondità in più che però non è richesta. Rivedendo il codice anche addAppender
, setLevel
, excludeLogger
, includeLogger
e clearExcludedLoggers
puoi ignorarle
Header file:
/**
* \file log.h
*
* Tiny logging utility
*
* This header provides you a simple, but effective, debugging logging utility.
*
* The module provides several levels of logging:
* \li debug;
* \li finest;
* \li finer;
* \li fine;
* \li info;
* \li warning;
* \li error;
* \li critical;
*
* Each of them has 3 functions you can use to logging (X stands for one of the logging levels)
*
* \li <tt>X(format, ...)</tt>: use to log something with a particular level;
* \li <tt>gX(cmds, format, ...)</tt> (*g* stands for "general"): use if you want to execute something before actuallly logging. If the log is disabled, cmds are not executed at all;
* \li <tt>bX(format, ...)</tt>(*b* stands for "buffer"): provides a buffer of char you can use only in your logging code. The buffer is named \c log_buffer.
*
* Created on: Dec 23, 2016
* Author: koldar
*/
#ifndef LOG_H_
#define LOG_H_
/**
* Enable this to improve log performance
*
* Define this macro with the level you want to use.
* This will make the logger slightly quicker, but will disable the runtime ::setLevel call
* Use only if you don't care about the runtime level at all.
*
* \note
* You should set this flag \b before including this header, otherwise nothing will happen
*
* \example
* Use this to trash away logs like ::LogLevel::LOG_DEBUG or ::LogLevel::LOG_FINE
*/
#ifdef QUICK_LOG
#endif
#define __LOG_ALL 0
#define __LOG_DEBUG 1
#define __LOG_FINEST 2
#define __LOG_FINER 3
#define __LOG_FINE 4
#define __LOG_INFO 5
#define __LOG_WARNING 6
#define __LOG_ERROR 7
#define __LOG_CRITICAL 8
#define __LOG_OFF 9
/**
* All the available log levels
*
* \attention
* Do not assign numbers to the levels, otheriwse ::LOG_LEVEL_SIZE won't work anymore
*/
typedef enum LogLevel {
LOG_ALL = __LOG_ALL,
LOG_DEBUG = __LOG_DEBUG,
LOG_FINEST = __LOG_FINEST,
LOG_FINER = __LOG_FINER,
LOG_FINE = __LOG_FINE,
LOG_INFO = __LOG_INFO,
LOG_WARNING = __LOG_WARNING,
LOG_ERROR = __LOG_ERROR,
LOG_CRITICAL = __LOG_CRITICAL,
LOG_OFF = __LOG_OFF
} LogLevel;
///the number of log levels in ::LogLevel
#define LOG_LEVEL_SIZE 10
/**
* Represents the type of the function we need to feed to ::addAppenders
*
* The typedef defines what exactly is an appender.
*
* \def An appender is whatever function that, given:
* \li the file where the log has been called;
* \li the function where the log has been called;
* \li the line involved;
* \li the ::LogLevel the log has been called on;
* \li the message string the developer wants to store
*
* actually adds to the physical log (i.e. a database, a file, the stdout) the log entry.
*/
typedef void (*appenderFunction)(const char*, const char*, const int, const LogLevel, const char*);
void __generic_log_function(const LogLevel level, const char* fileName, const char* functionName, const int lineNo, const char* format, ...);
void setLevel(LogLevel level);
void excludeLogger(const char* logger);
void includeLogger(const char* logger);
void clearExcludedLoggers();
/**
* Clear all the appenders added up until now
*/
void clearAppenders();
/**
* Add a new appender to the appenders list
*
* \todo at the moment, only an appender per time is available
*/
void addAppender(appenderFunction);
#ifndef LOG_BUFFER
# define LOG_BUFFER __cutils_log_buffer
#endif
#define log_buffer LOG_BUFFER
#ifndef LOG_BUFFER_SIZE
# define LOG_BUFFER_SIZE 300
#endif
//TODO generate genX and bX for every other levels as well!
//TODO test
#define CUTILS_LOG_GLOG(function, cmds, format, ...) \
{ \
cmds; \
function(format, ## __VA_ARGS__); \
}
#define CUTILS_LOG_BLOG(function, format, ...) g ## function (char log_buffer[LOG_BUFFER_SIZE], format, ## __VA_ARGS__)
#if QUICK_LOG > __LOG_DEBUG
# pragma message "DEBUG LOG TURNED OFF"
# define gdebug(cmds, format, ...) 0
# define bdebug(format, ...) 0
# define debug(format,...) 0
#else
# define gdebug(cmds, format, ...) CUTILS_LOG_GLOG(debug, cmds, format, ## __VA_ARGS__)
# define bdebug(format, ...) CUTILS_LOG_BLOG(debug, format, ## __VA_ARGS__)
# define debug(format,...) __generic_log_function(LOG_DEBUG, __FILE__, __FUNCTION__, __LINE__, format, ##__VA_ARGS__)
#endif
#if QUICK_LOG > __LOG_FINEST
# pragma message "FINEST LOG TURNED OFF"
# define gfinest(cmds, format, ...) 0
# define bfinest(format, ...) 0
# define finest(format,...) 0
#else
#define gfinest(cmds, format, ...) CUTILS_LOG_GLOG(finest, cmds, format, ## __VA_ARGS__)
# define bfinest(format, ...) CUTILS_LOG_BLOG(finest, format, ## __VA_ARGS__)
# define finest(format,...) __generic_log_function(LOG_FINEST, __FILE__, __FUNCTION__, __LINE__, format, ##__VA_ARGS__)
#endif
#if QUICK_LOG > __LOG_FINER
# pragma message "FINER LOG TURNED OFF"
# define gfiner(cmds, format, ...) 0
# define bfiner(format, ...) 0
# define finer(format,...) 0
#else
# define gfiner(cmds, format, ...) CUTILS_LOG_GLOG(finer, cmds, format, ## __VA_ARGS__)
# define bfiner(format, ...) CUTILS_LOG_BLOG(finer, format, ## __VA_ARGS__)
# define finer(format,...) __generic_log_function(LOG_FINER, __FILE__, __FUNCTION__, __LINE__, format, ##__VA_ARGS__)
#endif
#if QUICK_LOG > __LOG_FINE
# pragma message "FINER LOG TURNED OFF"
# define gfine(cmds, format, ...) 0
# define bfine(format, ...) 0
# define fine(format,...) 0
#else
# define gfine(cmds, format, ...) CUTILS_LOG_GLOG(fine, cmds, format, ## __VA_ARGS__)
# define bfine(format, ...) CUTILS_LOG_BLOG(fine, format, ## __VA_ARGS__)
# define fine(format,...) __generic_log_function(LOG_FINE, __FILE__, __FUNCTION__, __LINE__, format, ##__VA_ARGS__)
#endif
#if QUICK_LOG > __LOG_INFO
# pragma message "INFO LOG TURNED OFF"
# define ginfo(cmds, format, ...) 0
# define binfo(format, ...) 0
# define info(format,...) 0
#else
# define ginfo(cmds, format, ...) CUTILS_LOG_GLOG(info, cmds, format, ## __VA_ARGS__)
# define binfo(format, ...) CUTILS_LOG_BLOG(info, format, ## __VA_ARGS__)
# define info(format,...) __generic_log_function(LOG_INFO, __FILE__, __FUNCTION__, __LINE__, format, ##__VA_ARGS__)
#endif
#if QUICK_LOG > __LOG_WARNING
# pragma message "WARNING LOG TURNED OFF"
# define gwarning(cmds, format, ...) 0
# define bwarning(format, ...) 0
# define warning(format,...) 0
#else
# define gwarning(cmds, format, ...) CUTILS_LOG_GLOG(warning, cmds, format, ## __VA_ARGS__)
# define bwarning(format, ...) CUTILS_LOG_BLOG(warning, format, ## __VA_ARGS__)
# define warning(format,...) __generic_log_function(LOG_WARNING, __FILE__, __FUNCTION__, __LINE__, format, ##__VA_ARGS__)
#endif
#if QUICK_LOG > __LOG_ERROR
# pragma message "ERROR LOG TURNED OFF"
# define gerror(cmds, format, ...) 0
# define berror(format, ...) 0
# define error(format,...) 0
#else
# define gerror(cmds, format, ...) CUTILS_LOG_GLOG(error, cmds, format, ## __VA_ARGS__)
# define berror(format, ...) CUTILS_LOG_BLOG(error, format, ## __VA_ARGS__)
# define error(format,...) __generic_log_function(LOG_ERROR, __FILE__, __FUNCTION__, __LINE__, format, ##__VA_ARGS__)
#endif
#if QUICK_LOG > __LOG_CRITICAL
# pragma message "CRITICAL LOG TURNED OFF"
# define gcritical(cmds, format, ...) 0
# define bcritical(format, ...) 0
# define critical(format,...) 0
#else
# define gcritical(cmds, format, ...) CUTILS_LOG_GLOG(critical, cmds, format, ## __VA_ARGS__)
# define bcritical(format, ...) CUTILS_LOG_BLOG(critical, format, ## __VA_ARGS__)
# define critical(format,...) __generic_log_function(LOG_CRITICAL, __FILE__, __FUNCTION__, __LINE__, format, ##__VA_ARGS__)
#endif
#endif /* LOG_H_ */
Source code
/*
* log.c
*
* Created on: Dec 25, 2016
* Author: koldar
*/
#include "log.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <libgen.h>
#include <stdlib.h>
const char* LOGLEVEL_TO_STRING[LOG_LEVEL_SIZE] = {
"ALL",
"DEBUG",
"FINEST",
"FINER",
"FINE",
"INFO",
"WARNING",
"ERROR",
"CRITICAL",
"NOLOG"
};
typedef struct Names {
char* name;
//the hash of the name
unsigned long hashName;
struct Names* next;
} Names;
/**
* The level our log is operating on
*/
static LogLevel logLevel = LOG_ALL;
/**
* List of loggers (aka files) that we won't track
*/
static Names* excludedLoggers = NULL;
/**
* a buffer used to put all the log parameters inside the message template
*/
static char logBuffer[1000];
static void logToStdOut(const char* fileName, const char* functionName, const int lineNo, const LogLevel level, const char* message);
static void logToDevNull(const char* fileName, const char* functionName, const int lineNo, const LogLevel level, const char* message);
static void addHeadInNames(Names** names, const char* name);
static void removeFromNames(Names** names, const char* name);
static void removeAllNames(Names** names);
static bool isExcluded(Names** names, const char* name);
static unsigned long hashDjb2(const char *str);
/**
* A function we will use to log the message to the physical storage (either a database, the network or a file)
*/
static appenderFunction appenders = &logToStdOut;
void setLevel(LogLevel level) {
logLevel = level;
}
/**
* Avoid to print everything coming from a particular logger
*
* @param[in] logger the logger that from this time until ::includeLogger call won't be tracked down at all
*/
void excludeLogger(const char* logger) {
addHeadInNames(&excludedLoggers, logger);
}
/**
* Include in the logging the logs coming from this logger
*
* @param[in] logger the logger to include in the log output
*/
void includeLogger(const char* logger) {
removeFromNames(&excludedLoggers, logger);
}
void clearExcludedLoggers() {
removeAllNames(&excludedLoggers);
}
void clearAppenders() {
appenders = &logToDevNull;
}
void __generic_log_function(const LogLevel level, const char* absoluteFilePath, const char* functionName, const int lineNo, const char* format, ...) {
if (level < logLevel) {
return;
}
char* fileName = basename(absoluteFilePath);
if (isExcluded(&excludedLoggers, fileName)) {
return;
}
va_list argList;
va_start(argList, format);
vsprintf(logBuffer, format, argList);
va_end(argList);
appenders(fileName, functionName, lineNo, level, logBuffer);
}
static void addHeadInNames(Names** names, const char* name) {
Names* retVal = malloc(sizeof(Names));
if (retVal == NULL) {
return;
}
char* nameCopy = malloc(strlen(name)+1);
if (nameCopy == NULL) {
return;
}
strcpy(nameCopy, name);
retVal->name = nameCopy;
retVal->hashName = hashDjb2(retVal->name);
retVal->next = NULL;
if (*names != NULL) {
retVal->next = (*names);
}
(*names) = retVal;
}
static void removeAllNames(Names** names)
Names* tmp = *names;
Names* tmp2 = NULL;
while(tmp != NULL) {
tmp2 = tmp->next;
free(tmp->name);
free(tmp);
tmp = tmp2;
}
*names = NULL;
}
static void removeFromNames(Names** names, const char* name) {
Names* tmp = *names;
Names* old = NULL;
unsigned long hashName = hashDjb2(name);
while (tmp != NULL) {
if ( tmp->hashName == hashName) {
if (old == NULL) {//we need to remove the head
(*names) = tmp->next;
} else {
old->next = tmp->next;
free(tmp->name);
free(tmp);
}
}
old = tmp;
tmp = tmp->next;
}
}
static bool isExcluded(Names** names, const char* name) {
if (*names == NULL) {
return false;
}
Names* tmp = *names;
unsigned long hashName = hashDjb2(name);
while (tmp != NULL) {
if (tmp->hashName == hashName) {
return true;
}
tmp = tmp->next;
}
return false;
}
/**
* hash function using djb2 algorithm
*
* For further information please see <a href="http://www.cse.yorku.ca/~oz/hash.html">hash algorithms</a>
*
* @param[in] str the string to hash
* @return the hash value
*/
static unsigned long hashDjb2(const char *str) {
unsigned long hash = 5381;
int c;
while (c = *str++)
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
return hash;
}
static void logToStdOut(const char* fileName, const char* functionName, const int lineNo, const LogLevel level, const char* message) {
printf("%s:%s:%d[%s] %s\n", fileName, functionName, lineNo, LOGLEVEL_TO_STRING[level], message);
fflush(stdout);
}
static void logToDevNull(const char* fileName, const char* functionName, const int lineNo, const LogLevel level, const char* message) {
}
I need support for this "utility". Actually version is realy simple and is totalli incompleted. @Koldar maybe monday or when you are avaiable, can share with me some references? Thank you so much!