// 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 . #include "common.h" #include #include #include #include #include #include #include /** * 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 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(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( 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("/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(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::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(×tamp, &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; }