From 69493bc85778a12feeb073d60b67a0e88dfacc29 Mon Sep 17 00:00:00 2001 From: heck Date: Sat, 10 Jun 2023 18:39:12 +0200 Subject: [PATCH] init (from Orca-c heck fork) --- .clang-format | 42 ++++ .gitignore | 35 ++++ LICENSE.md | 21 ++ Makefile | 13 ++ Makefile.conf | 80 ++++++++ README.md | 3 + build.conf.example | 5 + src/Makefile | 32 +++ src/log.cc | 465 +++++++++++++++++++++++++++++++++++++++++++ src/log.h | 148 ++++++++++++++ test/Makefile | 31 +++ test/test_log.c | 95 +++++++++ test/test_log_cxx.cc | 99 +++++++++ 13 files changed, 1069 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 LICENSE.md create mode 100644 Makefile create mode 100644 Makefile.conf create mode 100644 README.md create mode 100644 build.conf.example create mode 100644 src/Makefile create mode 100644 src/log.cc create mode 100644 src/log.h create mode 100644 test/Makefile create mode 100644 test/test_log.c create mode 100644 test/test_log_cxx.cc diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..0fa41a7 --- /dev/null +++ b/.clang-format @@ -0,0 +1,42 @@ +BasedOnStyle: LLVM +ReflowComments: false + +MacroBlockBegin: "^BEGIN_OPERATOR" +MacroBlockEnd: "^END_OPERATOR" + +Language: Cpp +DerivePointerAlignment: true +SortIncludes: Never +PointerAlignment: Left +AlignAfterOpenBracket: AlwaysBreak +AlignOperands: AlignAfterOperator +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +BinPackArguments: false +BinPackParameters: false +ExperimentalAutoDetectBinPacking: true +BreakBeforeBraces: Custom +BraceWrapping: + AfterFunction: true +ColumnLimit: 100 +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +PenaltyBreakBeforeFirstCallParameter: 0 +PenaltyReturnTypeOnItsOwnLine: 1000000 +PenaltyBreakAssignment: 1000000 +PenaltyExcessCharacter: 10 +IndentCaseLabels: true +IndentWidth: 4 +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: All +SpaceAfterTemplateKeyword: false +AccessModifierOffset: -4 +AllowShortBlocksOnASingleLine: Always +IndentPPDirectives: BeforeHash +IndentExternBlock: Indent +Cpp11BracedListStyle: false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..56e901f --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ + +# Build config +/build.conf +/.idea + +# Prerequisites +*.d + +# Object files +*.o + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.a + +# Executables +/src/main_cli +/src/main_tui + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +#Test +/test/test_main +/test/test_main2 +/test/test_log +/test/test_log_cxx +/test/customfile.log +/test/newlogfile.log diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..9714691 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Hundredrabbits + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6d34a31 --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +.PHONY: all test src clean + +all: test src + +src: + $(MAKE) -C src + +test: + $(MAKE) -C test + +clean: + $(MAKE) -C src clean + $(MAKE) -C test clean \ No newline at end of file diff --git a/Makefile.conf b/Makefile.conf new file mode 100644 index 0000000..a39d12e --- /dev/null +++ b/Makefile.conf @@ -0,0 +1,80 @@ +LIBNAME:=heck_log +LIB:=lib$(LIBNAME).a + +C_LANG_VERSION=c99 +CXX_LANG_VERSION=c++17 + +# Build option defaults +PREFIX?=$(HOME)/local +SYS_PREFIX?=/opt/local +DEBUG?=1 + +COMPILE_FLAGS:= -MMD + +CFLAGS:=-std=$(C_LANG_VERSION) +CFLAGS+=\ + -finput-charset=UTF-8 \ + -Wpedantic \ + -Wextra \ + -Wwrite-strings \ + -Wconversion \ + -Wshadow \ +# -Wstrict-prototypes \ +# -Werror=implicit-function-declaration \ + -Werror=implicit-int \ + -Werror=incompatible-pointer-types \ + -Werror=int-conversion \ + -Wno-missing-field-initializers \ + +CXXFLAGS:=-std=$(CXX_LANG_VERSION) +CXXFLAGS+=\ + -fPIC \ + -Wall \ + -Wextra \ + -pedantic + +ifneq (,$(findstring g++,$(CXX))) + COMPILE_FLAGS+=-fdiagnostics-color=always +else ifneq (,$(findstring clang,$(CXX))) + COMPILE_FLAGS+=-fcolor-diagnostics +endif + +LIBS:=-lstdc++ +#-lpEpCxx11 + +LDFLAGS+=$(LIBS) + +######### Overrides from build.conf ######### +HERE:=$(dir $(lastword $(MAKEFILE_LIST))) +-include $(HERE)build.conf + +COMPILE_FLAGS+=-isystem$(SYS_PREFIX)/include +COMPILE_FLAGS+=-I$(PREFIX)/include +LDFLAGS+=-L$(SYS_PREFIX)/lib +LDFLAGS+=-L$(PREFIX)/lib + +ifeq ($(DEBUG),1) + COMPILE_FLAGS+=-g -O0 -DDEBUG +# -fsanitize=address \ +# -fsanitize=undefined \ +# -fsanitize=float-divide-by-zero \ +# -fsanitize=implicit-conversion \ +# -fsanitize=unsigned-integer-overflow +else + COMPILE_FLAGS+=-DNDEBUG -O2 -g0 +endif + +ifeq ($(PORTMIDI_ENABLED),1) + COMPILE_FLAGS+= -DFEAT_PORTMIDI + LDFLAGS+= -lportmidi +endif + +ifeq ($(MOUSE_ENABLED),0) + COMPILE_FLAGS+=-DFEAT_NOMOUSE +endif + +CXXFLAGS+=$(COMPILE_FLAGS) +CFLAGS+=$(COMPILE_FLAGS) + +$(info C-Compiler: $(shell $(CC) --version)) +$(info C++-Compiler: $(shell $(CXX) --version)) \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..4bbb3d0 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# HeckLog + +a little logging lib for mixed C and C++ projects, supporting linkage for both. diff --git a/build.conf.example b/build.conf.example new file mode 100644 index 0000000..592a445 --- /dev/null +++ b/build.conf.example @@ -0,0 +1,5 @@ +# The following values reflect the defaults +# uncomment and adjust + +# DEBUG?=1 +# PREFIX=/opt/local diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..bc5fce5 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,32 @@ +include ../Makefile.conf + +SRC:=$(wildcard *.c*) +SRC_EXE:=$(filter main_%, $(SRC)) +SRC_LIB=$(filter-out main_%, $(SRC)) +OBJS:=$(addsuffix .o, $(basename $(SRC))) +OBJS_LIB:=$(addsuffix .o, $(basename $(SRC_LIB))) +EXE:=$(basename $(SRC_EXE)) +DEPS:=$(addsuffix .d, $(basename $(SRC))) + +.PHONY: all install uninstall clean +.DEFAULT_GOAL:= all + +ifneq ($(MAKECMDGOALS),clean) + -include $(DEPS) +endif + +all: $(EXE) + +$(LIB): $(OBJS) + $(AR) -rc $@ $(OBJS_LIB) + +$(EXE) : $(LIB) + +clean: + rm -rf \ + $(OBJS) \ + $(EXE) \ + $(LIB) \ + $(DEPS) \ + *.dSYM \ + *.h.gch \ No newline at end of file diff --git a/src/log.cc b/src/log.cc new file mode 100644 index 0000000..ea84ae0 --- /dev/null +++ b/src/log.cc @@ -0,0 +1,465 @@ +#include "log.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------------------------- +// Internal functions Prototypes +// Global State +//------------------------------------------------------------------------------------------------- +#ifdef __cplusplus +extern "C" { +#endif + const char* _heck_log_level_to_string(const HECK_LOG_LEVEL* level); + bool _heck_logfile_open(void); + void _heck_logfile_close(void); +#ifdef __cplusplus +} +#endif + +namespace Heck { + namespace Utils { + std::atomic termsize_x = 120; + std::string _to_termcol(const HECK_LOG_COLOR& col); + std::string _decorate_three_lines(std::string_view msg, char decoration = '-'); + std::string _decorate_centered(std::string_view msg, char decoration = '-'); + } // namespace Utils + namespace Log { + std::atomic _log_level{ HECK_LOG_LEVEL_NONE }; + std::atomic _log_color{ HECK_LOG_COLOR_WHITE }; + namespace Backend { + std::mutex _mtx_backends{}; + HECK_LOG_BACKEND + _backends = (HECK_LOG_BACKEND)(HECK_LOG_BACKEND_STDOUT | HECK_LOG_BACKEND_FILE); + void _dispatcher(std::string_view msg, HECK_LOG_COLOR col = HECK_LOG_COLOR_DEFAULT); + namespace Console { + std::mutex _mtx_stdout{}; + void _write_stdout(std::string_view msg, HECK_LOG_COLOR col = HECK_LOG_COLOR_DEFAULT); + std::mutex _mtx_stderr{}; + void _write_stderr(std::string_view msg, HECK_LOG_COLOR col = HECK_LOG_COLOR_DEFAULT); + } // namespace Console + namespace Logfile { + std::recursive_mutex _mtx_path{}; + std::filesystem::path _path{ "heck.log" }; + std::recursive_mutex _mtx_stream{}; + std::ofstream _stream{}; + std::atomic _clear_on_open{ true }; + void _write(std::string_view msg); + } // namespace Logfile + } // namespace Backend + } // namespace Log +} // namespace Heck + +//------------------------------------------------------------------------------------------------- +// C linkage functions +//------------------------------------------------------------------------------------------------- +#ifdef __cplusplus +extern "C" { +#endif + void heck_log_level_set(HECK_LOG_LEVEL level) + { + Heck::Log::_log_level = level; + } + + HECK_LOG_LEVEL heck_log_level_get(void) + { + return Heck::Log::_log_level; + } + + void heck_log_backends_set(HECK_LOG_BACKEND backend) + { + std::lock_guard l{ Heck::Log::Backend::_mtx_backends }; + Heck::Log::Backend::_backends = backend; + } + + HECK_LOG_BACKEND heck_log_backend_get(void) + { + std::lock_guard l{ Heck::Log::Backend::_mtx_backends }; + return Heck::Log::Backend::_backends; + } + + void heck_log_logfile_path_set(const char* path) + { + std::lock_guard l{ Heck::Log::Backend::Logfile::_mtx_path }; + std::lock_guard l2{ Heck::Log::Backend::Logfile::_mtx_stream }; + Heck::Log::Backend::Logfile::_path = path; + if (Heck::Log::Backend::Logfile::_stream.is_open()) { + Heck::Log::Backend::Logfile::_stream.close(); + } + } + + void heck_log_logfile_path_get(const char** path) + { + std::lock_guard l{ Heck::Log::Backend::Logfile::_mtx_path }; + *path = Heck::Log::Backend::Logfile::_path.c_str(); + } + + void heck_log_color_set(HECK_LOG_COLOR col) + { + if (col == HECK_LOG_COLOR_DEFAULT) { + col = HECK_LOG_COLOR_RESET; + } + Heck::Log::_log_color = col; + } + + HECK_LOG_COLOR heck_log_color_get(void) + { + return Heck::Log::_log_color; + } + + void heck_log_logfile_clear_on_open_set(bool clear) + { + Heck::Log::Backend::Logfile::_clear_on_open = clear; + } + + bool heck_log_logfile_clear_on_open_get(void) + { + return Heck::Log::Backend::Logfile::_clear_on_open; + } + + void heck_log(const char* msg) + { + Heck::Log::Backend::_dispatcher(msg, Heck::Log::_log_color); + } + + void heck_log_h1(const char* msg) + { + Heck::Log::Backend::_dispatcher( + Heck::Utils::_decorate_three_lines(msg, '='), + Heck::Log::_log_color); + } + + void heck_log_h2(const char* msg) + { + Heck::Log::Backend::_dispatcher( + "\n" + Heck::Utils::_decorate_centered(msg, '='), + Heck::Log::_log_color); + } + + void heck_log_h3(const char* msg) + { + Heck::Log::Backend::_dispatcher( + Heck::Utils::_decorate_centered(msg, '-'), + Heck::Log::_log_color); + } + + void _heck_log_for_macro( + HECK_LOG_LEVEL level, + const char* file, + int line, + const char* funcname, + const char* fmt, + ...) + { + if (level > Heck::Log::_log_level) { + return; + } + std::string msg{ "heck log subsystem error" }; + va_list var_args; + va_start(var_args, fmt); + + char* msg_fmtd = nullptr; + int size = vasprintf(&msg_fmtd, fmt, var_args); + if (size >= 0) { + std::stringstream header{}; + header << _heck_log_level_to_string(&level) << " "; + header << getpid() << " "; + header << std::this_thread::get_id() << " "; + header << file << ":"; + header << line << " "; + header << funcname << " "; + msg = header.str(); + std::string_view msg_fmtd_cxx{ msg_fmtd }; + if (!msg_fmtd_cxx.empty()) { + msg += "- "; + msg += msg_fmtd_cxx; + } + } + free(msg_fmtd); + Heck::Log::Backend::_dispatcher(msg); + va_end(var_args); + } + + const char* _heck_log_level_to_string(const HECK_LOG_LEVEL* level) + { + switch (*level) { + case HECK_LOG_LEVEL_NONE: + return "NONE"; + case HECK_LOG_LEVEL_ERROR: + return "ERROR"; + case HECK_LOG_LEVEL_WARN: + return "WARN"; + case HECK_LOG_LEVEL_INFO: + return "INFO"; + case HECK_LOG_LEVEL_ALL: + return "ALL"; + default: + return "invalid log level"; + } + } + + bool _heck_logfile_open(void) + { + std::lock_guard l{ Heck::Log::Backend::Logfile::_mtx_stream }; + std::lock_guard l2{ Heck::Log::Backend::Logfile::_mtx_path }; + + if (Heck::Log::Backend::Logfile::_stream.is_open()) { + return true; + } + + // Append or clear + unsigned int openmode = std::ios::app; + if (Heck::Log::Backend::Logfile::_clear_on_open) { + openmode = std::ios::out | std::ios::trunc; + } + + Heck::Log::Backend::Logfile::_stream.open(Heck::Log::Backend::Logfile::_path, openmode); + if (Heck::Log::Backend::Logfile::_stream.fail()) { + // TODO: Where to log error + std::cerr << "LOGFILE ERROR" << std::endl; + Heck::Log::Backend::Logfile::_stream.clear(); + return false; + } + return true; + } + + void _heck_logfile_close(void) + { + std::lock_guard l{ Heck::Log::Backend::Logfile::_mtx_stream }; + Heck::Log::Backend::Logfile::_stream.close(); + } + +#ifdef __cplusplus +} +#endif + +//------------------------------------------------------------------------------------------------- +// C++ Linkage functions +//------------------------------------------------------------------------------------------------- + +namespace Heck { + namespace Log { + // for C++ some functions are just wrappers for the + // C linkage functions + // to be nicely available in the namespace + // C linkage functions are mutexed and therefore + // wrapped here even if otherwise trivial + + void set_level(HECK_LOG_LEVEL level) + { + ::heck_log_level_set(level); + } + + HECK_LOG_LEVEL get_level() + { + return ::heck_log_level_get(); + } + + void set_color(HECK_LOG_COLOR col) + { + ::heck_log_color_set(col); + } + + HECK_LOG_COLOR get_color() + { + return ::heck_log_color_get(); + } + + void _log_for_macro( + HECK_LOG_LEVEL level, + const std::string& file, + int line, + const std::string& funcname, + const std::string& msg) + { + ::_heck_log_for_macro(level, file.c_str(), line, funcname.c_str(), "%s", msg.c_str()); + } + + void log(std::string_view msg, HECK_LOG_COLOR col) + { + Backend::_dispatcher(msg, col); + } + + void logH1(std::string_view msg, HECK_LOG_COLOR col) + { + Backend::_dispatcher(Utils::_decorate_three_lines(msg, '='), col); + } + + void logH2(std::string_view msg, HECK_LOG_COLOR col) + { + Backend::_dispatcher("\n" + Utils::_decorate_centered(msg, '='), col); + } + + void logH3(std::string_view msg, HECK_LOG_COLOR col) + { + Backend::_dispatcher(Utils::_decorate_centered(msg, '-'), col); + } + + namespace Backend { + void set_backends(HECK_LOG_BACKEND backend) + { + ::heck_log_backends_set(backend); + } + + HECK_LOG_BACKEND get_backends() + { + return ::heck_log_backend_get(); + } + + void _dispatcher(std::string_view msg, HECK_LOG_COLOR col) + { + std::lock_guard l{ Heck::Log::Backend::_mtx_backends }; + if (_backends & HECK_LOG_BACKEND_STDOUT) { + Console::_write_stdout(msg, col); + } + if (_backends & HECK_LOG_BACKEND_STDERR) { + Console::_write_stderr(msg, col); + } + if (_backends & HECK_LOG_BACKEND_FILE) { + Logfile::_write(msg); + } + } + + namespace Console { + void _write_stdout(std::string_view msg, HECK_LOG_COLOR col) + { + std::lock_guard l{ _mtx_stdout }; + std::cerr << Utils::_to_termcol(col) << msg + << Utils::_to_termcol(HECK_LOG_COLOR_RESET) + << std::endl; //endl also flushes, but cerr is unbuffered anyways + } + void _write_stderr(std::string_view msg, HECK_LOG_COLOR col) + { + std::lock_guard l{ _mtx_stderr }; + std::cerr << Utils::_to_termcol(col) << msg + << Utils::_to_termcol(HECK_LOG_COLOR_RESET) + << std::endl; //endl also flushes, but cerr is unbuffered anyways + } + } // namespace Console + + namespace Logfile { + void _write(std::string_view msg) + { + std::lock_guard l{ _mtx_stream }; + if (!_heck_logfile_open()) { + std::cerr << "ERROR: Logfile open failed" << std::endl; + } else { + _stream << msg << std::endl; + if (_stream.fail()) { + std::cerr << "ERROR: Logfile Write failed" << std::endl; + _stream.clear(); + } + } + } + + void set_path(const std::filesystem::path& path) + { + ::heck_log_logfile_path_set(path.c_str()); + } + + const std::filesystem::path& get_path() + { + std::lock_guard l{ Heck::Log::Backend::Logfile::_mtx_path }; + return Heck::Log::Backend::Logfile::_path; + } + + void set_clear_on_open(bool clear) + { + ::heck_log_logfile_clear_on_open_set(clear); + } + + bool get_clear_on_open() + { + return ::heck_log_logfile_clear_on_open_get(); + } + + } // namespace Logfile + } // namespace Backend + } // namespace Log + + + namespace Utils { + std::string _to_termcol(const HECK_LOG_COLOR& col) + { + switch (col) { + case HECK_LOG_COLOR_DEFAULT: + // Caution: Make sure Heck::Log::_log_color can + // NEVER be set to HECK_LOG_COLOR_DEFAULT + return _to_termcol(Heck::Log::_log_color); + case HECK_LOG_COLOR_RESET: + return "\033[0m"; + case HECK_LOG_COLOR_BLACK: + return "\033[30m"; + case HECK_LOG_COLOR_RED: + return "\033[31m"; + case HECK_LOG_COLOR_GREEN: + return "\033[32m"; + case HECK_LOG_COLOR_YELLOW: + return "\033[33m"; + case HECK_LOG_COLOR_BLUE: + return "\033[34m"; + case HECK_LOG_COLOR_MAGENTA: + return "\033[35m"; + case HECK_LOG_COLOR_CYAN: + return "\033[36m"; + case HECK_LOG_COLOR_WHITE: + return "\033[37m"; + default: + return _to_termcol(HECK_LOG_COLOR_RESET); + } + } + + std::string _decorate_three_lines(std::string_view msg, char decoration) + { + std::stringstream tmp; + tmp << std::string(termsize_x, decoration) << std::endl + << msg << std::endl + << std::string(termsize_x, decoration); + return tmp.str(); + } + + std::string _decorate_centered(std::string_view msg, char decoration) + { + std::stringstream tmp; + size_t max_len = termsize_x - 10; + // truncate msg + std::string msg_truncated{ msg }; + if (msg.length() >= max_len) { + msg_truncated = msg.substr(0, max_len); + msg_truncated += "..."; + } + + // define decolen + int decolen = static_cast(floor((double(termsize_x - msg_truncated.length()))) / 2.0); + + tmp << std::string(decolen, decoration) << ' ' << msg_truncated << ' ' + << std::string(decolen, decoration); + return tmp.str(); + } + } // namespace Utils +} // namespace Heck + +//------------------------------------------------------------------------------------------------- +// C++ Linkage global namespace members +//------------------------------------------------------------------------------------------------- +std::ostream& operator<<(std::ostream& o, const HECK_LOG_LEVEL* level) +{ + return o << std::string_view(_heck_log_level_to_string(level)); +} + +// Stuff like that would be fucking handy +// TODO: make a lib with operator<< for ALL libstc++ types +template std::ostream& operator<<(std::ostream& o, std::vector& vec) +{ + std::stringstream ss{}; + for (const T& elem : vec) { + o << elem; + } + return o; +} diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..7713372 --- /dev/null +++ b/src/log.h @@ -0,0 +1,148 @@ +#ifndef _HECK_VALOG_H +#define _HECK_VALOG_H + +#define HECK_LOG_FUNCTION_ENTRY 1 + +#ifdef HECK_LOG_HAVE_PRETTY_FUNCTION + #define HECK_LOG_MACRO_FUNCNAME __PRETTY_FUNCTION__ +#else + #define HECK_LOG_MACRO_FUNCNAME __func__ +#endif + +typedef enum { + HECK_LOG_BACKEND_STDOUT = 1, + HECK_LOG_BACKEND_STDERR = 2, + HECK_LOG_BACKEND_FILE = 4 +} HECK_LOG_BACKEND; + +typedef enum { + HECK_LOG_LEVEL_NONE = 0, // Logging Disabled + HECK_LOG_LEVEL_ERROR = 100, + HECK_LOG_LEVEL_WARN = 200, + HECK_LOG_LEVEL_INFO = 300, + HECK_LOG_LEVEL_ALL = 1000000 // Convenience +} HECK_LOG_LEVEL; + +typedef enum { + HECK_LOG_COLOR_DEFAULT, + HECK_LOG_COLOR_RESET, + HECK_LOG_COLOR_BLACK, + HECK_LOG_COLOR_RED, + HECK_LOG_COLOR_GREEN, + HECK_LOG_COLOR_YELLOW, + HECK_LOG_COLOR_BLUE, + HECK_LOG_COLOR_MAGENTA, + HECK_LOG_COLOR_CYAN, + HECK_LOG_COLOR_WHITE, +} HECK_LOG_COLOR; + + +//--------------------------------------------------------------------------------------- +// C99 +//--------------------------------------------------------------------------------------- +#ifndef __cplusplus + #include + + #define HECK_LOG_ERR(...) HECK_LOG(HECK_LOG_LEVEL_ERROR, __VA_ARGS__) + + #define HECK_LOG_WARN(...) HECK_LOG(HECK_LOG_LEVEL_WARN, __VA_ARGS__) + + #define HECK_LOG_INFO(...) HECK_LOG(HECK_LOG_LEVEL_INFO, __VA_ARGS__) + + #define HECK_LOG(level, ...) \ + do { \ + _heck_log_for_macro(level, __FILE__, __LINE__, HECK_LOG_MACRO_FUNCNAME, "" __VA_ARGS__); \ + } while (0) + +// config/settings +void heck_log_level_set(HECK_LOG_LEVEL level); +HECK_LOG_LEVEL heck_log_level_get(void); + +void heck_log_backends_set(HECK_LOG_BACKEND backend); +HECK_LOG_BACKEND heck_log_backend_get(void); + +void heck_log_logfile_path_set(const char* path); +void heck_log_logfile_path_get(const char** path); + +void heck_log_color_set(HECK_LOG_COLOR col); +HECK_LOG_COLOR heck_log_color_get(void); + +void heck_log_logfile_clear_on_open_set(bool clear); +bool heck_log_logfile_clear_on_open_get(void); + +// logging +void heck_log(const char* msg); +void heck_log_h1(const char* msg); +void heck_log_h2(const char* msg); +void heck_log_h3(const char* msg); + +// helper function for macro use only +void _heck_log_for_macro( + HECK_LOG_LEVEL level, + const char* file, + int line, + const char* funcname, + const char* fmt, + ...); + +#endif // NOT __cplusplus + +//--------------------------------------------------------------------------------------- +// C++ +//--------------------------------------------------------------------------------------- +#ifdef __cplusplus + #include + #include + #include + + #define HECK_LOG_ERR(msg) HECK_LOG(HECK_LOG_LEVEL_ERROR, msg) + + #define HECK_LOG_WARN(msg) HECK_LOG(HECK_LOG_LEVEL_WARN, msg) + + #define HECK_LOG_INFO(msg) HECK_LOG(HECK_LOG_LEVEL_INFO, msg) + + #define HECK_LOG(level, msg) \ + do { \ + std::stringstream ss{}; \ + ss << msg; \ + ::Heck::Log::_log_for_macro(level, __FILE__, __LINE__, HECK_LOG_MACRO_FUNCNAME, ss.str()); \ + } while (0) + +namespace Heck { + namespace Log { + // config/settings + void set_level(HECK_LOG_LEVEL level); + HECK_LOG_LEVEL get_level(); + void set_color(HECK_LOG_COLOR col); + HECK_LOG_COLOR get_color(); + + namespace Backend { + void set_backends(HECK_LOG_BACKEND backend); + HECK_LOG_BACKEND get_backends(); + namespace Logfile { + void set_path(const std::filesystem::path& path); + const std::filesystem::path& get_path(); + void set_clear_on_open(bool clear); + bool get_clear_on_open(); + } // namespace Logfile + } // namespace Backend + + // logging + void log(std::string_view msg, HECK_LOG_COLOR col = HECK_LOG_COLOR_DEFAULT); + void logH1(std::string_view msg, HECK_LOG_COLOR col = HECK_LOG_COLOR_DEFAULT); + void logH2(std::string_view msg, HECK_LOG_COLOR col = HECK_LOG_COLOR_DEFAULT); + void logH3(std::string_view msg, HECK_LOG_COLOR col = HECK_LOG_COLOR_DEFAULT); + + // helper function for macro use only + void _log_for_macro( + HECK_LOG_LEVEL level, + const std::string& file, + int line, + const std::string& funcname, + const std::string& msg); + } // namespace Log +} // namespace Heck + +std::ostream& operator<<(std::ostream& o, const HECK_LOG_LEVEL* level); +#endif //__cplusplus +#endif diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..090fcc0 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,31 @@ +include ../Makefile.conf + +SRC:=$(wildcard *.c*) +SRC_EXE:=$(filter test_%, $(SRC)) +EXE:=$(basename $(SRC_EXE)) + +$(info src exe: $(SRC_EXE)) +$(info lib: $(LIB)) +$(info exe: $(EXE)) + +LDFLAGS+=-L../src +CFLAGS+=-I../src +CXXFLAGS+=-I../src + +.PHONY: lib all clean ../src/$(LIB) +.DEFAULT_GOAL := all + +all: $(EXE) + +../src/$(LIB): + $(MAKE) -C ../src $(LIB) + +$(EXE): ../src/$(LIB) + +clean: + rm -rf \ + $(OBJS) \ + $(EXE) \ + $(DEPS) \ + *.dSYM \ + *.h.gch \ No newline at end of file diff --git a/test/test_log.c b/test/test_log.c new file mode 100644 index 0000000..e904582 --- /dev/null +++ b/test/test_log.c @@ -0,0 +1,95 @@ +//#include +#include +#include +#include +#include "../src/log.h" +#include + +// HECK LOG C usage + +int main(void) +{ + // Test Levels + { + HECK_LOG_ERR("SHOULD NOT SEE THIS"); + + heck_log_level_set(HECK_LOG_LEVEL_ALL); + HECK_LOG_ERR(); + HECK_LOG_WARN(); + HECK_LOG_INFO(); + + heck_log_level_set(HECK_LOG_LEVEL_ERROR); + HECK_LOG_ERR(); + HECK_LOG_WARN("SHOULD NOT SEE THIS"); + HECK_LOG_INFO("SHOULD NOT SEE THIS"); + } + + // Logfile + { + const char *path = NULL; + heck_log_logfile_path_get(&path); + printf("LOGFILE PATH: %s\n", path); + heck_log_logfile_path_set("newlogfile.log"); + } + + // BASIC USAGE MACRO + { + heck_log_level_set(HECK_LOG_LEVEL_INFO); + // no message + HECK_LOG_INFO(); + + // simple string literal + HECK_LOG_INFO("HELLO"); + + // sprintf style formatting + char *str2 = strdup("best"); + HECK_LOG_INFO("The %s number is %i", str2, 23); + free(str2); + + // does not compile, 1st must be literal fmt string + // HECK_LOG_WARN(str1); + } + + // BASIC USAGE FUNC + { + heck_log_level_set(HECK_LOG_LEVEL_INFO); + // literal + heck_log("Literal RAW"); + + // c-string + char *str2 = strdup("c-string RAW"); + heck_log(str2); + free(str2); + + heck_log_h1("HEADING 1"); + heck_log_h2("HEADING 2"); + heck_log_h3("HEADING 3"); + } + + // Colors functions only + { + heck_log_h1("Colors functions only"); + heck_log("DEFAULT COLOR"); + heck_log_color_set(HECK_LOG_COLOR_GREEN); + heck_log("DEFAULT COLOR AFTER SET GREEN"); + heck_log_color_set(HECK_LOG_COLOR_YELLOW); + strdup("YELLOW ONE OFF"); + heck_log(strdup("YELLOW ONE OFF")); + heck_log_color_set(HECK_LOG_COLOR_RESET); + heck_log("COLOR RESET"); + } + + // Colors using Macros + { + heck_log_h1("Colors using Macros"); + heck_log("DEFAULT COLOR"); + heck_log_color_set(HECK_LOG_COLOR_GREEN); + HECK_LOG_ERR("DEFAULT COLOR AFTER SET GREEN"); + heck_log_color_set(HECK_LOG_COLOR_YELLOW); + HECK_LOG_ERR("YELLOW ONE OFF"); + heck_log_color_set(HECK_LOG_COLOR_RESET); + HECK_LOG_ERR("COLOR RESET"); + } + + heck_log("ALL TEST SUCCESSFUL"); +} diff --git a/test/test_log_cxx.cc b/test/test_log_cxx.cc new file mode 100644 index 0000000..bd5008d --- /dev/null +++ b/test/test_log_cxx.cc @@ -0,0 +1,99 @@ +//#include +//#include +#include +#include +#include "../src/log.h" +#include +#include + +// HECK LOG C++ usage + +int main() +{ + // Test Levels + { + HECK_LOG_ERR("SHOULD NOT SEE THIS"); + + Heck::Log::set_level(HECK_LOG_LEVEL_ALL); + HECK_LOG_ERR(""); + HECK_LOG_WARN(""); + HECK_LOG_INFO(""); + + Heck::Log::set_level(HECK_LOG_LEVEL_ERROR); + HECK_LOG_ERR(""); + HECK_LOG_WARN("SHOULD NOT SEE THIS"); + HECK_LOG_INFO("SHOULD NOT SEE THIS"); + } + + // Logfile + { + std::cout << " LOGFILE PATH: " << Heck::Log::Backend::Logfile::get_path(); + Heck::Log::Backend::Logfile::set_path("newlogfile.log"); + } + + // BASIC USAGE MACRO + { + Heck::Log::set_level(HECK_LOG_LEVEL_INFO); + // no message + HECK_LOG_INFO(""); + + // literals + HECK_LOG_INFO("literal"); + + // string concats + std::string str{ "casts" }; + HECK_LOG_ERR("everything " + str + " to a string"); + + // operator<< overloads + int i{ 23 }; + HECK_LOG_ERR(i); + } + + // BASIC USAGE FUNCTIONS + { + Heck::Log::set_level(HECK_LOG_LEVEL_INFO); + // no message + Heck::Log::log(""); + + // literals + Heck::Log::log("literal"); + + // string concats + std::string str{ "casts" }; + Heck::Log::log("everything " + str + " to a string"); + + // operator<< overloads + // WONT compile, needs std::string + // int i{ 23 }; + // Heck::Log::log(i); + + Heck::Log::logH1("HEADING 1"); + Heck::Log::logH2("HEADING 2"); + Heck::Log::logH3("HEADING 3"); + } + + // Colors functions only + { + Heck::Log::logH1("Colors functions only"); + Heck::Log::log("BLUE LOG", HECK_LOG_COLOR_BLUE); + Heck::Log::log("DEFAULT COLOR"); + Heck::Log::set_color(HECK_LOG_COLOR_GREEN); + Heck::Log::log("DEFAULT COLOR AFTER SET GREEN"); + Heck::Log::log("YELLOW ONE OFF", HECK_LOG_COLOR_YELLOW); + Heck::Log::log("DEFAULT COLOR AGAIN"); + } + + // Colors using Macros + { + Heck::Log::set_color(HECK_LOG_COLOR_DEFAULT); + Heck::Log::logH1("Colors using Macros"); + HECK_LOG_ERR("DEFAULT COLOR"); + Heck::Log::log("BLUE LOG", HECK_LOG_COLOR_BLUE); + HECK_LOG_ERR("DEFAULT COLOR AGAIN"); + Heck::Log::set_color(HECK_LOG_COLOR_GREEN); + Heck::Log::log("YELLOW ONE OFF", HECK_LOG_COLOR_YELLOW); + HECK_LOG_ERR("DEFAULT COLOR GREEN"); + } + + Heck::Log::log("ALL TEST SUCCESSFUL"); +}