diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml new file mode 100644 index 0000000..576d3e5 --- /dev/null +++ b/.github/workflows/ubuntu.yml @@ -0,0 +1,16 @@ +name: Ubuntu-amd64 + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: cmake build + run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release + - name: cmake make + run: cmake --build build --parallel 3 + - name: test + run: ./build/aixlog_example diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 0000000..68e27dc --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,16 @@ +name: Windows-win64 + +on: [push, pull_request] + +jobs: + build: + runs-on: windows-latest + + steps: + - uses: actions/checkout@v2 + - name: cmake build + run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release + - name: cmake make + run: cmake --build build --parallel 3 + - name: test + run: build\Debug\aixlog_example.exe diff --git a/.gitignore b/.gitignore index 9fa21f4..4f32526 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ build #test binary aixlog_example +all.log #vscode .vscode diff --git a/CMakeLists.txt b/CMakeLists.txt index 3fe0cb1..e9baa87 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,14 +4,14 @@ # \_/\_/(__)(_/\_)\____/ \__/ \___/ # This file is part of aixlog -# Copyright (C) 2017-2020 Johannes Pohl +# Copyright (C) 2017-2021 Johannes Pohl # This software may be modified and distributed under the terms # of the MIT license. See the LICENSE file for details. cmake_minimum_required(VERSION 3.0.0) -project(aixlog VERSION 1.4.0 LANGUAGES CXX) +project(aixlog VERSION 1.5.0 LANGUAGES CXX) set(PROJECT_DESCRIPTION "Header-only C++ logging library") set(PROJECT_URL "https://github.com/badaix/aixlog") @@ -40,3 +40,21 @@ endif (BUILD_EXAMPLE) install(FILES include/aixlog.hpp DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") + +FIND_PROGRAM(CLANG_FORMAT "clang-format") +IF(CLANG_FORMAT) + set(CHECK_CXX_SOURCE_FILES + ${CMAKE_SOURCE_DIR}/include/aixlog.hpp + ${CMAKE_SOURCE_DIR}/aixlog_example.cpp + ) + + ADD_CUSTOM_TARGET( + reformat + COMMAND + ${CLANG_FORMAT} + -i + -style=file + ${CHECK_CXX_SOURCE_FILES} + COMMENT "Auto formatting of all source files" + ) +ENDIF() diff --git a/LICENSE b/LICENSE index 192e296..20d49a4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017-2020 Johannes Pohl +Copyright (c) 2017-2021 Johannes Pohl Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/aixlog_example.cpp b/aixlog_example.cpp index 12baf86..20a7540 100644 --- a/aixlog_example.cpp +++ b/aixlog_example.cpp @@ -5,7 +5,7 @@ \_/\_/(__)(_/\_)\____/ \__/ \___/ This file is part of aixlog - Copyright (C) 2017-2020 Johannes Pohl + Copyright (C) 2017-2021 Johannes Pohl This software may be modified and distributed under the terms of the MIT license. See the LICENSE file for details. @@ -17,6 +17,33 @@ using namespace std; +/// Log Conditional to log only every x-th message +struct EveryXConditional : public AixLog::Conditional +{ + /// c'tor + /// @param every_x log only every_x-th line + EveryXConditional(size_t every_x) : every_x_(every_x), x_th_(0) + { + } + + /// check if this is the x-th log message + /// @return true if this is the x-th log message + bool is_true() const override + { + if (++x_th_ == every_x_) + { + x_th_ = 0; + return true; + } + return false; + } + +private: + size_t every_x_; + mutable size_t x_th_; +}; + + int main(int /*argc*/, char** /*argv*/) { AixLog::Log::init(AixLog::Severity::trace); @@ -31,9 +58,9 @@ int main(int /*argc*/, char** /*argv*/) filter.add_filter("LOG_TAG:DEBUG"); auto sink_cout = make_shared(filter); AixLog::Filter filter_syslog; - // log lines with tag "SYSLOG" to syslog - filter_syslog.add_filter("SYSLOG:TRACE"); - auto sink_syslog = make_shared("aixlog example", filter_syslog); + // log lines with tag "SYSLOG" to syslog + filter_syslog.add_filter("SYSLOG:TRACE"); + auto sink_syslog = make_shared("aixlog example", filter_syslog); AixLog::Log::init({sink_cout, sink_syslog}); @@ -59,6 +86,10 @@ int main(int /*argc*/, char** /*argv*/) << "\n\tfile: " << metadata.function.file << "\n"; })}); +#ifdef WIN32 + AixLog::Log::instance().add_logsink(AixLog::Severity::trace); +#endif + /// Log with info severity LOG(INFO) << "LOG(INFO)\n"; /// ... with a tag @@ -87,8 +118,30 @@ int main(int /*argc*/, char** /*argv*/) LOG(FATAL) << "LOG(FATAL) " << COLOR(red) << "red" << COLOR(none) << ", default color (using macros)\n"; LOG(FATAL) << "LOG(FATAL) " << AixLog::TextColor(AixLog::Color::yellow, AixLog::Color::blue) << "yellow on blue background" << AixLog::Color::none << ", default color\n"; +#ifndef WIN32 LOG(FATAL) << "LOG(FATAL) " << COLOR(yellow, blue) << "yellow on blue background" << COLOR(none) << ", default color (using macros)\n"; +#endif AixLog::Severity severity(AixLog::Severity::debug); LOG(severity) << "LOG(severity) << severity\n"; + + EveryXConditional every_x(3); + LOG(INFO) << every_x << "1st will not be logged\n"; + LOG(INFO) << every_x << "2nd will not be logged\n"; + LOG(INFO) << every_x << "3rd will be logged\n"; + LOG(INFO) << every_x << "4th will not be logged\n"; + LOG(INFO) << every_x << "5th will not be logged\n"; + LOG(INFO) << every_x << "6th will be logged\n"; + + AixLog::Conditional not_every_3(AixLog::Conditional::EvalFunc([] { + static size_t n(0); + return (++n % 3 != 0); + })); + + LOG(INFO) << not_every_3 << "1st will be logged\n"; + LOG(INFO) << not_every_3 << "2nd will be logged\n"; + LOG(INFO) << not_every_3 << "3rd will not be logged\n"; + LOG(INFO) << not_every_3 << "4th will be logged\n"; + LOG(INFO) << not_every_3 << "5th will be logged\n"; + LOG(INFO) << not_every_3 << "6th will not be logged\n"; } diff --git a/include/aixlog.hpp b/include/aixlog.hpp index 491f33c..3d0d30c 100644 --- a/include/aixlog.hpp +++ b/include/aixlog.hpp @@ -3,11 +3,11 @@ / _\ ( )( \/ )( ) / \ / __) / \ )( ) ( / (_/\( O )( (_ \ \_/\_/(__)(_/\_)\____/ \__/ \___/ - version 1.4.0 + version 1.5.0 https://github.com/badaix/aixlog This file is part of aixlog - Copyright (C) 2017-2020 Johannes Pohl + Copyright (C) 2017-2021 Johannes Pohl This software may be modified and distributed under the terms of the MIT license. See the LICENSE file for details. @@ -178,7 +178,7 @@ enum class Severity : std::int8_t static Severity to_severity(std::string severity, Severity def = Severity::info) { - std::transform(severity.begin(), severity.end(), severity.begin(), [](unsigned char c) { return std::tolower(c); }); + std::transform(severity.begin(), severity.end(), severity.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); if (severity == "trace") return Severity::trace; else if (severity == "debug") @@ -269,26 +269,29 @@ struct TextColor */ struct Conditional { - Conditional() : Conditional(true) + using EvalFunc = std::function; + + Conditional() : func_([](void) { return true; }) { } - Conditional(bool value) : is_true_(value) + Conditional(const EvalFunc& func) : func_(func) { } - void set(bool value) + Conditional(bool value) : func_([value](void) { return value; }) { - is_true_ = value; } - bool is_true() const + virtual ~Conditional() = default; + + virtual bool is_true() const { - return is_true_; + return func_(); } -private: - bool is_true_; +protected: + EvalFunc func_; }; /** @@ -601,7 +604,7 @@ class Log : public std::basic_streambuf> } protected: - Log() noexcept : last_buffer_(nullptr) + Log() noexcept : last_buffer_(nullptr), do_log_(true) { std::clog.rdbuf(this); std::clog << Severity() << Tag() << Function() << Conditional() << AixLog::Color::NONE << std::flush; @@ -617,7 +620,7 @@ class Log : public std::basic_streambuf> std::lock_guard lock(mutex_); if (!get_stream().str().empty()) { - if (conditional_.is_true()) + if (do_log_) { for (const auto& sink : log_sinks_) { @@ -639,7 +642,7 @@ class Log : public std::basic_streambuf> { if (c == '\n') sync(); - else + else if (do_log_) get_stream() << static_cast(c); } else @@ -667,11 +670,14 @@ class Log : public std::basic_streambuf> return *last_buffer_; } + /// one buffer per thread to avoid mixed log lines std::map buffer_; + /// the last thread id std::thread::id last_id_; + /// the last buffer std::stringstream* last_buffer_ = nullptr; Metadata metadata_; - Conditional conditional_; + bool do_log_; std::vector log_sinks_; std::recursive_mutex mutex_; }; @@ -843,8 +849,12 @@ struct SinkOutputDebugString : public Sink void log(const Metadata& metadata, const std::string& message) override { +#ifdef UNICODE std::wstring wide = std::wstring(message.begin(), message.end()); OutputDebugString(wide.c_str()); +#else + OutputDebugString(message.c_str()); +#endif } }; #endif @@ -1004,8 +1014,12 @@ struct SinkEventLog : public Sink { SinkEventLog(const std::string& ident, const Filter& filter) : Sink(filter) { +#ifdef UNICODE std::wstring wide = std::wstring(ident.begin(), ident.end()); // stijnvdb: RegisterEventSource expands to RegisterEventSourceW which takes wchar_t event_log = RegisterEventSource(NULL, wide.c_str()); +#else + event_log = RegisterEventSource(NULL, ident.c_str()); +#endif } WORD get_type(Severity severity) const @@ -1031,11 +1045,15 @@ struct SinkEventLog : public Sink void log(const Metadata& metadata, const std::string& message) override { +#ifdef UNICODE std::wstring wide = std::wstring(message.begin(), message.end()); // We need this temp variable because we cannot take address of rValue - const wchar_t* c_str = wide.c_str(); - + const auto* c_str = wide.c_str(); ReportEvent(event_log, get_type(metadata.severity), 0, 0, NULL, 1, 0, &c_str, NULL); +#else + const auto* c_str = message.c_str(); + ReportEvent(event_log, get_type(metadata.severity), 0, 0, NULL, 1, 0, &c_str, NULL); +#endif } protected: @@ -1131,7 +1149,7 @@ static std::ostream& operator<<(std::ostream& os, const Severity& log_severity) log->metadata_.timestamp = nullptr; log->metadata_.tag = nullptr; log->metadata_.function = nullptr; - log->conditional_.set(true); + log->do_log_ = true; } } else @@ -1192,7 +1210,7 @@ static std::ostream& operator<<(std::ostream& os, const Conditional& conditional if (log != nullptr) { std::lock_guard lock(log->mutex_); - log->conditional_.set(conditional.is_true()); + log->do_log_ = conditional.is_true(); } return os; }