/** * A massively over-engineered logging system. The basic idea here * is to set up logging that can be changed at compile time, so that * durign development you have lots of logging and the in production * very little, WITH NO CODE CHANGE. More, when logging at the ERROR level * all logging is to stderr, so it has delivery guaranteed. * * I used a system very similar to this for several years at one of my jobs. * * @author gtowell * Created: Sep 2019 * Modified (heavily): March 2021 * **/ #include /** * To change the log level * gcc -D LOG_LEVEL=v logging.c * * where v = 0,1,5,10 * * If the log level is not wrapped in an ifndef, then the * compile time setting is overwritten * **/ #ifndef LOG_LEVEL #define LOG_LEVEL 0 #endif #define LOG_VERBOSE 1 #define LOG_INFO 5 #define LOG_ERROR 10 // basic logging comment #define LOG(level, ...) if (LOG_LEVEL <= level) fprintf(LOG_LEVEL>=LOG_ERROR?stderr:stdout , __VA_ARGS__) // a logger to check for possible divide by zero errors #define CHECK_ZERO(d) if (d==0) LOG(LOG_ERROR, "Divide by zero line:%d file:%s\n", __LINE__, __FILE__) // log the start of a function ... arguably should include the args #define FUNC_START() if (LOG_LEVEL <= LOG_INFO) fprintf(LOG_LEVEL>=LOG_ERROR?stderr:stdout , "start %s\n", __func__) // log the end of a function #define FUNC_END() if (LOG_LEVEL <= LOG_INFO) fprintf(LOG_LEVEL>=LOG_ERROR?stderr:stdout , "finish %s\n", __func__) //Covert a character to upper case. Not clear that having this as a define //makes any sense. #define TO_UPPER(c) (c>='a' &&c<='z')?c+('A'-'a'):c /** * A function, just to illustrate everything * **/ void v1() { FUNC_START(); for (int i=0; i<10; i++) { CHECK_ZERO(i); LOG(LOG_VERBOSE, "%d %c %c\n", i, 'a'+i, TO_UPPER('a'+i)); } FUNC_END(); } int main(int argc, char * argv[]) { for (int i=0; i