mirror of
https://github.com/gnss-sdr/gnss-sdr
synced 2026-10-18 13:32:37 +00:00
Keep a separate logfile for each run with Abseil logging, as google-glog did
This commit is contained in:
@@ -531,6 +531,10 @@ All notable changes to GNSS-SDR will be documented in this file.
|
||||
- The PVT Monitor now reports per-signal details for satellites used in the
|
||||
position solution, including PRN, constellation, signal, azimuth, elevation,
|
||||
and whether multiple signals were combined. Contributed by @joebre.
|
||||
- Abseil logging now creates a unique timestamp/PID logfile for each run,
|
||||
preserving previous logs across the receiver, calibration tool, and test
|
||||
runners. On POSIX systems, an atomically updated relative symlink points to
|
||||
the latest logfile.
|
||||
|
||||
See the definitions of concepts and metrics at
|
||||
https://gnss-sdr.org/design-forces/
|
||||
|
||||
@@ -206,3 +206,27 @@ set_property(TARGET core_libs
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<BUILD_INTERFACE:${GNSSSDR_SOURCE_DIR}/src/core/interfaces>
|
||||
)
|
||||
|
||||
# Keep the file sink independent of receiver and signal-processing dependencies.
|
||||
# Older toolchains using glog do not build this C++17-only target.
|
||||
if(NOT ENABLE_GLOG_AND_GFLAGS)
|
||||
add_library(gnss_sdr_log_sink STATIC gnss_sdr_log_sink.cc gnss_sdr_log_sink.h)
|
||||
target_compile_features(gnss_sdr_log_sink PUBLIC cxx_std_17)
|
||||
target_include_directories(gnss_sdr_log_sink
|
||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
PRIVATE ${GNSSSDR_SOURCE_DIR}/src/algorithms/libs
|
||||
)
|
||||
target_link_libraries(gnss_sdr_log_sink
|
||||
PUBLIC absl::log_sink
|
||||
PRIVATE absl::log_sink_registry absl::time Threads::Threads
|
||||
)
|
||||
if(FILESYSTEM_FOUND)
|
||||
target_link_libraries(gnss_sdr_log_sink PRIVATE std::filesystem)
|
||||
target_compile_definitions(gnss_sdr_log_sink PRIVATE HAS_STD_FILESYSTEM=1)
|
||||
if(find_experimental)
|
||||
target_compile_definitions(gnss_sdr_log_sink PRIVATE HAS_STD_FILESYSTEM_EXPERIMENTAL=1)
|
||||
endif()
|
||||
else()
|
||||
target_link_libraries(gnss_sdr_log_sink PRIVATE Boost::filesystem $<$<BOOL:${NEED_BOOST_SYSTEM}>:Boost::system>)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
/*!
|
||||
* \file gnss_sdr_log_sink.cc
|
||||
* \brief Buffered, per-run file logging for Abseil.
|
||||
* \author Carles Fernandez-Prades, 2026 <carles.fernandez@cttc.es>
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
|
||||
* This file is part of GNSS-SDR.
|
||||
*
|
||||
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "gnss_sdr_log_sink.h"
|
||||
#include "gnss_sdr_filesystem.h"
|
||||
#include <absl/base/log_severity.h>
|
||||
#include <absl/log/log_entry.h>
|
||||
#include <absl/log/log_sink_registry.h>
|
||||
#include <absl/time/clock.h>
|
||||
#include <absl/time/time.h>
|
||||
#include <cerrno>
|
||||
#include <fcntl.h>
|
||||
#include <stdexcept>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
#if defined(_WIN32)
|
||||
#include <io.h>
|
||||
#include <process.h>
|
||||
#include <sys/stat.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
GnssSdrLogSink::GnssSdrLogSink(const std::string& directory, const std::string& basename)
|
||||
{
|
||||
if (basename.empty() || basename == "." || basename == ".." || basename.find_first_of("/\\:") != std::string::npos)
|
||||
{
|
||||
throw std::invalid_argument("Invalid log basename: " + basename);
|
||||
}
|
||||
const fs::path log_directory = fs::absolute(directory.empty() ? fs::temp_directory_path() : fs::path(directory));
|
||||
fs::create_directories(log_directory);
|
||||
#if defined(_WIN32)
|
||||
const auto pid = _getpid();
|
||||
#else
|
||||
const auto pid = getpid();
|
||||
#endif
|
||||
const std::string stem = basename + "." + absl::FormatTime("%Y%m%d-%H%M%S", absl::Now(), absl::LocalTimeZone()) + "." + std::to_string(pid);
|
||||
for (unsigned int attempt = 0;; ++attempt)
|
||||
{
|
||||
const std::string suffix = attempt == 0 ? "" : "." + std::to_string(attempt);
|
||||
const fs::path path = log_directory / (stem + suffix + ".log");
|
||||
d_filename = path.string();
|
||||
// C++17 ofstream cannot create exclusively. Keep the descriptor open
|
||||
// through fdopen so there is no close/reopen race or truncation.
|
||||
#if defined(_WIN32)
|
||||
const int fd = _wopen(path.c_str(), _O_WRONLY | _O_CREAT | _O_EXCL | _O_BINARY | _O_NOINHERIT, _S_IREAD | _S_IWRITE);
|
||||
#else
|
||||
// Same default mode as glog's --logfile_mode; the process umask still applies.
|
||||
const int fd = open(path.c_str(), O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0664);
|
||||
#endif
|
||||
if (fd < 0)
|
||||
{
|
||||
const int error = errno;
|
||||
if (error == EEXIST)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
throw std::system_error(error, std::generic_category(), "Cannot create logfile " + d_filename);
|
||||
}
|
||||
#if defined(_WIN32)
|
||||
d_logfile.reset(_fdopen(fd, "wb"));
|
||||
#else
|
||||
d_logfile.reset(fdopen(fd, "wb"));
|
||||
#endif
|
||||
if (!d_logfile)
|
||||
{
|
||||
const int error = errno;
|
||||
#if defined(_WIN32)
|
||||
_close(fd);
|
||||
#else
|
||||
close(fd);
|
||||
#endif
|
||||
throw std::system_error(error, std::generic_category(), "Cannot open logfile stream " + d_filename);
|
||||
}
|
||||
break;
|
||||
}
|
||||
try
|
||||
{
|
||||
UpdateLatestLink(basename);
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
std::fprintf(stderr, "Cannot update latest logfile link for %s: %s\n", d_filename.c_str(), ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GnssSdrLogSink::~GnssSdrLogSink()
|
||||
{
|
||||
Flush();
|
||||
if (std::fclose(d_logfile.release()) != 0)
|
||||
{
|
||||
ReportWriteError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GnssSdrLogSink::UpdateLatestLink(const std::string& basename) const
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
// Creating symlinks can require privileges on Windows. The per-run file
|
||||
// remains available without a stable link.
|
||||
(void)basename;
|
||||
#else
|
||||
const fs::path path(d_filename);
|
||||
const fs::path latest = path.parent_path() / (basename + ".log");
|
||||
// Preserve a regular logfile left by versions predating this sink. A hard
|
||||
// link keeps its contents available even when the stable name is replaced.
|
||||
if (fs::is_regular_file(fs::symlink_status(latest)))
|
||||
{
|
||||
fs::create_hard_link(latest, fs::path(d_filename + ".previous"));
|
||||
}
|
||||
fs::path temporary;
|
||||
for (unsigned int attempt = 0;; ++attempt)
|
||||
{
|
||||
temporary = fs::path(d_filename + ".link." + std::to_string(attempt));
|
||||
errorlib::error_code error;
|
||||
fs::create_symlink(path.filename(), temporary, error);
|
||||
if (!error)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (error != errorlib::errc::file_exists)
|
||||
{
|
||||
throw fs::filesystem_error("Cannot create latest logfile symlink", temporary, error);
|
||||
}
|
||||
}
|
||||
errorlib::error_code error;
|
||||
fs::rename(temporary, latest, error);
|
||||
if (error)
|
||||
{
|
||||
errorlib::error_code ignored;
|
||||
fs::remove(temporary, ignored);
|
||||
throw fs::filesystem_error("Cannot replace latest logfile symlink", latest, error);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void GnssSdrLogSink::Send(const absl::LogEntry& entry) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(d_mutex);
|
||||
const auto text = entry.text_message_with_prefix_and_newline();
|
||||
if (std::fwrite(text.data(), 1, text.size(), d_logfile.get()) != text.size())
|
||||
{
|
||||
ReportWriteError();
|
||||
}
|
||||
if (entry.log_severity() >= absl::LogSeverity::kError && std::fflush(d_logfile.get()) != 0)
|
||||
{
|
||||
ReportWriteError();
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
ReportWriteError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GnssSdrLogSink::Flush() noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(d_mutex);
|
||||
if (std::fflush(d_logfile.get()) != 0)
|
||||
{
|
||||
ReportWriteError();
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
ReportWriteError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GnssSdrLogSink::ReportWriteError() noexcept
|
||||
{
|
||||
if (!d_error_reported.test_and_set())
|
||||
{
|
||||
std::fprintf(stderr, "Failed to write or flush logfile %s\n", d_filename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GnssSdrLogSinkGuard::~GnssSdrLogSinkGuard()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
|
||||
void GnssSdrLogSinkGuard::Register(const std::string& directory, const std::string& basename)
|
||||
{
|
||||
if (d_sink)
|
||||
{
|
||||
throw std::logic_error("Log sink is already registered");
|
||||
}
|
||||
auto sink = std::make_unique<GnssSdrLogSink>(directory, basename);
|
||||
absl::AddLogSink(sink.get());
|
||||
d_sink = std::move(sink);
|
||||
}
|
||||
|
||||
|
||||
void GnssSdrLogSinkGuard::Shutdown() noexcept
|
||||
{
|
||||
if (d_sink)
|
||||
{
|
||||
// Stop callbacks before the destructor flushes and closes the file.
|
||||
absl::RemoveLogSink(d_sink.get());
|
||||
d_sink.reset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*!
|
||||
* \file gnss_sdr_log_sink.h
|
||||
* \brief Buffered, per-run file logging for Abseil.
|
||||
* \author Carles Fernandez-Prades, 2026 <carles.fernandez@cttc.es>
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
|
||||
* This file is part of GNSS-SDR.
|
||||
*
|
||||
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#ifndef GNSS_SDR_LOG_SINK_H
|
||||
#define GNSS_SDR_LOG_SINK_H
|
||||
|
||||
#include <absl/log/log_sink.h>
|
||||
#include <atomic>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
/** \addtogroup Core
|
||||
* \{ */
|
||||
/** \addtogroup Core_Receiver_Library
|
||||
* Utilities for the core GNSS receiver.
|
||||
* \{ */
|
||||
|
||||
|
||||
/** A sink must be unregistered, and direct callers stopped, before destruction.
|
||||
* Directory/file creation errors throw; symlink and runtime I/O errors are
|
||||
* reported to stderr. An empty directory selects the system temporary directory.
|
||||
*/
|
||||
class GnssSdrLogSink : public absl::LogSink
|
||||
{
|
||||
public:
|
||||
explicit GnssSdrLogSink(const std::string& directory, const std::string& basename = "gnss-sdr");
|
||||
~GnssSdrLogSink() override;
|
||||
GnssSdrLogSink(const GnssSdrLogSink&) = delete;
|
||||
GnssSdrLogSink& operator=(const GnssSdrLogSink&) = delete;
|
||||
GnssSdrLogSink(GnssSdrLogSink&&) = delete;
|
||||
GnssSdrLogSink& operator=(GnssSdrLogSink&&) = delete;
|
||||
|
||||
void Send(const absl::LogEntry& entry) noexcept override;
|
||||
void Flush() noexcept override;
|
||||
const std::string& filename() const noexcept { return d_filename; }
|
||||
|
||||
private:
|
||||
struct FileCloser
|
||||
{
|
||||
void operator()(std::FILE* file) const noexcept { std::fclose(file); }
|
||||
};
|
||||
|
||||
void UpdateLatestLink(const std::string& basename) const;
|
||||
void ReportWriteError() noexcept;
|
||||
|
||||
std::string d_filename;
|
||||
std::unique_ptr<std::FILE, FileCloser> d_logfile;
|
||||
std::mutex d_mutex;
|
||||
std::atomic_flag d_error_reported = ATOMIC_FLAG_INIT;
|
||||
};
|
||||
|
||||
/** Owns a registered sink. Initialize Abseil once before calling Register().
|
||||
* Register/Shutdown belong to the owning thread; Send/Flush may run concurrently.
|
||||
*/
|
||||
class GnssSdrLogSinkGuard
|
||||
{
|
||||
public:
|
||||
GnssSdrLogSinkGuard() = default;
|
||||
~GnssSdrLogSinkGuard();
|
||||
GnssSdrLogSinkGuard(const GnssSdrLogSinkGuard&) = delete;
|
||||
GnssSdrLogSinkGuard& operator=(const GnssSdrLogSinkGuard&) = delete;
|
||||
GnssSdrLogSinkGuard(GnssSdrLogSinkGuard&&) = delete;
|
||||
GnssSdrLogSinkGuard& operator=(GnssSdrLogSinkGuard&&) = delete;
|
||||
|
||||
void Register(const std::string& directory, const std::string& basename = "gnss-sdr");
|
||||
void Shutdown() noexcept;
|
||||
|
||||
private:
|
||||
std::unique_ptr<GnssSdrLogSink> d_sink;
|
||||
};
|
||||
|
||||
/** \} */
|
||||
/** \} */
|
||||
#endif // GNSS_SDR_LOG_SINK_H
|
||||
@@ -26,7 +26,7 @@ if(ENABLE_GLOG_AND_GFLAGS)
|
||||
target_link_libraries(gnss-sdr PRIVATE Gflags::gflags Glog::glog)
|
||||
target_compile_definitions(gnss-sdr PUBLIC -DUSE_GLOG_AND_GFLAGS=1)
|
||||
else()
|
||||
target_link_libraries(gnss-sdr PRIVATE absl::flags absl::flags_parse absl::log absl::log_initialize absl::log_sink absl::log_sink_registry)
|
||||
target_link_libraries(gnss-sdr PRIVATE absl::flags absl::flags_parse absl::log absl::log_initialize gnss_sdr_log_sink)
|
||||
target_link_libraries(gnss-sdr INTERFACE "$<LINK_LIBRARY:WHOLE_ARCHIVE,absl::log_flags>")
|
||||
endif()
|
||||
|
||||
|
||||
+3
-80
@@ -37,10 +37,8 @@
|
||||
#include <boost/thread/exceptions.hpp> // for thread_resource_error
|
||||
#include <chrono> // for time_point
|
||||
#include <exception> // for exception
|
||||
#include <fstream> // for ofstream
|
||||
#include <iostream> // for operator<<
|
||||
#include <memory> // for unique_ptr
|
||||
#include <mutex> // for lock_guard, mutex
|
||||
#include <ostream> // for std::flush
|
||||
#include <string> // for string
|
||||
|
||||
@@ -54,6 +52,7 @@ using namespace google;
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
#include "gnss_sdr_log_sink.h"
|
||||
#include <absl/flags/flag.h>
|
||||
#include <absl/flags/parse.h>
|
||||
#include <absl/flags/usage.h>
|
||||
@@ -62,84 +61,7 @@ using namespace google;
|
||||
#include <absl/log/globals.h>
|
||||
#include <absl/log/initialize.h>
|
||||
#include <absl/log/log.h>
|
||||
#include <absl/log/log_sink.h>
|
||||
#include <absl/log/log_sink_registry.h>
|
||||
std::string GnssSdrVersionString() { return std::string("gnss-sdr version ") + std::string(GNSS_SDR_VERSION) + "\n"; }
|
||||
class GnssSdrLogSink : public absl::LogSink
|
||||
{
|
||||
public:
|
||||
GnssSdrLogSink()
|
||||
{
|
||||
if (!absl::GetFlag(FLAGS_log_dir).empty())
|
||||
{
|
||||
filename = absl::GetFlag(FLAGS_log_dir) + "/gnss-sdr.log";
|
||||
}
|
||||
else
|
||||
{
|
||||
filename = GetTempDir() + "/gnss-sdr.log";
|
||||
}
|
||||
logfile.open(filename);
|
||||
}
|
||||
void Send(const absl::LogEntry& entry) override
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logfile_mutex);
|
||||
logfile << entry.text_message_with_prefix_and_newline() << std::flush;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex logfile_mutex;
|
||||
std::ofstream logfile;
|
||||
std::string filename;
|
||||
};
|
||||
|
||||
class GnssSdrLogSinkGuard
|
||||
{
|
||||
public:
|
||||
GnssSdrLogSinkGuard() = default;
|
||||
GnssSdrLogSinkGuard(const GnssSdrLogSinkGuard&) = delete;
|
||||
GnssSdrLogSinkGuard& operator=(const GnssSdrLogSinkGuard&) = delete;
|
||||
GnssSdrLogSinkGuard(GnssSdrLogSinkGuard&&) = delete;
|
||||
GnssSdrLogSinkGuard& operator=(GnssSdrLogSinkGuard&&) = delete;
|
||||
~GnssSdrLogSinkGuard() noexcept
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void Register()
|
||||
{
|
||||
log_sink.reset(new GnssSdrLogSink);
|
||||
absl::AddLogSink(log_sink.get());
|
||||
registered = true;
|
||||
absl::InitializeLog();
|
||||
}
|
||||
|
||||
void Shutdown() noexcept
|
||||
{
|
||||
if (registered)
|
||||
{
|
||||
try
|
||||
{
|
||||
absl::FlushLogSinks();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
absl::RemoveLogSink(log_sink.get());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
registered = false;
|
||||
}
|
||||
log_sink.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<GnssSdrLogSink> log_sink;
|
||||
bool registered = false;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if CUDA_GPU_ACCEL
|
||||
@@ -216,7 +138,8 @@ try
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
if (FLAGS_log_dir.empty())
|
||||
#else
|
||||
log_sink.Register();
|
||||
absl::InitializeLog();
|
||||
log_sink.Register(absl::GetFlag(FLAGS_log_dir), "gnss-sdr");
|
||||
if (absl::GetFlag(FLAGS_log_dir).empty())
|
||||
#endif
|
||||
{
|
||||
|
||||
+11
-10
@@ -740,7 +740,8 @@ if(ENABLE_UNIT_TESTING)
|
||||
target_link_libraries(run_tests PRIVATE Gflags::gflags Glog::glog)
|
||||
target_compile_definitions(run_tests PRIVATE -DUSE_GLOG_AND_GFLAGS=1)
|
||||
else()
|
||||
target_link_libraries(run_tests PRIVATE absl::flags absl::flags_parse absl::log absl::log_initialize)
|
||||
target_link_libraries(run_tests PRIVATE absl::flags absl::flags_parse absl::log absl::log_initialize gnss_sdr_log_sink)
|
||||
target_sources(run_tests PRIVATE unit-tests/control-plane/gnss_sdr_log_sink_test.cc)
|
||||
target_link_libraries(run_tests INTERFACE "$<LINK_LIBRARY:WHOLE_ARCHIVE,absl::log_flags>")
|
||||
endif()
|
||||
target_include_directories(run_tests
|
||||
@@ -889,7 +890,7 @@ if(ENABLE_FPGA)
|
||||
target_link_libraries(gps_l1_ca_dll_pll_tracking_test_fpga PRIVATE Gflags::gflags Glog::glog)
|
||||
target_compile_definitions(gps_l1_ca_dll_pll_tracking_test_fpga PRIVATE -DUSE_GLOG_AND_GFLAGS=1)
|
||||
else()
|
||||
target_link_libraries(gps_l1_ca_dll_pll_tracking_test_fpga PRIVATE absl::flags absl::flags_parse absl::log absl::log_initialize)
|
||||
target_link_libraries(gps_l1_ca_dll_pll_tracking_test_fpga PRIVATE absl::flags absl::flags_parse absl::log absl::log_initialize gnss_sdr_log_sink)
|
||||
target_link_libraries(gps_l1_ca_dll_pll_tracking_test_fpga INTERFACE "$<LINK_LIBRARY:WHOLE_ARCHIVE,absl::log_flags>")
|
||||
endif()
|
||||
target_include_directories(gps_l1_ca_dll_pll_tracking_test_fpga
|
||||
@@ -1016,7 +1017,7 @@ if(ENABLE_SYSTEM_TESTING)
|
||||
if(ENABLE_GLOG_AND_GFLAGS)
|
||||
set(OPT_LIBS_ ${OPT_LIBS_} Gflags::gflags Glog::glog)
|
||||
else()
|
||||
set(OPT_LIBS_ ${OPT_LIBS_} absl::flags_parse absl::log absl::log_initialize absl::log_sink absl::log_sink_registry)
|
||||
set(OPT_LIBS_ ${OPT_LIBS_} absl::flags_parse absl::log absl::log_initialize gnss_sdr_log_sink)
|
||||
endif()
|
||||
add_system_test(position_test
|
||||
CMAKE_ARGS -DCMAKE_BUILD_TYPE=$<$<CONFIG:Debug>:Debug>$<$<CONFIG:Release>:Release>$<$<CONFIG:RelWithDebInfo>:RelWithDebInfo>$<$<CONFIG:MinSizeRel>:MinSizeRel>$<$<CONFIG:NoOptWithASM>:Debug>$<$<CONFIG:Coverage>:Debug>$<$<CONFIG:O2WithASM>:RelWithDebInfo>$<$<CONFIG:O3WithASM>:RelWithDebInfo>$<$<CONFIG:ASAN>:Debug>
|
||||
@@ -1092,7 +1093,7 @@ if(NOT ENABLE_PACKAGING AND NOT ENABLE_FPGA)
|
||||
target_link_libraries(flowgraph_test PRIVATE Gflags::gflags Glog::glog)
|
||||
target_compile_definitions(flowgraph_test PRIVATE -DUSE_GLOG_AND_GFLAGS=1)
|
||||
else()
|
||||
target_link_libraries(flowgraph_test PRIVATE absl::flags absl::flags_parse absl::log absl::log_initialize)
|
||||
target_link_libraries(flowgraph_test PRIVATE absl::flags absl::flags_parse absl::log absl::log_initialize gnss_sdr_log_sink)
|
||||
target_link_libraries(flowgraph_test INTERFACE "$<LINK_LIBRARY:WHOLE_ARCHIVE,absl::log_flags>")
|
||||
endif()
|
||||
|
||||
@@ -1151,7 +1152,7 @@ if(NOT ENABLE_PACKAGING AND NOT ENABLE_FPGA)
|
||||
target_link_libraries(gnss_block_test PRIVATE Gflags::gflags Glog::glog)
|
||||
target_compile_definitions(gnss_block_test PRIVATE -DUSE_GLOG_AND_GFLAGS=1)
|
||||
else()
|
||||
target_link_libraries(gnss_block_test PRIVATE absl::flags absl::flags_parse absl::log absl::log_initialize)
|
||||
target_link_libraries(gnss_block_test PRIVATE absl::flags absl::flags_parse absl::log absl::log_initialize gnss_sdr_log_sink)
|
||||
target_link_libraries(gnss_block_test INTERFACE "$<LINK_LIBRARY:WHOLE_ARCHIVE,absl::log_flags>")
|
||||
endif()
|
||||
|
||||
@@ -1212,7 +1213,7 @@ if(NOT ENABLE_PACKAGING AND NOT ENABLE_FPGA)
|
||||
target_link_libraries(gnuradio_block_test PRIVATE Gflags::gflags Glog::glog)
|
||||
target_compile_definitions(gnuradio_block_test PRIVATE -DUSE_GLOG_AND_GFLAGS=1)
|
||||
else()
|
||||
target_link_libraries(gnuradio_block_test PRIVATE absl::flags absl::flags_parse absl::log absl::log_initialize)
|
||||
target_link_libraries(gnuradio_block_test PRIVATE absl::flags absl::flags_parse absl::log absl::log_initialize gnss_sdr_log_sink)
|
||||
target_link_libraries(gnuradio_block_test INTERFACE "$<LINK_LIBRARY:WHOLE_ARCHIVE,absl::log_flags>")
|
||||
endif()
|
||||
|
||||
@@ -1249,7 +1250,7 @@ if(ENABLE_GLOG_AND_GFLAGS)
|
||||
target_link_libraries(matio_test PRIVATE Gflags::gflags Glog::glog)
|
||||
target_compile_definitions(matio_test PRIVATE -DUSE_GLOG_AND_GFLAGS=1)
|
||||
else()
|
||||
target_link_libraries(matio_test PRIVATE absl::flags absl::flags_parse absl::log absl::log_initialize)
|
||||
target_link_libraries(matio_test PRIVATE absl::flags absl::flags_parse absl::log absl::log_initialize gnss_sdr_log_sink)
|
||||
target_link_libraries(matio_test INTERFACE "$<LINK_LIBRARY:WHOLE_ARCHIVE,absl::log_flags>")
|
||||
endif()
|
||||
|
||||
@@ -1302,7 +1303,7 @@ if(NOT ENABLE_PACKAGING AND NOT ENABLE_FPGA)
|
||||
target_link_libraries(acq_test PRIVATE Gflags::gflags Glog::glog)
|
||||
target_compile_definitions(acq_test PRIVATE -DUSE_GLOG_AND_GFLAGS=1)
|
||||
else()
|
||||
target_link_libraries(acq_test PRIVATE absl::flags absl::flags_parse absl::log absl::log_initialize)
|
||||
target_link_libraries(acq_test PRIVATE absl::flags absl::flags_parse absl::log absl::log_initialize gnss_sdr_log_sink)
|
||||
target_link_libraries(acq_test INTERFACE "$<LINK_LIBRARY:WHOLE_ARCHIVE,absl::log_flags>")
|
||||
endif()
|
||||
target_include_directories(acq_test
|
||||
@@ -1394,7 +1395,7 @@ if(NOT ENABLE_PACKAGING AND NOT ENABLE_FPGA)
|
||||
target_link_libraries(trk_test PRIVATE Gflags::gflags Glog::glog)
|
||||
target_compile_definitions(trk_test PRIVATE -DUSE_GLOG_AND_GFLAGS=1)
|
||||
else()
|
||||
target_link_libraries(trk_test PRIVATE absl::flags absl::flags_parse absl::log absl::log_initialize)
|
||||
target_link_libraries(trk_test PRIVATE absl::flags absl::flags_parse absl::log absl::log_initialize gnss_sdr_log_sink)
|
||||
target_link_libraries(trk_test INTERFACE "$<LINK_LIBRARY:WHOLE_ARCHIVE,absl::log_flags>")
|
||||
endif()
|
||||
if(USE_GENERIC_LAMBDAS)
|
||||
@@ -1459,7 +1460,7 @@ if(NOT ENABLE_PACKAGING AND NOT ENABLE_FPGA)
|
||||
target_link_libraries(control_thread_test PRIVATE Gflags::gflags Glog::glog)
|
||||
target_compile_definitions(control_thread_test PRIVATE -DUSE_GLOG_AND_GFLAGS=1)
|
||||
else()
|
||||
target_link_libraries(control_thread_test PRIVATE absl::flags absl::flags_parse absl::log absl::log_initialize)
|
||||
target_link_libraries(control_thread_test PRIVATE absl::flags absl::flags_parse absl::log absl::log_initialize gnss_sdr_log_sink)
|
||||
target_link_libraries(control_thread_test INTERFACE "$<LINK_LIBRARY:WHOLE_ARCHIVE,absl::log_flags>")
|
||||
endif()
|
||||
|
||||
|
||||
@@ -21,10 +21,8 @@
|
||||
#include "gnss_sdr_flags.h"
|
||||
#include "gps_acq_assist.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
@@ -39,86 +37,12 @@ using namespace google;
|
||||
DECLARE_string(log_dir);
|
||||
#endif
|
||||
#else
|
||||
#include "gnss_sdr_log_sink.h"
|
||||
#include <absl/flags/flag.h>
|
||||
#include <absl/flags/parse.h>
|
||||
#include <absl/log/flags.h>
|
||||
#include <absl/log/initialize.h>
|
||||
#include <absl/log/log.h>
|
||||
#include <absl/log/log_sink.h>
|
||||
#include <absl/log/log_sink_registry.h>
|
||||
class TestLogSink : public absl::LogSink
|
||||
{
|
||||
public:
|
||||
TestLogSink()
|
||||
{
|
||||
if (!absl::GetFlag(FLAGS_log_dir).empty())
|
||||
{
|
||||
logfile.open(absl::GetFlag(FLAGS_log_dir) + "/test.log");
|
||||
}
|
||||
else
|
||||
{
|
||||
logfile.open(GetTempDir() + "/test.log");
|
||||
}
|
||||
}
|
||||
void Send(const absl::LogEntry& entry) override
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logfile_mutex);
|
||||
logfile << entry.text_message_with_prefix_and_newline() << std::flush;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex logfile_mutex;
|
||||
std::ofstream logfile;
|
||||
};
|
||||
|
||||
class TestLogSinkGuard
|
||||
{
|
||||
public:
|
||||
TestLogSinkGuard() = default;
|
||||
TestLogSinkGuard(const TestLogSinkGuard&) = delete;
|
||||
TestLogSinkGuard& operator=(const TestLogSinkGuard&) = delete;
|
||||
TestLogSinkGuard(TestLogSinkGuard&&) = delete;
|
||||
TestLogSinkGuard& operator=(TestLogSinkGuard&&) = delete;
|
||||
~TestLogSinkGuard() noexcept
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void Register()
|
||||
{
|
||||
log_sink.reset(new TestLogSink);
|
||||
absl::AddLogSink(log_sink.get());
|
||||
registered = true;
|
||||
absl::InitializeLog();
|
||||
}
|
||||
|
||||
void Shutdown() noexcept
|
||||
{
|
||||
if (registered)
|
||||
{
|
||||
try
|
||||
{
|
||||
absl::FlushLogSinks();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
absl::RemoveLogSink(log_sink.get());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
registered = false;
|
||||
}
|
||||
log_sink.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<TestLogSink> log_sink;
|
||||
bool registered = false;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
@@ -140,7 +64,7 @@ try
|
||||
{
|
||||
} // catch the "testing::internal::<unnamed>::ClassUniqueToAlwaysTrue" from gtest
|
||||
#else
|
||||
TestLogSinkGuard log_sink;
|
||||
GnssSdrLogSinkGuard log_sink;
|
||||
absl::ParseCommandLine(argc, argv);
|
||||
try
|
||||
{
|
||||
@@ -149,7 +73,8 @@ try
|
||||
catch (...)
|
||||
{
|
||||
} // catch the "testing::internal::<unnamed>::ClassUniqueToAlwaysTrue" from gtest
|
||||
log_sink.Register();
|
||||
absl::InitializeLog();
|
||||
log_sink.Register(absl::GetFlag(FLAGS_log_dir), "test");
|
||||
#endif
|
||||
int res = 0;
|
||||
try
|
||||
|
||||
@@ -47,7 +47,6 @@
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <numeric>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
@@ -62,42 +61,17 @@ using namespace google;
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
#include "gnss_sdr_log_sink.h"
|
||||
#include <absl/flags/parse.h>
|
||||
#include <absl/log/globals.h>
|
||||
#include <absl/log/initialize.h>
|
||||
#include <absl/log/log.h>
|
||||
#include <absl/log/log_sink.h>
|
||||
#include <absl/log/log_sink_registry.h>
|
||||
#endif
|
||||
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
DEFINE_int32(num_channels, 11, "Number of channels");
|
||||
#else
|
||||
ABSL_FLAG(int32_t, num_channels, 11, "Number of channels");
|
||||
class PositionTestLogSink : public absl::LogSink
|
||||
{
|
||||
public:
|
||||
PositionTestLogSink()
|
||||
{
|
||||
if (!absl::GetFlag(FLAGS_log_dir).empty())
|
||||
{
|
||||
logfile.open(absl::GetFlag(FLAGS_log_dir) + "/position_test.log");
|
||||
}
|
||||
else
|
||||
{
|
||||
logfile.open(GetTempDir() + "/position_test.log");
|
||||
}
|
||||
}
|
||||
void Send(const absl::LogEntry& entry) override
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logfile_mutex);
|
||||
logfile << entry.text_message_with_prefix_and_newline() << std::flush;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex logfile_mutex;
|
||||
std::ofstream logfile;
|
||||
};
|
||||
#endif
|
||||
|
||||
// For GPS NAVIGATION (L1)
|
||||
@@ -1249,8 +1223,7 @@ int main(int argc, char** argv)
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
bool command_line_flags_initialized = false;
|
||||
#else
|
||||
std::unique_ptr<PositionTestLogSink> log_sink;
|
||||
bool log_sink_registered = false;
|
||||
GnssSdrLogSinkGuard log_sink;
|
||||
#endif
|
||||
|
||||
try
|
||||
@@ -1270,10 +1243,8 @@ int main(int argc, char** argv)
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
#else
|
||||
absl::ParseCommandLine(argc, argv);
|
||||
log_sink.reset(new PositionTestLogSink);
|
||||
absl::AddLogSink(log_sink.get());
|
||||
log_sink_registered = true;
|
||||
absl::InitializeLog();
|
||||
log_sink.Register(absl::GetFlag(FLAGS_log_dir), "position_test");
|
||||
#endif
|
||||
|
||||
// Run the Tests
|
||||
@@ -1311,20 +1282,7 @@ int main(int argc, char** argv)
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (log_sink_registered)
|
||||
{
|
||||
try
|
||||
{
|
||||
absl::FlushLogSinks();
|
||||
absl::RemoveLogSink(log_sink.get());
|
||||
log_sink_registered = false;
|
||||
log_sink.reset();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cerr << "Unexpected exception while shutting down logging.\n";
|
||||
}
|
||||
}
|
||||
log_sink.Shutdown();
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
+4
-77
@@ -19,81 +19,7 @@
|
||||
#include "unit_tests_common.h"
|
||||
|
||||
#if !USE_GLOG_AND_GFLAGS
|
||||
class TestingLogSink : public absl::LogSink
|
||||
{
|
||||
public:
|
||||
TestingLogSink()
|
||||
{
|
||||
if (!absl::GetFlag(FLAGS_log_dir).empty())
|
||||
{
|
||||
filename = std::string(absl::GetFlag(FLAGS_log_dir) + "/run_tests.log");
|
||||
}
|
||||
else
|
||||
{
|
||||
filename = std::string(GetTempDir() + "/run_tests.log");
|
||||
}
|
||||
logfile.open(filename);
|
||||
}
|
||||
void Send(const absl::LogEntry& entry) override
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logfile_mutex);
|
||||
logfile << entry.text_message_with_prefix_and_newline() << std::flush;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex logfile_mutex;
|
||||
std::ofstream logfile;
|
||||
std::string filename;
|
||||
};
|
||||
|
||||
class TestingLogSinkGuard
|
||||
{
|
||||
public:
|
||||
TestingLogSinkGuard() = default;
|
||||
TestingLogSinkGuard(const TestingLogSinkGuard&) = delete;
|
||||
TestingLogSinkGuard& operator=(const TestingLogSinkGuard&) = delete;
|
||||
TestingLogSinkGuard(TestingLogSinkGuard&&) = delete;
|
||||
TestingLogSinkGuard& operator=(TestingLogSinkGuard&&) = delete;
|
||||
~TestingLogSinkGuard() noexcept
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void Register()
|
||||
{
|
||||
log_sink.reset(new TestingLogSink);
|
||||
absl::AddLogSink(log_sink.get());
|
||||
registered = true;
|
||||
absl::InitializeLog();
|
||||
}
|
||||
|
||||
void Shutdown() noexcept
|
||||
{
|
||||
if (registered)
|
||||
{
|
||||
try
|
||||
{
|
||||
absl::FlushLogSinks();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
absl::RemoveLogSink(log_sink.get());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
registered = false;
|
||||
}
|
||||
log_sink.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<TestingLogSink> log_sink;
|
||||
bool registered = false;
|
||||
};
|
||||
#include "gnss_sdr_log_sink.h"
|
||||
#endif
|
||||
|
||||
// For GPS NAVIGATION (L1)
|
||||
@@ -116,9 +42,10 @@ try
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
#else
|
||||
TestingLogSinkGuard log_sink;
|
||||
GnssSdrLogSinkGuard log_sink;
|
||||
absl::ParseCommandLine(argc, argv);
|
||||
log_sink.Register();
|
||||
absl::InitializeLog();
|
||||
log_sink.Register(absl::GetFlag(FLAGS_log_dir), "run_tests");
|
||||
#endif
|
||||
try
|
||||
{
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
/*!
|
||||
* \file gnss_sdr_log_sink_test.cc
|
||||
* \brief Tests for per-run Abseil file logging.
|
||||
* \author Carles Fernandez-Prades, 2026. cfernandez(at)cttc.es
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
|
||||
* This file is part of GNSS-SDR.
|
||||
*
|
||||
* Copyright (C) 2010-2026 (see AUTHORS file for a list of contributors)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "gnss_sdr_filesystem.h"
|
||||
#include "gnss_sdr_log_sink.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <absl/log/log.h>
|
||||
#include <absl/log/log_sink_registry.h>
|
||||
#include <absl/time/clock.h>
|
||||
#include <absl/time/time.h>
|
||||
#include <atomic>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#if !defined(_WIN32)
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
class GnssSdrLogSinkTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
do
|
||||
{
|
||||
directory = fs::temp_directory_path() / ("gnss-sdr-log-sink-test-" + std::to_string(absl::ToUnixNanos(absl::Now())));
|
||||
}
|
||||
while (!fs::create_directory(directory));
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
errorlib::error_code error;
|
||||
fs::remove_all(directory, error);
|
||||
}
|
||||
|
||||
static std::string Read(const fs::path& path)
|
||||
{
|
||||
std::ifstream file(path.string(), std::ios::binary);
|
||||
return std::string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
// Compare the exact bytes supplied by Abseil, without assuming a timezone
|
||||
// or reimplementing its prefix formatting in the test.
|
||||
class ForwardingSink : public absl::LogSink
|
||||
{
|
||||
public:
|
||||
explicit ForwardingSink(GnssSdrLogSink& sink) : sink(sink) {}
|
||||
void Send(const absl::LogEntry& entry) override
|
||||
{
|
||||
expected.append(entry.text_message_with_prefix_and_newline().data(), entry.text_message_with_prefix_and_newline().size());
|
||||
sink.Send(entry);
|
||||
}
|
||||
GnssSdrLogSink& sink;
|
||||
std::string expected;
|
||||
};
|
||||
|
||||
fs::path directory;
|
||||
};
|
||||
|
||||
|
||||
TEST_F(GnssSdrLogSinkTest, CreatesDirectoriesAndPreservesEveryBasename)
|
||||
{
|
||||
const auto nested = directory / "nested" / "logs";
|
||||
for (const auto* basename : {"gnss-sdr", "run_tests", "test", "front_end_cal", "position_test"})
|
||||
{
|
||||
GnssSdrLogSink sink(nested.string(), basename);
|
||||
const fs::path logfile(sink.filename());
|
||||
EXPECT_TRUE(fs::is_regular_file(logfile));
|
||||
EXPECT_EQ(logfile.parent_path(), nested);
|
||||
EXPECT_TRUE(std::regex_match(logfile.filename().string(), std::regex(std::string(basename) + R"(\.[0-9]{8}-[0-9]{6}\.[0-9]+\.log)")));
|
||||
#if !defined(_WIN32)
|
||||
EXPECT_EQ(fs::read_symlink(nested / (std::string(basename) + ".log")), logfile.filename());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GnssSdrLogSinkTest, RepeatedCreationPreservesFilesAndUpdatesRelativeLink)
|
||||
{
|
||||
std::set<std::string> filenames;
|
||||
for (int run = 0; run < 20; ++run)
|
||||
{
|
||||
GnssSdrLogSink sink(directory.string());
|
||||
EXPECT_TRUE(filenames.insert(sink.filename()).second);
|
||||
LOG(INFO).NoPrefix().ToSinkOnly(&sink) << "run " << run;
|
||||
#if !defined(_WIN32)
|
||||
EXPECT_EQ(fs::read_symlink(directory / "gnss-sdr.log"), fs::path(sink.filename()).filename());
|
||||
#endif
|
||||
}
|
||||
for (const auto& filename : filenames)
|
||||
{
|
||||
EXPECT_EQ(Read(filename).substr(0, 4), "run ");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GnssSdrLogSinkTest, PreservesAbseilFormattingAndFlushesErrors)
|
||||
{
|
||||
GnssSdrLogSink sink(directory.string());
|
||||
ForwardingSink capture(sink);
|
||||
LOG(INFO).AtLocation("log_sink_test.cc", 42).ToSinkOnly(&capture) << "buffered message";
|
||||
sink.Flush();
|
||||
EXPECT_EQ(Read(sink.filename()), capture.expected);
|
||||
EXPECT_NE(capture.expected.find("log_sink_test.cc:42] buffered message\n"), std::string::npos);
|
||||
LOG(ERROR).NoPrefix().ToSinkOnly(&capture) << "flush immediately";
|
||||
EXPECT_EQ(Read(sink.filename()), capture.expected);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GnssSdrLogSinkTest, DestructorFlushesBufferedMessages)
|
||||
{
|
||||
std::string filename;
|
||||
{
|
||||
GnssSdrLogSink sink(directory.string());
|
||||
filename = sink.filename();
|
||||
LOG(INFO).NoPrefix().ToSinkOnly(&sink) << "flushed on destruction";
|
||||
}
|
||||
EXPECT_EQ(Read(filename), "flushed on destruction\n");
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GnssSdrLogSinkTest, ConcurrentSendAndFlushPreserveWholeMessages)
|
||||
{
|
||||
GnssSdrLogSink sink(directory.string());
|
||||
std::atomic<bool> done{false};
|
||||
std::thread flusher([&sink, &done]() {
|
||||
while (!done.load())
|
||||
{
|
||||
sink.Flush();
|
||||
std::this_thread::yield();
|
||||
}
|
||||
});
|
||||
std::vector<std::thread> writers;
|
||||
writers.reserve(8);
|
||||
for (int writer = 0; writer < 8; ++writer)
|
||||
{
|
||||
writers.emplace_back([&sink, writer]() {
|
||||
for (int message = 0; message < 250; ++message)
|
||||
{
|
||||
LOG(INFO).NoPrefix().ToSinkOnly(&sink) << writer << ":" << message;
|
||||
}
|
||||
});
|
||||
}
|
||||
for (auto& writer : writers)
|
||||
{
|
||||
writer.join();
|
||||
}
|
||||
done.store(true);
|
||||
flusher.join();
|
||||
sink.Flush();
|
||||
std::ifstream file(sink.filename());
|
||||
std::set<std::string> messages;
|
||||
std::string line;
|
||||
while (std::getline(file, line))
|
||||
{
|
||||
EXPECT_TRUE(messages.insert(line).second);
|
||||
}
|
||||
EXPECT_EQ(messages.size(), 2000U);
|
||||
for (int writer = 0; writer < 8; ++writer)
|
||||
{
|
||||
for (int message = 0; message < 250; ++message)
|
||||
{
|
||||
EXPECT_EQ(messages.count(std::to_string(writer) + ":" + std::to_string(message)), 1U);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GnssSdrLogSinkTest, RejectsInvalidDirectoryAndBasename)
|
||||
{
|
||||
const auto file = directory / "file";
|
||||
std::ofstream(file.string()) << "keep me";
|
||||
EXPECT_THROW(GnssSdrLogSink sink(file.string()), std::exception);
|
||||
EXPECT_THROW(GnssSdrLogSink sink((file / "logs").string()), std::exception);
|
||||
EXPECT_THROW(GnssSdrLogSink sink(directory.string(), "../escape"), std::invalid_argument);
|
||||
EXPECT_EQ(Read(file), "keep me");
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GnssSdrLogSinkTest, GuardRegistersFlushesAndUnregisters)
|
||||
{
|
||||
GnssSdrLogSinkGuard guard;
|
||||
guard.Register(directory.string());
|
||||
EXPECT_THROW(guard.Register(directory.string()), std::logic_error);
|
||||
LOG(INFO) << "registered message";
|
||||
absl::FlushLogSinks();
|
||||
fs::path logfile;
|
||||
for (const auto& entry : fs::directory_iterator(directory))
|
||||
{
|
||||
if (!fs::is_symlink(entry.path()) && entry.path().extension() == ".log")
|
||||
{
|
||||
logfile = entry.path();
|
||||
}
|
||||
}
|
||||
ASSERT_FALSE(logfile.empty());
|
||||
EXPECT_NE(Read(logfile).find("registered message"), std::string::npos);
|
||||
guard.Shutdown();
|
||||
guard.Shutdown();
|
||||
const auto contents = Read(logfile);
|
||||
LOG(INFO) << "after unregister";
|
||||
EXPECT_EQ(Read(logfile), contents);
|
||||
}
|
||||
|
||||
|
||||
#if !defined(_WIN32)
|
||||
TEST_F(GnssSdrLogSinkTest, PreservesLegacyStableLogfile)
|
||||
{
|
||||
std::ofstream((directory / "gnss-sdr.log").string()) << "legacy log";
|
||||
GnssSdrLogSink sink(directory.string());
|
||||
EXPECT_EQ(Read(sink.filename() + ".previous"), "legacy log");
|
||||
EXPECT_EQ(fs::read_symlink(directory / "gnss-sdr.log"), fs::path(sink.filename()).filename());
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GnssSdrLogSinkTest, SymlinkFailureLeavesLoggingUsableAndCleansTemporaryLink)
|
||||
{
|
||||
fs::create_directory(directory / "gnss-sdr.log");
|
||||
GnssSdrLogSink sink(directory.string());
|
||||
LOG(ERROR).NoPrefix().ToSinkOnly(&sink) << "still logging";
|
||||
EXPECT_EQ(Read(sink.filename()), "still logging\n");
|
||||
EXPECT_TRUE(fs::is_directory(directory / "gnss-sdr.log"));
|
||||
for (const auto& entry : fs::directory_iterator(directory))
|
||||
{
|
||||
EXPECT_EQ(entry.path().filename().string().find(".link."), std::string::npos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GnssSdrLogSinkTest, RejectsUnwritableDirectory)
|
||||
{
|
||||
if (geteuid() == 0)
|
||||
{
|
||||
GTEST_SKIP() << "Root can write despite directory permissions";
|
||||
}
|
||||
const auto readonly = directory / "readonly";
|
||||
fs::create_directory(readonly);
|
||||
ASSERT_EQ(chmod(readonly.c_str(), 0500), 0);
|
||||
EXPECT_THROW(GnssSdrLogSink sink(readonly.string()), std::exception);
|
||||
EXPECT_EQ(chmod(readonly.c_str(), 0700), 0);
|
||||
}
|
||||
#endif
|
||||
@@ -70,7 +70,7 @@ if(ENABLE_GLOG_AND_GFLAGS)
|
||||
target_link_libraries(front-end-cal PRIVATE Glog::glog)
|
||||
target_compile_definitions(front-end-cal PRIVATE -DUSE_GLOG_AND_GFLAGS=1)
|
||||
else()
|
||||
target_link_libraries(front-end-cal PRIVATE absl::flags absl::flags_parse absl::log absl::log_initialize absl::log_sink absl::log_sink_registry)
|
||||
target_link_libraries(front-end-cal PRIVATE absl::flags absl::flags_parse absl::log absl::log_initialize gnss_sdr_log_sink)
|
||||
endif()
|
||||
|
||||
if(GNURADIO_USES_STD_POINTERS)
|
||||
|
||||
@@ -63,7 +63,6 @@
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <stdexcept> // for logic_error
|
||||
#include <string>
|
||||
#include <thread>
|
||||
@@ -74,6 +73,7 @@
|
||||
#include <gflags/gflags.h>
|
||||
#include <glog/logging.h>
|
||||
#else
|
||||
#include "gnss_sdr_log_sink.h"
|
||||
#include <absl/flags/flag.h>
|
||||
#include <absl/flags/parse.h>
|
||||
#include <absl/flags/usage.h>
|
||||
@@ -81,10 +81,6 @@
|
||||
#include <absl/log/globals.h>
|
||||
#include <absl/log/initialize.h>
|
||||
#include <absl/log/log.h>
|
||||
#include <absl/log/log_sink.h>
|
||||
#include <absl/log/log_sink_registry.h>
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#if HAS_GENERIC_LAMBDA
|
||||
@@ -107,80 +103,6 @@ using namespace google;
|
||||
#endif
|
||||
DECLARE_string(log_dir);
|
||||
#else
|
||||
class FrontEndCalLogSink : public absl::LogSink
|
||||
{
|
||||
public:
|
||||
FrontEndCalLogSink()
|
||||
{
|
||||
if (!absl::GetFlag(FLAGS_log_dir).empty())
|
||||
{
|
||||
logfile.open(absl::GetFlag(FLAGS_log_dir) + "/front_end_cal.log");
|
||||
}
|
||||
else
|
||||
{
|
||||
logfile.open(GetTempDir() + "/front_end_cal.log");
|
||||
}
|
||||
}
|
||||
void Send(const absl::LogEntry& entry) override
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logfile_mutex);
|
||||
logfile << entry.text_message_with_prefix_and_newline() << std::flush;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex logfile_mutex;
|
||||
std::ofstream logfile;
|
||||
};
|
||||
|
||||
class FrontEndCalLogSinkGuard
|
||||
{
|
||||
public:
|
||||
FrontEndCalLogSinkGuard() = default;
|
||||
FrontEndCalLogSinkGuard(const FrontEndCalLogSinkGuard&) = delete;
|
||||
FrontEndCalLogSinkGuard& operator=(const FrontEndCalLogSinkGuard&) = delete;
|
||||
FrontEndCalLogSinkGuard(FrontEndCalLogSinkGuard&&) = delete;
|
||||
FrontEndCalLogSinkGuard& operator=(FrontEndCalLogSinkGuard&&) = delete;
|
||||
~FrontEndCalLogSinkGuard() noexcept
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void Register()
|
||||
{
|
||||
log_sink.reset(new FrontEndCalLogSink);
|
||||
absl::AddLogSink(log_sink.get());
|
||||
registered = true;
|
||||
absl::InitializeLog();
|
||||
}
|
||||
|
||||
void Shutdown() noexcept
|
||||
{
|
||||
if (registered)
|
||||
{
|
||||
try
|
||||
{
|
||||
absl::FlushLogSinks();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
absl::RemoveLogSink(log_sink.get());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
registered = false;
|
||||
}
|
||||
log_sink.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<FrontEndCalLogSink> log_sink;
|
||||
bool registered = false;
|
||||
};
|
||||
|
||||
std::string FrontEndCalVersionString() { return std::string(FRONT_END_CAL_VERSION) + "\n"; }
|
||||
#endif
|
||||
|
||||
@@ -369,7 +291,7 @@ try
|
||||
{
|
||||
#if USE_GLOG_AND_GFLAGS
|
||||
#else
|
||||
FrontEndCalLogSinkGuard log_sink;
|
||||
GnssSdrLogSinkGuard log_sink;
|
||||
#endif
|
||||
|
||||
try
|
||||
@@ -404,12 +326,13 @@ try
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
if (FLAGS_log_dir.empty())
|
||||
#else
|
||||
log_sink.Register();
|
||||
absl::InitializeLog();
|
||||
log_sink.Register(absl::GetFlag(FLAGS_log_dir), "front_end_cal");
|
||||
if (absl::GetFlag(FLAGS_log_dir).empty())
|
||||
#endif
|
||||
{
|
||||
std::cout << "Logging will be done at "
|
||||
<< "/tmp"
|
||||
<< fs::temp_directory_path()
|
||||
<< '\n'
|
||||
<< "Use front-end-cal --log_dir=/path/to/log to change that."
|
||||
<< '\n';
|
||||
|
||||
Reference in New Issue
Block a user