From 94cf00f9e0011dbf3b2ba632647846713559a13d Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Sat, 21 Jun 2025 07:01:34 -0400 Subject: [PATCH] feat(liblogging): added working build system and other requirments to break out liblogging --- .gitignore | 88 ++++++ build-config/meson.build | 3 + build-config/quill/meson.build | 2 +- meson.build | 38 +++ readme.md | 23 ++ .../probe.h => logging/include/logging.h} | 49 +--- src/logging/lib/logging.cpp | 98 +++++++ src/{probe => logging}/meson.build | 31 +-- src/meson.build | 1 + src/probe/private/probe.cpp | 263 ------------------ subprojects/quill.wrap | 5 + 11 files changed, 274 insertions(+), 327 deletions(-) create mode 100644 .gitignore create mode 100644 build-config/meson.build create mode 100644 meson.build create mode 100644 readme.md rename src/{probe/public/probe.h => logging/include/logging.h} (62%) create mode 100644 src/logging/lib/logging.cpp rename src/{probe => logging}/meson.build (63%) create mode 100644 src/meson.build delete mode 100644 src/probe/private/probe.cpp create mode 100644 subprojects/quill.wrap diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..141f25e --- /dev/null +++ b/.gitignore @@ -0,0 +1,88 @@ +# Python +__pycache__/ +*.py[cod] +*.pyo +*.pyd +*.env +*.venv +env/ +venv/ +ENV/ +ENV.bak/ +*.egg-info/ +dist/ +build/ +*.egg + +# C and C++ (using Meson) +build/ +*.o +*.a +*.so +*.d +*.dSYM/ +*.exe +*.out +*.obj +*.dll +*.lib +*.pdb +*.exp +*.log +*.stackdump + +# Fortran +*.mod +*.o +*.a +*.so +*.exe +*.out + +# Doxygen +html/ +latex/ +xml/ +man/ +rtf/ +tags + +## Misc +*.swp +*._DS_Store +*.DS_Store +*.bak +*.tmp +*.log +*.cache +*.private +*.private/ + +subprojects/mfem/ +subprojects/tetgen/ +subprojects/yaml-cpp/ +subprojects/quill/ +subprojects/googletest-*/ +subprojects/opat-core/ +subprojects/pybind11*/ +subprojects/packagecache/ +subprojects/hypre/ +subprojects/qhull/ +subprojects/libconstants/ + +qhull.wrap + +.vscode/ + +*.log +mpi-install-log.txt + +output/ + +.boost_installed +4DSSE_logs/ +.last_build_flags + +.idea/ + +scratch/ diff --git a/build-config/meson.build b/build-config/meson.build new file mode 100644 index 0000000..f75b694 --- /dev/null +++ b/build-config/meson.build @@ -0,0 +1,3 @@ +cmake = import('cmake') + +subdir('quill') diff --git a/build-config/quill/meson.build b/build-config/quill/meson.build index a069255..9a960bf 100644 --- a/build-config/quill/meson.build +++ b/build-config/quill/meson.build @@ -7,4 +7,4 @@ quill_sp = cmake.subproject( 'quill', options: quill_cmake_options, ) -quill_dep = quill_sp.dependency('quill') \ No newline at end of file +quill_dep = quill_sp.dependency('quill') diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..55ebf7c --- /dev/null +++ b/meson.build @@ -0,0 +1,38 @@ +# *********************************************************************** +# +# Copyright (C) 2025 -- The 4D-STAR Collaboration +# File Author: Emily Boudreaux +# Last Modified: March 19, 2025 +# +# liblogging is free software; you can use it and/or modify +# it under the terms and restrictions the GNU General Library Public +# License version 3 (GPLv3) as published by the Free Software Foundation. +# +# liblogging 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 Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this software; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# *********************************************************************** # +project('liblogging', 'cpp', version: '0.0.1a', default_options: ['cpp_std=c++23'], meson_version: '>=1.5.0') + +# Add default visibility for all C++ targets +add_project_arguments('-fvisibility=default', language: 'cpp') +subdir('build-config') +subdir('src') + +pkg = import('pkgconfig') +pkg.generate( + name: 'liblogging', + description: 'Logging manager for SERiF and related projects', + version: meson.project_version(), + libraries: [liblogging], + subdirs: ['liblogging'], + filebase: 'liblogging', + install_dir: join_paths(get_option('libdir'), 'pkgconfig') +) + diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..a66e168 --- /dev/null +++ b/readme.md @@ -0,0 +1,23 @@ +# liblogging + +liblogging is a single module to manage logs for SERiF and related projects. + +This has been broken out of the main serif project to allow for more modularity + +## Building +In order to build liblogging you need `meson>=1.5.0`. This can be installed with `pip` + +```bash +pip install "meson>=1.5.0" +``` + +Then from the root liblogging directory it is as simple as + +```bash +meson setup build --buildtype=release +meson compile -C build +meson test -C build +``` + +this will auto generate a pkg-config file for you so that linking other libraries to liblogging is easy. + diff --git a/src/probe/public/probe.h b/src/logging/include/logging.h similarity index 62% rename from src/probe/public/probe.h rename to src/logging/include/logging.h index 6b55474..b9addc7 100644 --- a/src/probe/public/probe.h +++ b/src/logging/include/logging.h @@ -4,11 +4,11 @@ // File Author: Emily Boudreaux // Last Modified: April 03, 2025 // -// 4DSSE is free software; you can use it and/or modify +// liblogging is free software; you can use it and/or modify // it under the terms and restrictions the GNU General Library Public // License version 3 (GPLv3) as published by the Free Software Foundation. // -// 4DSSE is distributed in the hope that it will be useful, +// liblogging 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 Library General Public License for more details. @@ -26,53 +26,12 @@ #include #include -#include "mfem.hpp" #include "quill/Logger.h" /** * @brief The Probe namespace contains utility functions for debugging and logging. */ -namespace serif::probe { - /** - * @brief Pause the execution and wait for user input. - */ - void pause(); - - /** - * @brief Wait for a specified number of seconds. - * @param seconds The number of seconds to wait. - */ - void wait(int seconds); - - /** - * @brief Visualize a solution using GLVis. - * @param u The GridFunction to visualize. - * @param mesh The mesh associated with the GridFunction. - * @param windowTitle The title of the visualization window. - * @param keyset The keyset to use for visualization. - */ - void glVisView(mfem::GridFunction& u, mfem::Mesh& mesh, - const std::string& windowTitle = "grid function", const std::string& keyset=""); - - /** - * @brief Visualize a vector using GLVis. - * @param vec The vector to visualize. - * @param mesh The mesh associated with the vector. - * @param windowTitle The title of the visualization window. - * @param keyset The keyset to use for visualization. - */ - void glVisView(mfem::Vector &vec, mfem::FiniteElementSpace &fes, - const std::string &windowTitle = "vector", const std::string& keyset=""); - - double getMeshRadius(mfem::Mesh& mesh); - - std::pair, std::vector> getRaySolution(mfem::GridFunction& u, mfem::Mesh& mesh, - const std::vector& rayDirection, int numSamples, std::string filename=""); - - std::pair, std::vector> getRaySolution(mfem::Vector &vec, mfem::FiniteElementSpace &fes, - const std::vector& rayDirection, int numSamples, std::string filename=""); - - +namespace fourdst::logging { /** * @brief Class to manage logging operations. */ @@ -134,4 +93,4 @@ namespace serif::probe { const std::string& loggerName); }; -} // namespace Probe \ No newline at end of file +} // namespace Probe diff --git a/src/logging/lib/logging.cpp b/src/logging/lib/logging.cpp new file mode 100644 index 0000000..610608d --- /dev/null +++ b/src/logging/lib/logging.cpp @@ -0,0 +1,98 @@ +/* *********************************************************************** +// +// Copyright (C) 2025 -- The 4D-STAR Collaboration +// File Author: Emily Boudreaux +// Last Modified: March 18, 2025 +// +// liblogging is free software; you can use it and/or modify +// it under the terms and restrictions the GNU General Library Public +// License version 3 (GPLv3) as published by the Free Software Foundation. +// +// liblogging 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 Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public License +// along with this software; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// *********************************************************************** */ +#include "quill/Backend.h" +#include "quill/Frontend.h" +#include "quill/Logger.h" +#include "quill/sinks/ConsoleSink.h" +#include "quill/sinks/FileSink.h" +#include "quill/LogMacros.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "logging.h" + + +namespace fourdst::logging { + +LogManager::LogManager() { + quill::Backend::start(); + auto CLILogger = quill::Frontend::create_or_get_logger( + "root", + quill::Frontend::create_or_get_sink("sink_id_1")); + + newFileLogger("fourdst.log", "log"); + loggerMap.emplace("stdout", CLILogger); +} + +LogManager::~LogManager() = default; + +quill::Logger* LogManager::getLogger(const std::string& loggerName) { + auto it = loggerMap.find(loggerName); // Find *once* + if (it == loggerMap.end()) { + throw std::runtime_error("Cannot find logger " + loggerName); + } + return it->second; // Return the raw pointer from the shared_ptr +} + +std::vector LogManager::getLoggerNames() { + std::vector loggerNames; + loggerNames.reserve(loggerMap.size()); + for (const auto& pair : loggerMap) { // Use range-based for loop and const auto& + loggerNames.push_back(pair.first); + } + return loggerNames; +} + +std::vector LogManager::getLoggers() { + std::vector loggers; + loggers.reserve(loggerMap.size()); + for (const auto& pair : loggerMap) { + loggers.push_back(pair.second); // Get the raw pointer + } + return loggers; +} + +quill::Logger* LogManager::newFileLogger(const std::string& filename, + const std::string& loggerName) { + auto file_sink = quill::Frontend::create_or_get_sink( + filename, + []() { + quill::FileSinkConfig cfg; + cfg.set_open_mode('w'); + return cfg; + }(), + quill::FileEventNotifier{}); + // Get the raw pointer. + quill::Logger* rawLogger = quill::Frontend::create_or_get_logger(loggerName, std::move(file_sink)); + + // Create a shared_ptr from the raw pointer. + loggerMap.emplace(loggerName, rawLogger); + return rawLogger; +} + +} // namespace Probe diff --git a/src/probe/meson.build b/src/logging/meson.build similarity index 63% rename from src/probe/meson.build rename to src/logging/meson.build index 186586b..37feb89 100644 --- a/src/probe/meson.build +++ b/src/logging/meson.build @@ -4,11 +4,11 @@ # File Author: Emily Boudreaux # Last Modified: March 19, 2025 # -# 4DSSE is free software; you can use it and/or modify +# liblogging is free software; you can use it and/or modify # it under the terms and restrictions the GNU General Library Public # License version 3 (GPLv3) as published by the Free Software Foundation. # -# 4DSSE is distributed in the hope that it will be useful, +# liblogging 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 Library General Public License for more details. @@ -18,33 +18,28 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # *********************************************************************** # -# Define the library -probe_sources = files( - 'private/probe.cpp', +logging_sources = files( + 'lib/logging.cpp', ) -probe_headers = files( - 'public/probe.h' +logging_headers = files( + 'include/logging.h' ) dependencies = [ - config_dep, - mfem_dep, quill_dep, - macros_dep ] -# Define the liblogger library so it can be linked against by other parts of the build system -libprobe = library('probe', - probe_sources, - include_directories: include_directories('public'), +liblogging = library('logging', + logging_sources, + include_directories: include_directories('include'), cpp_args: ['-fvisibility=default'], install : true, dependencies: dependencies ) -probe_dep = declare_dependency( - include_directories: include_directories('public'), - link_with: libprobe, +logging_dep = declare_dependency( + include_directories: include_directories('include'), + link_with: liblogging, dependencies: dependencies -) \ No newline at end of file +) diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..ca3c7a3 --- /dev/null +++ b/src/meson.build @@ -0,0 +1 @@ +subdir('logging') diff --git a/src/probe/private/probe.cpp b/src/probe/private/probe.cpp deleted file mode 100644 index 111f01d..0000000 --- a/src/probe/private/probe.cpp +++ /dev/null @@ -1,263 +0,0 @@ -/* *********************************************************************** -// -// Copyright (C) 2025 -- The 4D-STAR Collaboration -// File Author: Emily Boudreaux -// Last Modified: March 18, 2025 -// -// 4DSSE is free software; you can use it and/or modify -// it under the terms and restrictions the GNU General Library Public -// License version 3 (GPLv3) as published by the Free Software Foundation. -// -// 4DSSE 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 Library General Public License for more details. -// -// You should have received a copy of the GNU Library General Public License -// along with this software; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -// *********************************************************************** */ -#include "quill/Backend.h" -#include "quill/Frontend.h" -#include "quill/Logger.h" -#include "quill/sinks/ConsoleSink.h" -#include "quill/sinks/FileSink.h" -#include "quill/LogMacros.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mfem.hpp" - -#include "config.h" -#include "probe.h" - -#include "warning_control.h" - - -namespace serif::probe { - -void pause() { - std::cout << "Execution paused. Please press enter to continue..." - << std::endl; // Use endl to flush - std::cin.get(); -} - -void wait(int seconds) { - std::this_thread::sleep_for(std::chrono::seconds(seconds)); -} - -void glVisView(mfem::GridFunction& u, mfem::Mesh& mesh, - const std::string& windowTitle, const std::string& keyset) { - serif::config::Config& config = serif::config::Config::getInstance(); - quill::Logger* logger = LogManager::getInstance().getLogger("log"); - if (config.get("Probe:GLVis:Visualization", true)) { - std::string usedKeyset; - LOG_INFO(logger, "Visualizing solution using GLVis..."); - LOG_INFO(logger, "Window title: {}", windowTitle); - if (keyset.empty()) { - usedKeyset = config.get("Probe:GLVis:DefaultKeyset", ""); - } else { - usedKeyset = keyset; - } - LOG_INFO(logger, "Keyset: {}", usedKeyset); - const auto vishost = config.get("Probe:GLVis:Host", "localhost"); - const auto visport = config.get("Probe:GLVis:Port", 19916); - mfem::socketstream sol_sock(vishost.c_str(), visport); - sol_sock.precision(8); - sol_sock << "solution\n" << mesh << u - << "window_title '" << windowTitle << - "'\n" << "keys " << usedKeyset << "\n"; - sol_sock << std::flush; - } -} - -void glVisView(mfem::Vector &vec, mfem::FiniteElementSpace &fes, const std::string &windowTitle, const std::string& keyset) { - mfem::GridFunction gf(&fes); - - DEPRECATION_WARNING_OFF - gf.SetData(vec); - DEPRECATION_WARNING_ON - glVisView(gf, *fes.GetMesh(), windowTitle, keyset); -} - -double getMeshRadius(mfem::Mesh& mesh) { - mesh.EnsureNodes(); - const mfem::GridFunction *nodes = mesh.GetNodes(); - double *node_data = nodes->GetData(); // THIS IS KEY - - int data_size = nodes->Size(); - double max_radius = 0.0; - - for (int i = 0; i < data_size; ++i) { - if (node_data[i] > max_radius) { - max_radius = node_data[i]; - } - } - return max_radius; - -} - -std::pair, std::vector> getRaySolution(mfem::GridFunction& u, mfem::Mesh& mesh, - const std::vector& rayDirection, - int numSamples, std::string filename) { - serif::config::Config& config = serif::config::Config::getInstance(); - serif::probe::LogManager& logManager = serif::probe::LogManager::getInstance(); - quill::Logger* logger = logManager.getLogger("log"); - LOG_INFO(logger, "Getting ray solution..."); - // Check if the directory to write to exists - // If it does not exist and MakeDir is true create it - // Otherwise throw an exception - bool makeDir = config.get("Probe:GetRaySolution:MakeDir", true); - std::filesystem::path path = filename; - - if (makeDir) { - std::filesystem::path dir = path.parent_path(); - if (!std::filesystem::exists(dir)) { - LOG_INFO(logger, "Creating directory {}", dir.string()); - std::filesystem::create_directories(dir); - } - } else { - if (!std::filesystem::exists(path.parent_path())) { - throw(std::runtime_error("Directory " + path.parent_path().string() + " does not exist")); - } - } - - std::vector samples; - samples.reserve(numSamples); - double x, y, z, r, sampleValue; - double radius = getMeshRadius(mesh); - mfem::Vector rayOrigin(3); rayOrigin = 0.0; - mfem::DenseMatrix rayPoints(3, numSamples); - std::vector radialPoints; - radialPoints.reserve(numSamples); - mfem::ElementTransformation* Trans = nullptr; - mfem::Vector physicalCoords; - - for (int i = 0; i < numSamples; i++) { - r = i * radius / numSamples; - // Let rayDirection = (theta, phi) that the ray will be cast too - x = r * std::sin(rayDirection[0]) * std::cos(rayDirection[1]); - y = r * std::sin(rayDirection[0]) * std::sin(rayDirection[1]); - z = r * std::cos(rayDirection[0]); - rayPoints(0, i) = x; - rayPoints(1, i) = y; - rayPoints(2, i) = z; - } - - mfem::Array elementIds; - mfem::Array ips; - mesh.FindPoints(rayPoints, elementIds, ips); - for (int i = 0; i < elementIds.Size(); i++) { - Trans = mesh.GetElementTransformation(elementIds[i]); - Trans->Transform(ips[i], physicalCoords); - double r = std::sqrt(physicalCoords[0] * physicalCoords[0] + - physicalCoords[1] * physicalCoords[1] + - physicalCoords[2] * physicalCoords[2]); - radialPoints.push_back(r); - - int elementId = elementIds[i]; - mfem::IntegrationPoint ip = ips[i]; - if (elementId >= 0) { // Check if the point was found in an element - sampleValue = u.GetValue(elementId, ip); - LOG_DEBUG(logger, "Probe::getRaySolution() : Ray point {} found in element {} with r={:0.2f} and theta={:0.2f}", i, elementId, r, sampleValue); - samples.push_back(sampleValue); - } else { // If the point was not found in an element - LOG_INFO(logger, "Probe::getRaySolution() : Ray point {} not found", i); - samples.push_back(0.0); - } - } - std::pair, std::vector> samplesPair(radialPoints, samples); - - if (!filename.empty()) { - std::ofstream file(filename); - if (file.is_open()) { - file << "r,u\n"; - for (int i = 0; i < numSamples; i++) { - file << samplesPair.first[i] << "," << samplesPair.second[i] << "\n"; - } - file << std::endl; - file.close(); - } else { - throw(std::runtime_error("Could not open file " + filename)); - } - } - - return samplesPair; -} - -std::pair, std::vector> getRaySolution(mfem::Vector &vec, mfem::FiniteElementSpace &fes, - const std::vector& rayDirection, - int numSamples, std::string filename) { - mfem::GridFunction gf(&fes); - DEPRECATION_WARNING_OFF - gf.SetData(vec); - DEPRECATION_WARNING_ON - return getRaySolution(gf, *fes.GetMesh(), rayDirection, numSamples, filename); -} - -LogManager::LogManager() { - serif::config::Config& config = serif::config::Config::getInstance(); - quill::Backend::start(); - auto CLILogger = quill::Frontend::create_or_get_logger( - "root", - quill::Frontend::create_or_get_sink("sink_id_1")); - - newFileLogger(config.get("Probe:LogManager:DefaultLogName", "4DSSE.log"), "log"); - loggerMap.emplace("stdout", CLILogger); -} - -LogManager::~LogManager() = default; - -quill::Logger* LogManager::getLogger(const std::string& loggerName) { - auto it = loggerMap.find(loggerName); // Find *once* - if (it == loggerMap.end()) { - throw std::runtime_error("Cannot find logger " + loggerName); - } - return it->second; // Return the raw pointer from the shared_ptr -} - -std::vector LogManager::getLoggerNames() { - std::vector loggerNames; - loggerNames.reserve(loggerMap.size()); - for (const auto& pair : loggerMap) { // Use range-based for loop and const auto& - loggerNames.push_back(pair.first); - } - return loggerNames; -} - -std::vector LogManager::getLoggers() { - std::vector loggers; - loggers.reserve(loggerMap.size()); - for (const auto& pair : loggerMap) { - loggers.push_back(pair.second); // Get the raw pointer - } - return loggers; -} - -quill::Logger* LogManager::newFileLogger(const std::string& filename, - const std::string& loggerName) { - auto file_sink = quill::Frontend::create_or_get_sink( - filename, - []() { - quill::FileSinkConfig cfg; - cfg.set_open_mode('w'); - return cfg; - }(), - quill::FileEventNotifier{}); - // Get the raw pointer. - quill::Logger* rawLogger = quill::Frontend::create_or_get_logger(loggerName, std::move(file_sink)); - - // Create a shared_ptr from the raw pointer. - loggerMap.emplace(loggerName, rawLogger); - return rawLogger; -} - -} // namespace Probe \ No newline at end of file diff --git a/subprojects/quill.wrap b/subprojects/quill.wrap new file mode 100644 index 0000000..d857641 --- /dev/null +++ b/subprojects/quill.wrap @@ -0,0 +1,5 @@ +[wrap-git] +url = https://github.com/odygrd/quill +revision = v8.1.1 + +[cmake] \ No newline at end of file