C++ Colorized Console Output

This post explores an easy way to add color to console output.

Output to the console is very simple and useful technique when writing code to unit test functions or libraries. It is also a useful tool for outputting debugging information in programs that use a GUI interface. In a previous post I discussed a simple way to use the console for debugging and logging. In this post I'll explore how to add colors to the logging macros to make them stand out even more.

Here is the source code from Derzu for "Colorize.h" that implements the function that we will use to change the color on an ANSI compliant console.

Colorize.h
// By Derzu on StackOverflow Sep 28, 2021 at 4:11
// https://stackoverflow.com/questions/4053837/colorizing-text-in-the-console-with-c

#ifndef COLORIZE_H
#define COLORIZE_H

#include <iostream>
#include <cstdio>

namespace Colorize {
#ifdef _WIN32
#include <windows.h>

#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#define DISABLE_NEWLINE_AUTO_RETURN  0x0008

    inline void activateVirtualTerminal()
    {
        HANDLE handleOut = GetStdHandle(STD_OUTPUT_HANDLE);
        DWORD consoleMode;
        GetConsoleMode( handleOut , &consoleMode);
        consoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
        consoleMode |= DISABLE_NEWLINE_AUTO_RETURN;
        SetConsoleMode( handleOut , consoleMode );
    }
#endif

    enum COLORS {
        NC = -1,
        BLACK,
        RED,
        GREEN,
        YELLOW,
        BLUE,
        MAGENTA,
        CYAN,
        WHITE,
    };

/**
* Colorize terminal colors ANSI escape sequences.
*
* @param font font color (-1 to 7), see COLORS enum
* @param back background color (-1 to 7), see COLORS enum
* @param style font style (1==bold, 4==underline)
**/
    inline const char *colorize(int font, int back = -1, int style = -1) {
        static char code[20];

        if (font >= 0)
            font += 30;
        else
            font = 0;
        if (back >= 0)
            back += 40;
        else
            back = 0;

        if (back > 0 && style > 0) {
            std::sprintf(code, "\033[%d;%d;%dm", font, back, style);
        } else if (back > 0) {
            std::sprintf(code, "\033[%d;%dm", font, back);
        } else {

            std::sprintf(code, "\033[%dm", font);
        }

        return code;
    }

}
#endif

/*
int main()
{
#ifdef _WIN32
    activateVirtualTerminal();
#endif

    cout << colorize(RED) << "trying red" << colorize(NC) << endl;
    cout << colorize(RED, BLACK) << "red and black background" << colorize(NC) << endl;
    cout << colorize(YELLOW, BLUE, 1) << "yellow blue bold" << colorize(NC) << endl;
    cout << colorize(BLACK, WHITE) << "Black white" << colorize(NC) << endl;
    cout << colorize(MAGENTA, CYAN) << "Magenta cyan" << colorize(NC) << endl;

    return 1;
}
*/

Using this header file we can change our "Debug.h" to use different colors for the different log levels.

Debug.h
//
// Created by Richard Lesh on 11/3/21.
// Copyright © 2021 Richard Lesh.  All rights reserved.
//

// To turn on DEBUG macros for programming by contract
// copy one of the following DEBUG defines to your program
// before including "Debug.h".  Best set at project level in IDE.
//#define DEBUG 1
//#undef DEBUG

// To use the LOG_xxxx_MESSAGE macros include "ParameterPacks.h"
// before you include "Debug.h"

// To turn on logging macros for logging to console
// copy one of the following LOG_LEVEL defines to your
// program before including "Debug.h". Best set at project level
// in IDE.
//#define LOG_LEVEL 0     // Fatal only
//#define LOG_LEVEL 1     // Fatal or Error
//#define LOG_LEVEL 2     // Fatal, Error or Warning
//#define LOG_LEVEL 3     // Fatal, Error, Warning or Info
//#define LOG_LEVEL 4     // Fatal, Error, Warning, Info or Debug

#ifndef _DEBUG_H
#define _DEBUG_H

#include <cstdio>
#include <iostream>
#include "Colorize.h"

#define LOG_PRINTF(b, color, args...) { if (b) { \
        std::cout << __FILE_NAME__ << "(" << __LINE__ << "):" << colorize(Colorize::color); \
        std::fprintf(stdout, args); \
        std::cout << colorize(Colorize::NC) << std::endl; \
    }}

#ifdef DEBUG
#define PRECONDITION(b) LOG_FATAL(!(b), "Precondition failure: " #b)
#define POSTCONDITION(b) LOG_FATAL(!(b), "Postcondition failure: " #b)
#define INVARIANT(b) LOG_FATAL(!(b), "Invariant failure: " #b)
#define ASSUME(b) LOG_FATAL(!(b), "Assumption failure: " #b)
#else
#define PRECONDITION(b)
#define POSTCONDITION(b)
#define INVARIANT(b)
#define ASSUME(b)
#endif

#if LOG_LEVEL >= 0
#define LOG_FATAL(b, args...) LOG_PRINTF((b), RED, args){if (b) throw std::exception();}
#else
#define LOG_FATAL(b, ...)
#endif

#if LOG_LEVEL >= 1
#define LOG_ERROR(b, args...) LOG_PRINTF((b), RED, args)
#else
#define LOG_ERROR(b, ...)
#endif

#if LOG_LEVEL >= 2
#define LOG_WARN(b, args...) LOG_PRINTF((b), YELLOW, args)
#else
#define LOG_WARN(b, ...)
#endif

#if LOG_LEVEL >= 3
#define LOG_INFO(b, args...) LOG_PRINTF((b), GREEN, args)
#else
#define LOG_INFO(b, ...)
#endif

#if LOG_LEVEL >= 4
#define LOG_DEBUG(b, args...) LOG_PRINTF((b), BLUE, args)
#else
#define LOG_DEBUG(b, ...)
#endif

#endif