diff --git a/bindings/libdnf5/logger.i b/bindings/libdnf5/logger.i index 6983db3f6..9a2d51f75 100644 --- a/bindings/libdnf5/logger.i +++ b/bindings/libdnf5/logger.i @@ -19,7 +19,9 @@ typedef int32_t pid_t; %{ #include "libdnf/common/weak_ptr.hpp" #include "libdnf/logger/log_router.hpp" + #include "libdnf/logger/global_logger.hpp" #include "libdnf/logger/memory_buffer_logger.hpp" + #include "libdnf/logger/factory.hpp" %} #define CV __perl_CV @@ -44,4 +46,6 @@ wrap_unique_ptr(MemoryBufferLoggerUniquePtr, libdnf::MemoryBufferLogger); } %include "libdnf/logger/log_router.hpp" +%include "libdnf/logger/global_logger.hpp" %include "libdnf/logger/memory_buffer_logger.hpp" +%include "libdnf/logger/factory.hpp" diff --git a/dnf5/main.cpp b/dnf5/main.cpp index a5e6a6b77..9fde4517f 100644 --- a/dnf5/main.cpp +++ b/dnf5/main.cpp @@ -54,20 +54,17 @@ along with libdnf. If not, see . #include #include #include +#include #include #include -#include #include #include #include #include #include -#include #include -namespace fs = std::filesystem; - namespace dnf5 { using namespace libdnf::cli; @@ -648,21 +645,11 @@ int main(int argc, char * argv[]) try { close(fd); } - auto log_dir_full_path = fs::path(base.get_config().get_installroot_option().get_value()); - log_dir_full_path += base.get_config().get_logdir_option().get_value(); - std::filesystem::create_directories(log_dir_full_path); - auto log_file = log_dir_full_path / "dnf5.log"; - auto log_stream = std::make_unique(log_file, std::ios::app); - if (!log_stream->is_open()) { - throw std::runtime_error(fmt::format("Cannot open log file: {}: {}", log_file.c_str(), strerror(errno))); - } - // Throw exceptions if there is an error while writing to the log stream - log_stream->exceptions(std::ios::badbit | std::ios::failbit); - std::unique_ptr logger = std::make_unique(std::move(log_stream)); + auto file_logger = libdnf::create_file_logger(base); // Swap to destination stream logger (log to file) - log_router.swap_logger(logger, 0); + log_router.swap_logger(file_logger, 0); // Write messages from memory buffer logger to stream logger - dynamic_cast(*logger).write_to_logger(log_router); + dynamic_cast(*file_logger).write_to_logger(log_router); base.setup(); diff --git a/include/libdnf/logger/factory.hpp b/include/libdnf/logger/factory.hpp new file mode 100644 index 000000000..e85186f0d --- /dev/null +++ b/include/libdnf/logger/factory.hpp @@ -0,0 +1,40 @@ +/* +Copyright Contributors to the libdnf project. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2.1 of the License, or +(at your option) any later version. + +Libdnf is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF_LOGGER_FACTORY_HPP +#define LIBDNF_LOGGER_FACTORY_HPP + +#include "logger.hpp" + +#include "libdnf/base/base.hpp" + + +namespace libdnf { + +// File logger destination filename. +constexpr const char * FILE_LOGGER_FILENAME = "dnf5.log"; + +/// @brief Helper method for creating a file logger. +/// @param base Reference to Base for loading the configured logger path. +/// @return Instance of a new file logger. +std::unique_ptr create_file_logger(libdnf::Base & base); + +} // namespace libdnf + +#endif diff --git a/libdnf/logger/factory.cpp b/libdnf/logger/factory.cpp new file mode 100644 index 000000000..64f52c6e0 --- /dev/null +++ b/libdnf/logger/factory.cpp @@ -0,0 +1,51 @@ +/* +Copyright Contributors to the libdnf project. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2.1 of the License, or +(at your option) any later version. + +Libdnf is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#include "libdnf/logger/factory.hpp" + +#include "libdnf/logger/stream_logger.hpp" + +#include +#include + + +namespace libdnf { + +using namespace std::filesystem; + +std::unique_ptr create_file_logger(Base & base) { + auto & config = base.get_config(); + auto & installroot = config.get_installroot_option().get_value(); + auto & logdir = config.get_logdir_option().get_value(); + auto logdir_full_path = path(installroot) / path(logdir).relative_path(); + + create_directories(logdir_full_path); + auto log_file = logdir_full_path / FILE_LOGGER_FILENAME; + auto log_stream = std::make_unique(log_file, std::ios::app); + if (!log_stream->is_open()) { + throw std::runtime_error(fmt::format("Cannot open log file: {}: {}", log_file.c_str(), strerror(errno))); + } + + // Throw exceptions if there is an error while writing to the log stream + log_stream->exceptions(std::ios::badbit | std::ios::failbit); + + return std::make_unique(std::move(log_stream)); +} + +} // namespace libdnf diff --git a/test/libdnf/logger/test_file_logger.cpp b/test/libdnf/logger/test_file_logger.cpp new file mode 100644 index 000000000..1192366fb --- /dev/null +++ b/test/libdnf/logger/test_file_logger.cpp @@ -0,0 +1,63 @@ +/* +Copyright Contributors to the libdnf project. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2.1 of the License, or +(at your option) any later version. + +Libdnf is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + + +#include "test_file_logger.hpp" + +#include "libdnf/logger/factory.hpp" + + +using namespace std::filesystem; + + +CPPUNIT_TEST_SUITE_REGISTRATION(FileLoggerTest); + + +void FileLoggerTest::setUp() { + BaseTestCase::setUp(); + + auto & config = base.get_config(); + auto & installroot = config.get_installroot_option().get_value(); + auto & temp_logdir = "/var/log/FileLoggerTestLogDir"; + config.get_logdir_option().set(temp_logdir); + + full_log_path = path(installroot) / path(temp_logdir).relative_path() / libdnf::FILE_LOGGER_FILENAME; +} + + +void FileLoggerTest::tearDown() { + BaseTestCase::tearDown(); + remove_all(full_log_path.parent_path()); +} + + +void FileLoggerTest::test_file_logger_create() { + CPPUNIT_ASSERT(!exists(full_log_path)); + auto file_logger = libdnf::create_file_logger(base); + CPPUNIT_ASSERT(exists(full_log_path)); +} + + +void FileLoggerTest::test_file_logger_add() { + auto log_router = base.get_logger(); + auto loggers_count_before = log_router->get_loggers_count(); + auto file_logger = libdnf::create_file_logger(base); + log_router->add_logger(std::move(file_logger)); + CPPUNIT_ASSERT_EQUAL(loggers_count_before + 1, log_router->get_loggers_count()); +} diff --git a/test/libdnf/logger/test_file_logger.hpp b/test/libdnf/logger/test_file_logger.hpp new file mode 100644 index 000000000..4c391d4aa --- /dev/null +++ b/test/libdnf/logger/test_file_logger.hpp @@ -0,0 +1,46 @@ +/* +Copyright Contributors to the libdnf project. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Libdnf is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with libdnf. If not, see . +*/ + + +#ifndef LIBDNF_TEST_FILE_LOGGER_HPP +#define LIBDNF_TEST_FILE_LOGGER_HPP + +#include "base_test_case.hpp" + +#include +#include + + +class FileLoggerTest : public BaseTestCase { + CPPUNIT_TEST_SUITE(FileLoggerTest); + CPPUNIT_TEST(test_file_logger_create); + CPPUNIT_TEST(test_file_logger_add); + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp() override; + void tearDown() override; + void test_file_logger_create(); + void test_file_logger_add(); + +private: + std::filesystem::path full_log_path; +}; + +#endif diff --git a/test/python3/libdnf5/logger/test_file_logger.py b/test/python3/libdnf5/logger/test_file_logger.py new file mode 100644 index 000000000..3d86305dc --- /dev/null +++ b/test/python3/libdnf5/logger/test_file_logger.py @@ -0,0 +1,60 @@ +# Copyright Contributors to the libdnf project. +# +# This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ +# +# Libdnf is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# Libdnf is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with libdnf. If not, see . + +import libdnf5 + +import base_test_case + +import os +import shutil + + +class TestFileLogger(base_test_case.BaseTestCase): + def setUp(self): + super().setUp() + config = self.base.get_config() + config.logdir = "FileLoggerTestLogDir" + self.full_log_path = os.path.join(config.installroot, config.logdir, libdnf5.logger.FILE_LOGGER_FILENAME) + + def tearDown(self): + super().tearDown() + shutil.rmtree(os.path.dirname(self.full_log_path), ignore_errors=True) + + def test_file_logger_create(self): + self.assertFalse(os.path.exists(self.full_log_path)) + _ = libdnf5.logger.create_file_logger(self.base) + self.assertTrue(os.path.exists(self.full_log_path)) + + def test_file_logger_add(self): + log_router = self.base.get_logger() + loggers_count_before = log_router.get_loggers_count() + logger = libdnf5.logger.create_file_logger(self.base) + log_router.add_logger(logger) + self.assertEqual(loggers_count_before + 1, log_router.get_loggers_count()) + + def test_with_global_logger(self): + log_router = self.base.get_logger() + global_logger = libdnf5.logger.GlobalLogger() + global_logger.set(log_router.get(), libdnf5.logger.Logger.Level_DEBUG) + logger = libdnf5.logger.create_file_logger(self.base) + log_router.add_logger(logger) + + # Run an action including librepo logging + self.add_repo_repomd("repomd-repo1") + + with open(self.full_log_path) as logfile: + self.assertTrue('[librepo]' in logfile.read())