From 73d5dea86fb7b3bfa913df9cefde1b732f42569b Mon Sep 17 00:00:00 2001 From: Dimo Markov Date: Wed, 17 Jul 2024 15:44:27 +0300 Subject: [PATCH] Prefixes for all message styles; Plain-text logger redirector/duplicator; Combining colors in different ways for less redundant pushing/popping; Bug fix --- CMakeLists.txt | 1 + source/HTML.cpp | 11 ++- source/Logger.cpp | 75 +++++++++++------ source/Logger.hpp | 201 +++++++++++++++++++++++++++++++++++++++----- source/Logger.inl | 95 +++++++++++---------- source/TXT.cpp | 74 ++++++++++++++++ test/Main.cpp | 8 +- test/TestLogger.cpp | 2 +- 8 files changed, 374 insertions(+), 93 deletions(-) create mode 100644 source/TXT.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8587654..21dfe2d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ fetch_external_module( add_library(LangulusLogger ${LANGULUS_LIBRARY_TYPE} source/Logger.cpp source/HTML.cpp + source/TXT.cpp ) target_compile_definitions(LangulusLogger diff --git a/source/HTML.cpp b/source/HTML.cpp index 9b3fcd7..e0e2053 100644 --- a/source/HTML.cpp +++ b/source/HTML.cpp @@ -21,6 +21,7 @@ ToHTML::ToHTML(const TextView& filename) : mFilename {filename} { } ToHTML::~ToHTML() { + WriteFooter(); mFile.close(); } @@ -33,7 +34,7 @@ void ToHTML::Write(const TextView& text) const noexcept { /// Apply some style /// @param style - the style to set -void ToHTML::Write(const Style& style) const noexcept { +void ToHTML::Write(Style style) const noexcept { // Always reset before a style change Write(" \n"); @@ -179,7 +180,6 @@ void ToHTML::Write(const Style& style) const noexcept { /// Remove formatting, add a new line, add a timestamp and tabulate /// @attention top of the style stack is not applied -/// @param timestamp - whether to insert a timestamp void ToHTML::NewLine() const noexcept { Write("
"); Write(Instance.TimeStampStyle); @@ -211,3 +211,10 @@ void ToHTML::WriteHeader() const { Write(GetAdvancedTime()); Write("\n"); } + +/// Write file footer - just the official shutdown timestamp +void ToHTML::WriteFooter() const { + Write("

Log ended - "); + Write(GetAdvancedTime()); + Write("

"); +} diff --git a/source/Logger.cpp b/source/Logger.cpp index fae169e..0d2c500 100644 --- a/source/Logger.cpp +++ b/source/Logger.cpp @@ -128,7 +128,7 @@ void Interface::Write(const TextView& stdString) const noexcept { /// Change the style /// @param s - the style -void Interface::Write(const Style& s) const noexcept { +void Interface::Write(Style s) const noexcept { // Dispatch to redirectors if (not mRedirectors.empty()) { for (auto attachment : mRedirectors) @@ -200,7 +200,7 @@ void Interface::Clear() const noexcept { /// Execute a logger command /// @param c - the command to execute -void Interface::RunCommand(const Command& c) noexcept { +void Interface::RunCommand(Command c) noexcept { switch (c) { case Command::Clear: Clear(); @@ -232,6 +232,11 @@ void Interface::RunCommand(const Command& c) noexcept { case Command::Push: mStyleStack.push(mStyleStack.top()); break; + case Command::PopAndPush: + if (mStyleStack.size() > 1) + mStyleStack.pop(); + mStyleStack.push(mStyleStack.top()); + break; case Command::Stylize: Write(mStyleStack.top()); break; @@ -246,9 +251,31 @@ void Interface::RunCommand(const Command& c) noexcept { } /// Change the foreground/background color -/// @param c - the color +/// @param c_with_flags - the color with optional mixing flags /// @return the last style, with coloring applied -const Style& Interface::SetColor(const Color& c) noexcept { +const Style& Interface::SetColor(Color c_with_flags) noexcept { + if (static_cast(c_with_flags) + & static_cast(Color::PreviousColor)) { + // We have to pop + if (mStyleStack.size() > 1) + mStyleStack.pop(); + } + + if (static_cast(c_with_flags) + & static_cast(Color::NextColor)) { + // We have to push + mStyleStack.push(mStyleStack.top()); + } + + // Strip the mixing bits from the color + const Color c = static_cast( + static_cast(c_with_flags) & (~( + static_cast(Color::PreviousColor) + | static_cast(Color::NextColor) + )) + ); + + // Mix... auto& style = mStyleStack.top(); const auto oldStyle = style; if (c == Color::NoForeground) { @@ -284,7 +311,7 @@ const Style& Interface::SetColor(const Color& c) noexcept { /// Change the emphasis /// @param c - the color -const Style& Interface::SetEmphasis(const Emphasis& e) noexcept { +const Style& Interface::SetEmphasis(Emphasis e) noexcept { auto& style = mStyleStack.top(); style |= static_cast(e); return style; @@ -292,7 +319,7 @@ const Style& Interface::SetEmphasis(const Emphasis& e) noexcept { /// Change the style /// @param s - the style -const Style& Interface::SetStyle(const Style& s) noexcept { +const Style& Interface::SetStyle(Style s) noexcept { mStyleStack.top() = s; return mStyleStack.top(); } @@ -337,7 +364,7 @@ Logger::A::Interface& Logger::A::Interface::operator << (Logger::A::Interface&) /// Push a command /// @param c - the command to push /// @return a reference to the logger for chaining -Logger::A::Interface& Logger::A::Interface::operator << (const Command& c) noexcept { +Logger::A::Interface& Logger::A::Interface::operator << (Command c) noexcept { Instance.RunCommand(c); return *this; } @@ -345,7 +372,7 @@ Logger::A::Interface& Logger::A::Interface::operator << (const Command& c) noexc /// Push a foreground color /// @param c - the command to push /// @return a reference to the logger for chaining -Logger::A::Interface& Logger::A::Interface::operator << (const Color& c) noexcept { +Logger::A::Interface& Logger::A::Interface::operator << (Color c) noexcept { Instance.SetColor(c); Instance.RunCommand(Command::Stylize); return *this; @@ -354,7 +381,7 @@ Logger::A::Interface& Logger::A::Interface::operator << (const Color& c) noexcep /// Push an emphasis /// @param e - the emphasis to push /// @return a reference to the logger for chaining -Logger::A::Interface& Logger::A::Interface::operator << (const Emphasis& e) noexcept { +Logger::A::Interface& Logger::A::Interface::operator << (Emphasis e) noexcept { Instance.SetEmphasis(e); Instance.RunCommand(Command::Stylize); return *this; @@ -363,25 +390,12 @@ Logger::A::Interface& Logger::A::Interface::operator << (const Emphasis& e) noex /// Push a foreground and background color /// @param c - the state to push /// @return a reference to the logger for chaining -Logger::A::Interface& Logger::A::Interface::operator << (const Style& c) noexcept { +Logger::A::Interface& Logger::A::Interface::operator << (Style c) noexcept { Instance.SetStyle(c); Instance.RunCommand(Command::Stylize); return *this; } -/// Push a number of tabs -/// @param t - the tabs to push -/// @return a reference to the logger for chaining -Logger::A::Interface& Logger::A::Interface::operator << (const Tabs& t) noexcept { - auto tabs = ::std::max(1, t.mTabs); - while (tabs) { - Instance.RunCommand(Command::Tab); - --tabs; - } - - return *this; -} - /// Write string views /// @param t - text to write /// @return a reference to the logger for chaining @@ -392,11 +406,24 @@ Logger::A::Interface& Logger::A::Interface::operator << (const TextView& t) noex /// Write a nullptr as "null" /// @return a reference to the logger for chaining -Logger::A::Interface& Logger::A::Interface::operator << (const ::std::nullptr_t&) noexcept { +Logger::A::Interface& Logger::A::Interface::operator << (::std::nullptr_t) noexcept { Instance.Write("null"); return *this; } +/// Push a number of tabs +/// @param t - the tabs to push +/// @return a reference to the logger for chaining +Logger::A::Interface& Logger::A::Interface::operator << (const Tabs& t) noexcept { + auto tabs = ::std::max(1, t.mTabs); + while (tabs) { + Instance.RunCommand(Command::Tab); + --tabs; + } + + return *this; +} + /// Push a number of tabs /// Keeps track of the number of tabs that have been pushed, and then /// automatically untabs when the Tabs object is destroyed diff --git a/source/Logger.hpp b/source/Logger.hpp index 86da3be..564f59b 100644 --- a/source/Logger.hpp +++ b/source/Logger.hpp @@ -39,7 +39,7 @@ namespace Langulus::Logger /// Color codes, consistent with ANSI/VT100 escapes /// Also consistent with fmt::terminal_color - enum class Color : ::std::underlying_type_t { + enum class Color : unsigned { NoForeground = 0, NoBackground = 1, @@ -77,7 +77,138 @@ namespace Langulus::Logger BlueBgr, PurpleBgr, CyanBgr, - WhiteBgr + WhiteBgr, + + // Bits that dictate how to mix the colors + // Color is always mixed with the currently set one, unless the + // 'PreviousColor' bit is on, in which case the style is popped + // before applying color + PreviousColor = 128, + + // Color is always mixed with the currently set one, unless the + // 'NextColor' bit is on, in which case the style is pushed + // before applying color + NextColor = 128, + + // Colors that mix with the previous color + PopNoForeground = NoForeground | PreviousColor, + PopNoBackground = NoBackground | PreviousColor, + + PopBlack = Black | PreviousColor, + PopDarkRed = DarkRed | PreviousColor, + PopDarkGreen = DarkGreen | PreviousColor, + PopDarkYellow = DarkYellow | PreviousColor, + PopDarkBlue = DarkBlue | PreviousColor, + PopDarkPurple = DarkPurple | PreviousColor, + PopDarkCyan = DarkCyan | PreviousColor, + PopGray = Gray | PreviousColor, + + PopBlackBgr = BlackBgr | PreviousColor, + PopDarkRedBgr = DarkRedBgr | PreviousColor, + PopDarkGreenBgr = DarkGreenBgr | PreviousColor, + PopDarkYellowBgr = DarkYellowBgr | PreviousColor, + PopDarkBlueBgr = DarkBlueBgr | PreviousColor, + PopDarkPurpleBgr = DarkPurpleBgr | PreviousColor, + PopDarkCyanBgr = DarkCyanBgr | PreviousColor, + PopGrayBgr = GrayBgr | PreviousColor, + + PopDarkGray = DarkGray | PreviousColor, + PopRed = Red | PreviousColor, + PopGreen = Green | PreviousColor, + PopYellow = Yellow | PreviousColor, + PopBlue = Blue | PreviousColor, + PopPurple = Purple | PreviousColor, + PopCyan = Cyan | PreviousColor, + PopWhite = White | PreviousColor, + + PopDarkGrayBgr = DarkGrayBgr | PreviousColor, + PopRedBgr = RedBgr | PreviousColor, + PopGreenBgr = GreenBgr | PreviousColor, + PopYellowBgr = YellowBgr | PreviousColor, + PopBlueBgr = BlueBgr | PreviousColor, + PopPurpleBgr = PurpleBgr | PreviousColor, + PopCyanBgr = CyanBgr | PreviousColor, + PopWhiteBgr = WhiteBgr | PreviousColor, + + // Colors that mix with the next color + PushNoForeground = NoForeground | NextColor, + PushNoBackground = NoBackground | NextColor, + + PushBlack = Black | NextColor, + PushDarkRed = DarkRed | NextColor, + PushDarkGreen = DarkGreen | NextColor, + PushDarkYellow = DarkYellow | NextColor, + PushDarkBlue = DarkBlue | NextColor, + PushDarkPurple = DarkPurple | NextColor, + PushDarkCyan = DarkCyan | NextColor, + PushGray = Gray | NextColor, + + PushBlackBgr = BlackBgr | NextColor, + PushDarkRedBgr = DarkRedBgr | NextColor, + PushDarkGreenBgr = DarkGreenBgr | NextColor, + PushDarkYellowBgr = DarkYellowBgr | NextColor, + PushDarkBlueBgr = DarkBlueBgr | NextColor, + PushDarkPurpleBgr = DarkPurpleBgr | NextColor, + PushDarkCyanBgr = DarkCyanBgr | NextColor, + PushGrayBgr = GrayBgr | NextColor, + + PushDarkGray = DarkGray | NextColor, + PushRed = Red | NextColor, + PushGreen = Green | NextColor, + PushYellow = Yellow | NextColor, + PushBlue = Blue | NextColor, + PushPurple = Purple | NextColor, + PushCyan = Cyan | NextColor, + PushWhite = White | NextColor, + + PushDarkGrayBgr = DarkGrayBgr | NextColor, + PushRedBgr = RedBgr | NextColor, + PushGreenBgr = GreenBgr | NextColor, + PushYellowBgr = YellowBgr | NextColor, + PushBlueBgr = BlueBgr | NextColor, + PushPurpleBgr = PurpleBgr | NextColor, + PushCyanBgr = CyanBgr | NextColor, + PushWhiteBgr = WhiteBgr | NextColor, + + // Colors that reset to previous color, push and mix + PopAndPushNoForeground = NoForeground | NextColor | PreviousColor, + PopAndPushNoBackground = NoBackground | NextColor | PreviousColor, + + PopAndPushBlack = Black | NextColor | PreviousColor, + PopAndPushDarkRed = DarkRed | NextColor | PreviousColor, + PopAndPushDarkGreen = DarkGreen | NextColor | PreviousColor, + PopAndPushDarkYellow = DarkYellow | NextColor | PreviousColor, + PopAndPushDarkBlue = DarkBlue | NextColor | PreviousColor, + PopAndPushDarkPurple = DarkPurple | NextColor | PreviousColor, + PopAndPushDarkCyan = DarkCyan | NextColor | PreviousColor, + PopAndPushGray = Gray | NextColor | PreviousColor, + + PopAndPushBlackBgr = BlackBgr | NextColor | PreviousColor, + PopAndPushDarkRedBgr = DarkRedBgr | NextColor | PreviousColor, + PopAndPushDarkGreenBgr = DarkGreenBgr | NextColor | PreviousColor, + PopAndPushDarkYellowBgr = DarkYellowBgr | NextColor | PreviousColor, + PopAndPushDarkBlueBgr = DarkBlueBgr | NextColor | PreviousColor, + PopAndPushDarkPurpleBgr = DarkPurpleBgr | NextColor | PreviousColor, + PopAndPushDarkCyanBgr = DarkCyanBgr | NextColor | PreviousColor, + PopAndPushGrayBgr = GrayBgr | NextColor | PreviousColor, + + PopAndPushDarkGray = DarkGray | NextColor | PreviousColor, + PopAndPushRed = Red | NextColor | PreviousColor, + PopAndPushGreen = Green | NextColor | PreviousColor, + PopAndPushYellow = Yellow | NextColor | PreviousColor, + PopAndPushBlue = Blue | NextColor | PreviousColor, + PopAndPushPurple = Purple | NextColor | PreviousColor, + PopAndPushCyan = Cyan | NextColor | PreviousColor, + PopAndPushWhite = White | NextColor | PreviousColor, + + PopAndPushDarkGrayBgr = DarkGrayBgr | NextColor | PreviousColor, + PopAndPushRedBgr = RedBgr | NextColor | PreviousColor, + PopAndPushGreenBgr = GreenBgr | NextColor | PreviousColor, + PopAndPushYellowBgr = YellowBgr | NextColor | PreviousColor, + PopAndPushBlueBgr = BlueBgr | NextColor | PreviousColor, + PopAndPushPurpleBgr = PurpleBgr | NextColor | PreviousColor, + PopAndPushCyanBgr = CyanBgr | NextColor | PreviousColor, + PopAndPushWhiteBgr = WhiteBgr | NextColor | PreviousColor }; /// GCC equates templates with enum types as their underlying type, so we @@ -113,8 +244,9 @@ namespace Langulus::Logger enum class Command : uint8_t { Clear, // Clear the console NewLine, // Write a new line, with a timestamp and tabulation - Pop, // Pop the style - Push, // Push the style + Pop, // Pop the style, and apply previous style + Push, // Push the current style + PopAndPush, // Pop the style and push another, don't stylize yet Invert, // Inverts background and foreground colors Reset, // Reset the style Stylize, // Apply the last style @@ -169,7 +301,7 @@ namespace Langulus::Logger static Text GetSimpleTime() noexcept; virtual void Write(const TextView&) const noexcept = 0; - virtual void Write(const Style&) const noexcept = 0; + virtual void Write(Style) const noexcept = 0; virtual void NewLine() const noexcept = 0; virtual void Clear() const noexcept = 0; @@ -182,12 +314,13 @@ namespace Langulus::Logger LANGULUS_API(LOGGER) Interface& operator << (A::Interface&) noexcept; LANGULUS_API(LOGGER) Interface& operator << (const TextView&) noexcept; - LANGULUS_API(LOGGER) Interface& operator << (const Command&) noexcept; - LANGULUS_API(LOGGER) Interface& operator << (const Color&) noexcept; - LANGULUS_API(LOGGER) Interface& operator << (const Emphasis&) noexcept; - LANGULUS_API(LOGGER) Interface& operator << (const Style&) noexcept; + LANGULUS_API(LOGGER) Interface& operator << (Command) noexcept; + LANGULUS_API(LOGGER) Interface& operator << (Color) noexcept; + LANGULUS_API(LOGGER) Interface& operator << (Emphasis) noexcept; + LANGULUS_API(LOGGER) Interface& operator << (Style) noexcept; + LANGULUS_API(LOGGER) Interface& operator << (::std::nullptr_t) noexcept; + LANGULUS_API(LOGGER) Interface& operator << (const Tabs&) noexcept; - LANGULUS_API(LOGGER) Interface& operator << (const ::std::nullptr_t&) noexcept; LANGULUS_API(LOGGER) ScopedTabs operator << (Tabs&&) noexcept; Interface& operator << (const CT::Sparse auto&) noexcept; @@ -231,17 +364,17 @@ namespace Langulus::Logger /// Interface override /// LANGULUS_API(LOGGER) void Write(const TextView&) const noexcept; - LANGULUS_API(LOGGER) void Write(const Style&) const noexcept; + LANGULUS_API(LOGGER) void Write(Style) const noexcept; LANGULUS_API(LOGGER) void NewLine() const noexcept; LANGULUS_API(LOGGER) void Clear() const noexcept; /// /// State changers /// - LANGULUS_API(LOGGER) void RunCommand(const Command&) noexcept; - LANGULUS_API(LOGGER) const Style& SetStyle(const Style&) noexcept; - LANGULUS_API(LOGGER) const Style& SetColor(const Color&) noexcept; - LANGULUS_API(LOGGER) const Style& SetEmphasis(const Emphasis&) noexcept; + LANGULUS_API(LOGGER) void RunCommand(Command) noexcept; + LANGULUS_API(LOGGER) const Style& SetStyle(Style) noexcept; + LANGULUS_API(LOGGER) const Style& SetColor(Color) noexcept; + LANGULUS_API(LOGGER) const Style& SetEmphasis(Emphasis) noexcept; /// /// Attachments @@ -349,7 +482,7 @@ namespace Langulus::Logger /// struct MessageSink final : Logger::A::Interface { void Write(const TextView&) const noexcept {} - void Write(const Style&) const noexcept {} + void Write(Style) const noexcept {} void NewLine() const noexcept {} void Clear() const noexcept {} }; @@ -357,9 +490,9 @@ namespace Langulus::Logger LANGULUS_API(LOGGER) extern MessageSink MessageSinkInstance; /// - /// Generates HTML code from logging messages and append them to an - /// output file. Can be used both as duplicator or redirector. - /// Use it like this: + /// Generates HTML code from logging messages. Can be used both as + /// duplicator or redirector. Colors and styles are consistent with + /// console output. Use it like this: /// Logger::ToHTML logRedirect("outputfile.htm"); /// Logger::AttachRedirector(&logRedirect); /// @@ -372,13 +505,41 @@ namespace Langulus::Logger mutable std::ofstream mFile; void WriteHeader() const; + void WriteFooter() const; public: LANGULUS_API(LOGGER) ToHTML(const TextView&); LANGULUS_API(LOGGER) ~ToHTML(); LANGULUS_API(LOGGER) void Write(const TextView&) const noexcept; - LANGULUS_API(LOGGER) void Write(const Style&) const noexcept; + LANGULUS_API(LOGGER) void Write(Style) const noexcept; + LANGULUS_API(LOGGER) void NewLine() const noexcept; + LANGULUS_API(LOGGER) void Clear() const noexcept; + }; + + /// + /// Generates plain text file from logging messages. Can be used both as + /// duplicator or redirector. Strips and and all styling. Use it like this: + /// Logger::ToTXT logRedirect("outputfile.htm"); + /// Logger::AttachRedirector(&logRedirect); + /// + /// Logger::DettachRedirector(&logRedirect); + /// + /// + struct ToTXT final : Logger::A::Interface { + private: + std::string mFilename; + mutable std::ofstream mFile; + + void WriteHeader() const; + void WriteFooter() const; + + public: + LANGULUS_API(LOGGER) ToTXT(const TextView&); + LANGULUS_API(LOGGER) ~ToTXT(); + + LANGULUS_API(LOGGER) void Write(const TextView&) const noexcept; + LANGULUS_API(LOGGER) void Write(Style) const noexcept; LANGULUS_API(LOGGER) void NewLine() const noexcept; LANGULUS_API(LOGGER) void Clear() const noexcept; }; diff --git a/source/Logger.inl b/source/Logger.inl index 6cb55c3..6fcb7a0 100644 --- a/source/Logger.inl +++ b/source/Logger.inl @@ -1,4 +1,4 @@ -/// +/// /// Langulus::Logger /// Copyright (c) 2012 Dimo Markov /// Part of the Langulus framework, see https://langulus.com @@ -128,10 +128,8 @@ namespace Langulus::Logger decltype(auto) Section(T&&...arguments) noexcept { if constexpr (sizeof...(arguments) > 0) { Instance.NewLine(); - Instance << Command::Push - << Color::White - << Emphasis::Bold - << Emphasis::Underline; + Instance << Interface::TabStyle << "┌─ " + << Color::PushWhite << Emphasis::Bold << Emphasis::Underline; (Instance << ... << ::std::forward(arguments)); return (Instance << Command::Pop << Tabs {}); } @@ -145,7 +143,7 @@ namespace Langulus::Logger decltype(auto) Fatal([[maybe_unused]] T&&...arguments) noexcept { #ifdef LANGULUS_LOGGER_ENABLE_FATALERRORS Instance.NewLine(); - Instance << Command::Pop << Command::Push << Color::DarkRed; + Instance << Command::PopAndPush << Color::DarkRed; if constexpr (sizeof...(arguments) > 0) { Instance << "FATAL ERROR: "; @@ -173,7 +171,7 @@ namespace Langulus::Logger decltype(auto) Error([[maybe_unused]] T&&...arguments) noexcept { #ifdef LANGULUS_LOGGER_ENABLE_ERRORS Instance.NewLine(); - Instance << Command::Pop << Command::Push << Color::Red; + Instance << Command::PopAndPush << Color::Red; if constexpr (sizeof...(arguments) > 0) { Instance << "ERROR: "; @@ -201,7 +199,7 @@ namespace Langulus::Logger decltype(auto) Warning([[maybe_unused]] T&&...arguments) noexcept { #ifdef LANGULUS_LOGGER_ENABLE_WARNINGS Instance.NewLine(); - Instance << Command::Pop << Command::Push << Color::DarkYellow; + Instance << Command::PopAndPush << Color::DarkYellow; if constexpr (sizeof...(arguments) > 0) { Instance << "WARNING: "; @@ -229,12 +227,13 @@ namespace Langulus::Logger decltype(auto) Verbose([[maybe_unused]] T&&...arguments) noexcept { #ifdef LANGULUS_LOGGER_ENABLE_VERBOSE Instance.NewLine(); - Instance << Command::Pop << Command::Push << Color::DarkGray; + Instance << Command::PopAndPush << Color::DarkGray; - if constexpr (sizeof...(arguments) > 0) + if constexpr (sizeof...(arguments) > 0) { + Instance << "VERBOSE: "; return (Instance << ... << ::std::forward(arguments)); - else - return (Instance); + } + else return (Instance); #else return (Instance); #endif @@ -256,12 +255,13 @@ namespace Langulus::Logger decltype(auto) Info([[maybe_unused]] T&&...arguments) noexcept { #ifdef LANGULUS_LOGGER_ENABLE_INFOS Instance.NewLine(); - Instance << Command::Pop << Command::Push << Color::Gray; + Instance << Command::PopAndPush << Color::Gray; - if constexpr (sizeof...(arguments) > 0) + if constexpr (sizeof...(arguments) > 0) { + Instance << "INFO: "; return (Instance << ... << ::std::forward(arguments)); - else - return (Instance); + } + else return (Instance); #else return (Instance); #endif @@ -283,12 +283,13 @@ namespace Langulus::Logger decltype(auto) Message([[maybe_unused]] T&&...arguments) noexcept { #ifdef LANGULUS_LOGGER_ENABLE_MESSAGES Instance.NewLine(); - Instance << Command::Pop << Command::Push << Color::White; + Instance << Command::PopAndPush << Color::White; - if constexpr (sizeof...(arguments) > 0) + if constexpr (sizeof...(arguments) > 0) { + Instance << "MESSAGE: "; return (Instance << ... << ::std::forward(arguments)); - else - return (Instance); + } + else return (Instance); #else return (Instance); #endif @@ -310,12 +311,13 @@ namespace Langulus::Logger decltype(auto) Special([[maybe_unused]] T&&...arguments) noexcept { #ifdef LANGULUS_LOGGER_ENABLE_SPECIALS Instance.NewLine(); - Instance << Command::Pop << Command::Push << Color::Purple; + Instance << Command::PopAndPush << Color::Purple; - if constexpr (sizeof...(arguments) > 0) + if constexpr (sizeof...(arguments) > 0) { + Instance << "SPECIAL: "; return (Instance << ... << ::std::forward(arguments)); - else - return (Instance); + } + else return (Instance); #else return (Instance); #endif @@ -337,12 +339,13 @@ namespace Langulus::Logger decltype(auto) Flow([[maybe_unused]] T&&...arguments) noexcept { #ifdef LANGULUS_LOGGER_ENABLE_FLOWS Instance.NewLine(); - Instance << Command::Pop << Command::Push << Color::DarkCyan; + Instance << Command::PopAndPush << Color::DarkCyan; - if constexpr (sizeof...(arguments) > 0) + if constexpr (sizeof...(arguments) > 0) { + Instance << "FLOW: "; return (Instance << ... << ::std::forward(arguments)); - else - return (Instance); + } + else return (Instance); #else return (Instance); #endif @@ -364,12 +367,13 @@ namespace Langulus::Logger decltype(auto) Input([[maybe_unused]] T&&...arguments) noexcept { #ifdef LANGULUS_LOGGER_ENABLE_INPUTS Instance.NewLine(); - Instance << Command::Pop << Command::Push << Color::Blue; + Instance << Command::PopAndPush << Color::Blue; - if constexpr (sizeof...(arguments) > 0) + if constexpr (sizeof...(arguments) > 0) { + Instance << "INPUT: "; return (Instance << ... << ::std::forward(arguments)); - else - return (Instance); + } + else return (Instance); #else return (Instance); #endif @@ -391,12 +395,13 @@ namespace Langulus::Logger decltype(auto) Network([[maybe_unused]] T&&...arguments) noexcept { #ifdef LANGULUS_LOGGER_ENABLE_NETWORKS Instance.NewLine(); - Instance << Command::Pop << Command::Push << Color::Yellow; + Instance << Command::PopAndPush << Color::Yellow; - if constexpr (sizeof...(arguments) > 0) + if constexpr (sizeof...(arguments) > 0) { + Instance << "NETWORK: "; return (Instance << ... << ::std::forward(arguments)); - else - return (Instance); + } + else return (Instance); #else return (Instance); #endif @@ -418,12 +423,13 @@ namespace Langulus::Logger decltype(auto) OS([[maybe_unused]] T&&...arguments) noexcept { #ifdef LANGULUS_LOGGER_ENABLE_OS Instance.NewLine(); - Instance << Command::Pop << Command::Push << Color::DarkBlue; + Instance << Command::PopAndPush << Color::DarkBlue; - if constexpr (sizeof...(arguments) > 0) + if constexpr (sizeof...(arguments) > 0) { + Instance << "OS: "; return (Instance << ... << ::std::forward(arguments)); - else - return (Instance); + } + else return (Instance); #else return (Instance); #endif @@ -445,12 +451,13 @@ namespace Langulus::Logger decltype(auto) Prompt([[maybe_unused]] T&&...arguments) noexcept { #ifdef LANGULUS_LOGGER_ENABLE_PROMPTS Instance.NewLine(); - Instance << Command::Pop << Command::Push << Color::Green; + Instance << Command::PopAndPush << Color::Green; - if constexpr (sizeof...(arguments) > 0) + if constexpr (sizeof...(arguments) > 0) { + Instance << "PROMPT: "; return (Instance << ... << ::std::forward(arguments)); - else - return (Instance); + } + else return (Instance); #else return (Instance); #endif diff --git a/source/TXT.cpp b/source/TXT.cpp new file mode 100644 index 0000000..70f7dde --- /dev/null +++ b/source/TXT.cpp @@ -0,0 +1,74 @@ +/// +/// Langulus::Logger +/// Copyright (c) 2012 Dimo Markov +/// Part of the Langulus framework, see https://langulus.com +/// +/// SPDX-License-Identifier: MIT +/// +#include "Logger.hpp" + +using namespace Langulus; +using namespace Langulus::Logger; + + +/// Create a plain text file duplicator/redirector +/// @param filename - the relative filename of the log file +ToTXT::ToTXT(const TextView& filename) : mFilename {filename} { + mFile.open(mFilename, std::ios::out | std::ios::trunc); + if (not mFile) + throw std::runtime_error {"Can't open log file"}; + WriteHeader(); +} + +ToTXT::~ToTXT() { + WriteFooter(); + mFile.close(); +} + +/// Write text +/// @param text - the text to append to the file +void ToTXT::Write(const TextView& text) const noexcept { + mFile << text; + mFile.flush(); +} + +/// Plain text logging ignores all styles +/// @param style - the style to set +void ToTXT::Write(Style style) const noexcept { + LANGULUS(NOOP); +} + +/// Remove formatting, add a new line, add a timestamp and tabulate +void ToTXT::NewLine() const noexcept { + Write("\n"); + Write(GetSimpleTime()); + Write("| "); + + auto tabs = Instance.GetTabs(); + if (tabs) { + while (tabs) { + Write(Instance.TabString); + --tabs; + } + } +} + +/// Clear the log file +void ToTXT::Clear() const noexcept { + mFile.close(); + mFile.open(mFilename, std::ios::out | std::ios::trunc); + WriteHeader(); +} + +/// Write file header - just a timestamp +void ToTXT::WriteHeader() const { + Write("Log started - "); + Write(GetAdvancedTime()); + Write("\n\n"); +} + +/// Write file footer - just a timestamp +void ToTXT::WriteFooter() const { + Write("\n\nLog ended - "); + Write(GetAdvancedTime()); +} diff --git a/test/Main.cpp b/test/Main.cpp index 7fc78c7..d885214 100644 --- a/test/Main.cpp +++ b/test/Main.cpp @@ -12,8 +12,12 @@ int main(int argc, char* argv[]) { // Duplicate any logging messages to an external HTML file - Logger::ToHTML logFile {"logfile.htm"}; - Logger::AttachDuplicator(&logFile); + Logger::ToHTML logFile1 {"logfile.htm"}; + Logger::AttachDuplicator(&logFile1); + + // Duplicate any logging messages to an external txt file + Logger::ToTXT logFile2 {"logfile.txt"}; + Logger::AttachDuplicator(&logFile2); Catch::Session session; return session.run(argc, argv); diff --git a/test/TestLogger.cpp b/test/TestLogger.cpp index 4672360..eaf1bf6 100644 --- a/test/TestLogger.cpp +++ b/test/TestLogger.cpp @@ -139,7 +139,7 @@ SCENARIO("Logging to console", "[logger]") { WHEN("Pushing and popping styles") { Logger::Warning("This is a warning, ", Logger::Push, Logger::Underline, "but now we underline it, ", - Logger::Push, Logger::RedBgr, "then we even change color, ", + Logger::PushRedBgr, "then we even change color, ", Logger::Pop, "but then we return to underlined warning, ", Logger::Pop, "and finally, back to warning, ", Logger::Pop, "but if we actually pop once more, we return to default Logger style\n\n");