diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9991d07 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,21 @@ +name: CI + +on: + push: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Compile library + run: g++ -std=c++11 -Wall -Wextra -c text_support.cpp -o text_support.o + + - name: Build test binary + run: g++ -std=c++11 -Wall -Wextra text_support.cpp test_main.cpp -o test_runner + + - name: Run tests + run: ./test_runner diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e71f752 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +test_runner diff --git a/README.md b/README.md index ba94570..3bc489e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,85 @@ # TextSupport -Colored text & console log support for C++ +Colored text & console log support for C++ ![ScreenShot](https://github.com/FL1NE/TextSupport/blob/master/screenshot.png "ScreenShot") + +## Requirements + +- C++11 or later +- POSIX-compatible environment (Linux, macOS) + +## Usage + +### Include + +```cpp +#include "text_support.h" +``` + +### Named log functions + +```cpp +textSupport::debugMessage("something happened"); +textSupport::infoMessage("server started"); +textSupport::warningMessage("retrying connection"); +textSupport::errorMessage("file not found"); +textSupport::fatalMessage("unrecoverable error"); +``` + +### Log with LogLevel enum + +Use `log()` to select the level at call site: + +```cpp +textSupport::log(textSupport::LogLevel::Debug, "debug message"); +textSupport::log(textSupport::LogLevel::Info, "info message"); +textSupport::log(textSupport::LogLevel::Warning, "warning message"); +textSupport::log(textSupport::LogLevel::Error, "error message"); +textSupport::log(textSupport::LogLevel::Fatal, "fatal message"); +``` + +### Adding a new log level + +Edit `LOG_TABLE` in `text_support.cpp` and add a corresponding entry to the `LogLevel` enum in `text_support.h`: + +```cpp +// text_support.h +enum class LogLevel { Debug, Info, Warning, Error, Fatal, Verbose }; // add here + +// text_support.cpp +static const LogEntry LOG_TABLE[] = { + /* Debug */ { "DBG", TEXT_SUPPORT_BLUE, &std::cout }, + /* Info */ { "INFO", TEXT_SUPPORT_CYAN, &std::cout }, + /* Warning */ { "WARN", TEXT_SUPPORT_YELLOW, &std::cout }, + /* Error */ { "ERR", TEXT_SUPPORT_RED, &std::cerr }, + /* Fatal */ { "FATAL", TEXT_SUPPORT_BOLDRED, &std::cerr }, + /* Verbose */ { "VERBOSE", TEXT_SUPPORT_WHITE, &std::cout }, // add here +}; +``` + +### Color macros + +The following macros are available for direct use: + +| Macro | Color | +|---|---| +| `TEXT_SUPPORT_RESET` | Reset | +| `TEXT_SUPPORT_BLACK` | Black | +| `TEXT_SUPPORT_RED` | Red | +| `TEXT_SUPPORT_GREEN` | Green | +| `TEXT_SUPPORT_YELLOW` | Yellow | +| `TEXT_SUPPORT_BLUE` | Blue | +| `TEXT_SUPPORT_MAGENTA` | Magenta | +| `TEXT_SUPPORT_CYAN` | Cyan | +| `TEXT_SUPPORT_WHITE` | White | +| `TEXT_SUPPORT_BOLD*` | Bold variants of the above | + +## Build + +```sh +g++ -std=c++11 your_program.cpp text_support.cpp -o your_program +``` + +## License + +Copyright (c) 2017 FRONTL1NE All rights reserved diff --git a/test_main.cpp b/test_main.cpp new file mode 100644 index 0000000..c976bd5 --- /dev/null +++ b/test_main.cpp @@ -0,0 +1,19 @@ +#include "text_support.h" + +int main() { + // Named functions + textSupport::debugMessage("debugMessage OK"); + textSupport::infoMessage("infoMessage OK"); + textSupport::warningMessage("warningMessage OK"); + textSupport::errorMessage("errorMessage OK"); + textSupport::fatalMessage("fatalMessage OK"); + + // Direct log() with LogLevel enum + textSupport::log(textSupport::LogLevel::Debug, "log(Debug) OK"); + textSupport::log(textSupport::LogLevel::Info, "log(Info) OK"); + textSupport::log(textSupport::LogLevel::Warning, "log(Warning) OK"); + textSupport::log(textSupport::LogLevel::Error, "log(Error) OK"); + textSupport::log(textSupport::LogLevel::Fatal, "log(Fatal) OK"); + + return 0; +} diff --git a/text_support.cpp b/text_support.cpp index 3de4436..f95069b 100644 --- a/text_support.cpp +++ b/text_support.cpp @@ -16,200 +16,72 @@ #include "text_support.h" -void textSupport::debugMessage(const char* message){ +static std::string getTimestamp() { time_t timer; - struct tm *t_st; + struct tm t_st; time(&timer); - t_st = localtime(&timer); - - - std::cout << "[" << TEXT_SUPPORT_BLUE << "DBG" << TEXT_SUPPORT_RESET << "]" << " " - << std::setw(4) << std::setfill('0') << t_st->tm_year + 1900 << "-" - << std::setw(2) << std::setfill('0') << t_st->tm_mon + 1 << "-" - << std::setw(2) << std::setfill('0') << t_st->tm_mday << " " - << std::setw(2) << std::setfill('0') << t_st->tm_hour << ":" - << std::setw(2) << std::setfill('0') << t_st->tm_min << ":" - << std::setw(2) << std::setfill('0') << t_st->tm_sec << " " - << message << std::endl; + localtime_r(&timer, &t_st); + + std::ostringstream oss; + oss << std::setw(4) << std::setfill('0') << t_st.tm_year + 1900 << "-" + << std::setw(2) << std::setfill('0') << t_st.tm_mon + 1 << "-" + << std::setw(2) << std::setfill('0') << t_st.tm_mday << " " + << std::setw(2) << std::setfill('0') << t_st.tm_hour << ":" + << std::setw(2) << std::setfill('0') << t_st.tm_min << ":" + << std::setw(2) << std::setfill('0') << t_st.tm_sec; + return oss.str(); } -void textSupport::debugMessage(std::string message){ - time_t timer; - struct tm *t_st; - time(&timer); - t_st = localtime(&timer); - - - std::cout << "[" << TEXT_SUPPORT_BLUE << "DBG" << TEXT_SUPPORT_RESET << "]" << " " - << std::setw(4) << std::setfill('0') << t_st->tm_year + 1900 << "-" - << std::setw(2) << std::setfill('0') << t_st->tm_mon + 1 << "-" - << std::setw(2) << std::setfill('0') << t_st->tm_mday << " " - << std::setw(2) << std::setfill('0') << t_st->tm_hour << ":" - << std::setw(2) << std::setfill('0') << t_st->tm_min << ":" - << std::setw(2) << std::setfill('0') << t_st->tm_sec << " " - << message << std::endl; -} +struct LogEntry { + const char* label; + const char* color; + std::ostream* stream; +}; -void textSupport::infoMessage(const char* message){ - time_t timer; - struct tm *t_st; - time(&timer); - t_st = localtime(&timer); +static const LogEntry LOG_TABLE[] = { + /* Debug */ { "DBG", TEXT_SUPPORT_BLUE, &std::cout }, + /* Info */ { "INFO", TEXT_SUPPORT_CYAN, &std::cout }, + /* Warning */ { "WARN", TEXT_SUPPORT_YELLOW, &std::cout }, + /* Error */ { "ERR", TEXT_SUPPORT_RED, &std::cerr }, + /* Fatal */ { "FATAL", TEXT_SUPPORT_BOLDRED, &std::cerr }, +}; - std::cout << "[" << TEXT_SUPPORT_CYAN << "INFO" << TEXT_SUPPORT_RESET << "]" << " " - << std::setw(4) << std::setfill('0') << t_st->tm_year + 1900 << "-" - << std::setw(2) << std::setfill('0') << t_st->tm_mon + 1 << "-" - << std::setw(2) << std::setfill('0') << t_st->tm_mday << " " - << std::setw(2) << std::setfill('0') << t_st->tm_hour << ":" - << std::setw(2) << std::setfill('0') << t_st->tm_min << ":" - << std::setw(2) << std::setfill('0') << t_st->tm_sec << " " - << message << std::endl; +void textSupport::log(LogLevel level, const std::string& message) { + const LogEntry& e = LOG_TABLE[static_cast(level)]; + const std::string lbl(e.label); + *e.stream << "[" << e.color << lbl << TEXT_SUPPORT_RESET << "]" + << std::string(7 - lbl.size(), ' ') + << getTimestamp() << " " << message << std::endl; } -void textSupport::infoMessage(std::string message){ - time_t timer; - struct tm *t_st; - time(&timer); - t_st = localtime(&timer); - - - std::cout << "[" << TEXT_SUPPORT_CYAN << "INFO" << TEXT_SUPPORT_RESET << "]" << " " - << std::setw(4) << std::setfill('0') << t_st->tm_year + 1900 << "-" - << std::setw(2) << std::setfill('0') << t_st->tm_mon + 1 << "-" - << std::setw(2) << std::setfill('0') << t_st->tm_mday << " " - << std::setw(2) << std::setfill('0') << t_st->tm_hour << ":" - << std::setw(2) << std::setfill('0') << t_st->tm_min << ":" - << std::setw(2) << std::setfill('0') << t_st->tm_sec << " " - << message << std::endl; -} - - -void textSupport::warningMessage(const char* message){ - time_t timer; - struct tm *t_st; - time(&timer); - t_st = localtime(&timer); - - - std::cout << "[" << TEXT_SUPPORT_YELLOW << "WARN" << TEXT_SUPPORT_RESET << "]" << " " - << std::setw(4) << std::setfill('0') << t_st->tm_year + 1900 << "-" - << std::setw(2) << std::setfill('0') << t_st->tm_mon + 1 << "-" - << std::setw(2) << std::setfill('0') << t_st->tm_mday << " " - << std::setw(2) << std::setfill('0') << t_st->tm_hour << ":" - << std::setw(2) << std::setfill('0') << t_st->tm_min << ":" - << std::setw(2) << std::setfill('0') << t_st->tm_sec << " " - << message << std::endl; -} - -void textSupport::warningMessage(std::string message){ - time_t timer; - struct tm *t_st; - time(&timer); - t_st = localtime(&timer); - - - std::cout << "[" << TEXT_SUPPORT_YELLOW << "WARN" << TEXT_SUPPORT_RESET << "]" << " " - << std::setw(4) << std::setfill('0') << t_st->tm_year + 1900 << "-" - << std::setw(2) << std::setfill('0') << t_st->tm_mon + 1 << "-" - << std::setw(2) << std::setfill('0') << t_st->tm_mday << " " - << std::setw(2) << std::setfill('0') << t_st->tm_hour << ":" - << std::setw(2) << std::setfill('0') << t_st->tm_min << ":" - << std::setw(2) << std::setfill('0') << t_st->tm_sec << " " - << message << std::endl; -} - - -void textSupport::errorMessage(const char* message){ - time_t timer; - struct tm *t_st; - time(&timer); - t_st = localtime(&timer); - - - std::cerr << "[" << TEXT_SUPPORT_RED << "ERR" << TEXT_SUPPORT_RESET << "]" << " " - << std::setw(4) << std::setfill('0') << t_st->tm_year + 1900 << "-" - << std::setw(2) << std::setfill('0') << t_st->tm_mon + 1 << "-" - << std::setw(2) << std::setfill('0') << t_st->tm_mday << " " - << std::setw(2) << std::setfill('0') << t_st->tm_hour << ":" - << std::setw(2) << std::setfill('0') << t_st->tm_min << ":" - << std::setw(2) << std::setfill('0') << t_st->tm_sec << " " - << message << std::endl; -} - -void textSupport::errorMessage(std::string message){ - time_t timer; - struct tm *t_st; - time(&timer); - t_st = localtime(&timer); - - - std::cerr << "[" << TEXT_SUPPORT_RED << "ERR" << TEXT_SUPPORT_RESET << "]" << " " - << std::setw(4) << std::setfill('0') << t_st->tm_year + 1900 << "-" - << std::setw(2) << std::setfill('0') << t_st->tm_mon + 1 << "-" - << std::setw(2) << std::setfill('0') << t_st->tm_mday << " " - << std::setw(2) << std::setfill('0') << t_st->tm_hour << ":" - << std::setw(2) << std::setfill('0') << t_st->tm_min << ":" - << std::setw(2) << std::setfill('0') << t_st->tm_sec << " " - << message << std::endl; -} - - -void textSupport::fatalMessage(const char* message){ - time_t timer; - struct tm *t_st; - time(&timer); - t_st = localtime(&timer); - - - std::cerr << "[" << TEXT_SUPPORT_BOLDRED << "FATAL" << TEXT_SUPPORT_RESET << "]" << " " - << std::setw(4) << std::setfill('0') << t_st->tm_year + 1900 << "-" - << std::setw(2) << std::setfill('0') << t_st->tm_mon + 1 << "-" - << std::setw(2) << std::setfill('0') << t_st->tm_mday << " " - << std::setw(2) << std::setfill('0') << t_st->tm_hour << ":" - << std::setw(2) << std::setfill('0') << t_st->tm_min << ":" - << std::setw(2) << std::setfill('0') << t_st->tm_sec << " " - << message << std::endl; -} - -void textSupport::fatalMessage(std::string message){ - time_t timer; - struct tm *t_st; - time(&timer); - t_st = localtime(&timer); - - - std::cerr << "[" << TEXT_SUPPORT_BOLDRED << "FATAL" << TEXT_SUPPORT_RESET << "]" << " " - << std::setw(4) << std::setfill('0') << t_st->tm_year + 1900 << "-" - << std::setw(2) << std::setfill('0') << t_st->tm_mon + 1 << "-" - << std::setw(2) << std::setfill('0') << t_st->tm_mday << " " - << std::setw(2) << std::setfill('0') << t_st->tm_hour << ":" - << std::setw(2) << std::setfill('0') << t_st->tm_min << ":" - << std::setw(2) << std::setfill('0') << t_st->tm_sec << " " - << message << std::endl; -} +void textSupport::debugMessage(const std::string& message) { log(LogLevel::Debug, message); } +void textSupport::infoMessage(const std::string& message) { log(LogLevel::Info, message); } +void textSupport::warningMessage(const std::string& message) { log(LogLevel::Warning, message); } +void textSupport::errorMessage(const std::string& message) { log(LogLevel::Error, message); } +void textSupport::fatalMessage(const std::string& message) { log(LogLevel::Fatal, message); } -void textSupport::testMessage(){ +void textSupport::testMessage() { std::cout - << TEXT_SUPPORT_RESET << "RESET " - << TEXT_SUPPORT_BLACK << "BLACK " - << TEXT_SUPPORT_RED << "RED " - << TEXT_SUPPORT_GREEN << "GREEN " - << TEXT_SUPPORT_YELLOW << "YELLOW " - << TEXT_SUPPORT_BLUE << "BLUE " + << TEXT_SUPPORT_RESET << "RESET " + << TEXT_SUPPORT_BLACK << "BLACK " + << TEXT_SUPPORT_RED << "RED " + << TEXT_SUPPORT_GREEN << "GREEN " + << TEXT_SUPPORT_YELLOW << "YELLOW " + << TEXT_SUPPORT_BLUE << "BLUE " << TEXT_SUPPORT_MAGENTA << "MAGENTA " - << TEXT_SUPPORT_CYAN << "CYAN " - << TEXT_SUPPORT_WHITE << "WHITE " - << TEXT_SUPPORT_BOLDBLACK << "BOLDBLACK " - << TEXT_SUPPORT_BOLDRED << "BOLDRED " - << TEXT_SUPPORT_BOLDGREEN << "BOLDGREEN " - << TEXT_SUPPORT_BOLDYELLOW << "BOLDYELLOW " - << TEXT_SUPPORT_BOLDBLUE << "BOLDBLUE " + << TEXT_SUPPORT_CYAN << "CYAN " + << TEXT_SUPPORT_WHITE << "WHITE " + << TEXT_SUPPORT_BOLDBLACK << "BOLDBLACK " + << TEXT_SUPPORT_BOLDRED << "BOLDRED " + << TEXT_SUPPORT_BOLDGREEN << "BOLDGREEN " + << TEXT_SUPPORT_BOLDYELLOW << "BOLDYELLOW " + << TEXT_SUPPORT_BOLDBLUE << "BOLDBLUE " << TEXT_SUPPORT_BOLDMAGENTA << "BOLDMAGENTA " - << TEXT_SUPPORT_BOLDCYAN << "BOLDCYAN " - << TEXT_SUPPORT_BOLDWHITE << "BOLDWHITE " + << TEXT_SUPPORT_BOLDCYAN << "BOLDCYAN " + << TEXT_SUPPORT_BOLDWHITE << "BOLDWHITE " << TEXT_SUPPORT_RESET << std::endl; textSupport::debugMessage("This is DEBUG Message."); diff --git a/text_support.h b/text_support.h index f664e74..ee37edd 100644 --- a/text_support.h +++ b/text_support.h @@ -38,16 +38,15 @@ namespace textSupport{ - void debugMessage(const char* message); - void debugMessage(std::string message); - void infoMessage(const char* message); - void infoMessage(std::string message); - void warningMessage(const char* message); - void warningMessage(std::string message); - void errorMessage(const char* message); - void errorMessage(std::string message); - void fatalMessage(const char* message); - void fatalMessage(std::string message); + enum class LogLevel { Debug, Info, Warning, Error, Fatal }; + + void log(LogLevel level, const std::string& message); + + void debugMessage(const std::string& message); + void infoMessage(const std::string& message); + void warningMessage(const std::string& message); + void errorMessage(const std::string& message); + void fatalMessage(const std::string& message); void testMessage(void); }