Every TimingMark ("TIMING <section>") and every rugnux step ("TIMING step ...") now
opens an NVTX v3 range of the same name, so Nsight Systems traces from different
machines can be lined up by pipeline step. Start/end ranges, not push/pop, so a
range may end on another thread and parallel arms may overlap. Header-only NVTX
(CUDA::nvtx3), a no-op without a profiler attached and compiled out without CUDA.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
110 lines
3.3 KiB
C++
110 lines
3.3 KiB
C++
// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#include "spdlog/sinks/daily_file_sink.h"
|
|
#include "spdlog/sinks/stdout_color_sinks.h"
|
|
#include "spdlog/sinks/base_sink.h"
|
|
#include "spdlog/fmt/bin_to_hex.h"
|
|
|
|
#include "Logger.h"
|
|
#include "GitInfo.h"
|
|
|
|
#ifdef JFJOCH_USE_CUDA
|
|
#include <nvtx3/nvToolsExt.h>
|
|
|
|
uint64_t NvtxRangeStart(const std::string &name) {
|
|
return nvtxRangeStartA(name.c_str());
|
|
}
|
|
|
|
void NvtxRangeEnd(uint64_t id) {
|
|
nvtxRangeEnd(id);
|
|
}
|
|
#else
|
|
uint64_t NvtxRangeStart(const std::string &) {
|
|
return 0;
|
|
}
|
|
|
|
void NvtxRangeEnd(uint64_t) {}
|
|
#endif
|
|
|
|
namespace {
|
|
spdlog::sink_ptr routed_sink; // set once, at start-up, before any thread logs
|
|
}
|
|
|
|
void Logger::RouteAll(spdlog::sink_ptr sink) {
|
|
routed_sink = std::move(sink);
|
|
}
|
|
|
|
Logger::Logger(const std::string &service_name, const std::string &file_name) {
|
|
if (routed_sink) {
|
|
spdlog_logger = std::make_shared<spdlog::logger>(service_name, routed_sink);
|
|
spdlog_logger->set_level(spdlog::level::debug);
|
|
return;
|
|
}
|
|
std::string variant = "";
|
|
#ifdef JFJOCH_USE_CUDA
|
|
variant += "C";
|
|
#endif
|
|
#ifdef JFJOCH_USE_FFTW
|
|
variant += "F";
|
|
#endif
|
|
std::vector<spdlog::sink_ptr> sinks;
|
|
|
|
sinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
|
|
if (!file_name.empty())
|
|
sinks.push_back(std::make_shared<spdlog::sinks::daily_file_sink_mt>(file_name, 23, 59));
|
|
|
|
spdlog_logger = std::make_shared<spdlog::logger>(service_name, std::begin(sinks), std::end(sinks));
|
|
if (jfjoch_git_sha1().empty())
|
|
spdlog_logger->info("Version {} {}", jfjoch_version(), variant);
|
|
else
|
|
spdlog_logger->info("Version {} (git {} {}) {}",
|
|
jfjoch_version(),
|
|
jfjoch_git_sha1().substr(0, 6),
|
|
jfjoch_git_date(),
|
|
variant);
|
|
}
|
|
|
|
class LogBufferSink : public spdlog::sinks::base_sink<std::mutex> {
|
|
public:
|
|
struct Line {
|
|
spdlog::log_clock::time_point time;
|
|
spdlog::level::level_enum level;
|
|
std::string text;
|
|
};
|
|
std::vector<Line> lines;
|
|
protected:
|
|
void sink_it_(const spdlog::details::log_msg &msg) override {
|
|
lines.push_back({msg.time, msg.level, std::string(msg.payload.data(), msg.payload.size())});
|
|
}
|
|
void flush_() override {}
|
|
};
|
|
|
|
Logger::Logger(std::shared_ptr<LogBufferSink> sink) : buffer(std::move(sink)) {
|
|
spdlog_logger = std::make_shared<spdlog::logger>("buffer", buffer);
|
|
spdlog_logger->set_level(spdlog::level::trace); // the logger it is replayed into filters
|
|
}
|
|
|
|
Logger Logger::Buffered() {
|
|
return Logger(std::make_shared<LogBufferSink>());
|
|
}
|
|
|
|
// Called once the work that logged is finished, so nothing is still writing to the buffer. Each line
|
|
// keeps the time it was logged at, not the time of the replay, so a timestamped log still shows when
|
|
// the work happened.
|
|
void Logger::ReplayInto(Logger &to) const {
|
|
for (const auto &l : buffer->lines)
|
|
to.spdlog_logger->log(l.time, spdlog::source_loc{}, l.level, l.text);
|
|
}
|
|
|
|
void Logger::ErrorException(const std::exception &e) {
|
|
spdlog_logger->error(e.what());
|
|
}
|
|
|
|
Logger &Logger::Verbose(bool input) {
|
|
if (input)
|
|
spdlog_logger->set_level(spdlog::level::debug);
|
|
else
|
|
spdlog_logger->set_level(spdlog::level::info);
|
|
return *this;
|
|
} |