STB1019 / SkullOfSummer

Learn stuff with less 7-days
Apache License 2.0
5 stars 0 forks source link

Log Utility #4

Closed RedsAnDev closed 7 years ago

RedsAnDev commented 7 years ago

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!

Koldar commented 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) {
}