Files
yabridge/src/common/logging/common.cpp
T
Robbert van der Helm df93944f3b Prevent allocations caused by Logger::log_trace
C++ would always construct an `std::string` from the string constant
every iteration. Since this also happened when `YABRIDGE_DEBUG_LEVEL` is
not set to 2, this ended up causing unnecessary allocations.
2021-05-23 00:21:21 +02:00

128 lines
4.7 KiB
C++

// yabridge: a Wine VST bridge
// Copyright (C) 2020-2021 Robbert van der Helm
//
// This program 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 3 of the License, or
// (at your option) any later version.
//
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
#include "common.h"
#include <vestige/aeffectx.h>
#include <boost/process/environment.hpp>
#include <chrono>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <iostream>
/**
* The environment variable indicating whether to log to a file. Will log to
* STDERR if not specified.
*/
constexpr char logging_file_environment_variable[] = "YABRIDGE_DEBUG_FILE";
/**
* The verbosity of the logging, defaults to `Logger::Verbosity::basic`.
*
* @see Logger::Verbosity
*/
constexpr char logging_verbosity_environment_variable[] =
"YABRIDGE_DEBUG_LEVEL";
Logger::Logger(std::shared_ptr<std::ostream> stream,
Verbosity verbosity_level,
std::string prefix,
bool prefix_timestamp)
: verbosity(verbosity_level),
stream(stream),
prefix(prefix),
prefix_timestamp(prefix_timestamp) {}
Logger Logger::create_from_environment(std::string prefix) {
auto env = boost::this_process::environment();
std::string file_path = env[logging_file_environment_variable].to_string();
std::string verbosity =
env[logging_verbosity_environment_variable].to_string();
// Default to `Verbosity::basic` if the environment variable has not
// been set or if it is not an integer.
Verbosity verbosity_level;
try {
verbosity_level = static_cast<Verbosity>(std::stoi(verbosity));
} catch (const std::invalid_argument&) {
verbosity_level = Verbosity::basic;
}
// If `file` points to a valid location then use create/truncate the
// file and write all of the logs there, otherwise use STDERR
auto log_file = std::make_shared<std::ofstream>(
file_path, std::fstream::out | std::fstream::app);
if (log_file->is_open()) {
return Logger(log_file, verbosity_level, prefix);
} else {
// For STDERR we sadly can't just use `std::cerr`. In the group process
// we need to capture all output generated by the process itself, and
// the only way to do this is by reopening the STDERR and STDOUT streams
// to a pipe. Luckily `/dev/stderr` stays unaffected, so we can still
// write there without causing infinite loops.
return Logger(std::make_shared<std::ofstream>("/dev/stderr"),
verbosity_level, prefix);
}
}
Logger Logger::create_wine_stderr() {
auto env = boost::this_process::environment();
std::string verbosity =
env[logging_verbosity_environment_variable].to_string();
Verbosity verbosity_level;
try {
verbosity_level = static_cast<Verbosity>(std::stoi(verbosity));
} catch (const std::invalid_argument&) {
verbosity_level = Verbosity::basic;
}
// We're logging directly to `std::cerr` instead of to `/dev/stderr` because
// we want the STDERR redirection from the group host processes to still
// function here
return Logger(std::shared_ptr<std::ostream>(&std::cerr, [](auto*) {}),
verbosity_level, "", false);
}
void Logger::log(const std::string& message) {
std::ostringstream formatted_message;
if (prefix_timestamp) {
const auto current_time = std::chrono::system_clock::now();
const time_t timestamp =
std::chrono::system_clock::to_time_t(current_time);
// How did C++ manage to get time formatting libraries without a way to
// actually get a timestamp in a threadsafe way? `localtime_r` in C++ is
// not portable but luckily we only have to support GCC anyway.
std::tm tm;
localtime_r(&timestamp, &tm);
formatted_message << std::put_time(&tm, "%T") << " ";
}
formatted_message << prefix;
formatted_message << message;
// Flushing a stringstream doesn't do anything, but we need to put a
// linefeed in this string stream rather writing it sprightly to the output
// stream to prevent two messages from being put on the same row
formatted_message << std::endl;
*stream << formatted_message.str() << std::flush;
}