diff --git a/src/logging.cpp b/src/logging.cpp index 6451906..7d5119b 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -1,6 +1,12 @@ #include "logging.h" #include +#include +#include #include +#include +#include +// ReSharper disable once CppUnusedIncludeDirective +#include #include #include "tools.h" @@ -14,6 +20,9 @@ using std::tuple; using std::make_tuple; namespace expr = boost::log::expressions; +namespace keywords = boost::log::keywords; +namespace sinks = boost::log::sinks; +namespace attr = boost::log::attributes; template <> const string& getEnumTypeName() { @@ -75,7 +84,17 @@ void PausableBackendAdapter::resume() { buffer.clear(); } -boost::shared_ptr initLogging() { +BOOST_LOG_GLOBAL_LOGGER_INIT(globalLogger, LoggerType) { + LoggerType logger; + + logger.add_attribute("TimeStamp", attr::local_clock()); + + return logger; +} + +BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", LogLevel) + +boost::shared_ptr addPausableStderrSink(LogLevel minLogLevel) { // Create logging backend that logs to stderr auto streamBackend = boost::make_shared(); streamBackend->add_stream(boost::shared_ptr(&std::cerr, [](std::ostream*) {})); @@ -86,14 +105,24 @@ boost::shared_ptr initLogging() { // Create a sink that feeds into the adapter auto sink = boost::make_shared>(pausableAdapter); - - // Set output formatting - sink->set_formatter(expr::stream << "[" << expr::attr("Severity") << "] " << expr::smessage); - + sink->set_formatter(expr::stream << "[" << severity << "] " << expr::smessage); + sink->set_filter(severity >= minLogLevel); boost::log::core::get()->add_sink(sink); + return pausableAdapter; } +void addFileSink(const boost::filesystem::path& logFilePath, LogLevel minLogLevel) { + auto textFileBackend = boost::make_shared( + keywords::file_name = logFilePath.string()); + auto sink = boost::make_shared>(textFileBackend); + sink->set_formatter(expr::stream + << "[" << expr::format_date_time("TimeStamp", "%Y-%m-%d %H:%M:%S") + << "] [" << severity << "] " << expr::smessage); + sink->set_filter(severity >= minLogLevel); + boost::log::core::get()->add_sink(sink); +} + void logTimedEvent(const string& eventName, centiseconds start, centiseconds end, const string& value) { LOG_DEBUG << "##" << eventName << "[" << formatDuration(start) << "-" << formatDuration(end) << "]: " << value; } diff --git a/src/logging.h b/src/logging.h index 0d32aa7..0031b4c 100644 --- a/src/logging.h +++ b/src/logging.h @@ -9,6 +9,7 @@ #include #include #include "centiseconds.h" +#include #include "enumTools.h" enum class LogLevel { @@ -33,7 +34,7 @@ std::istream& operator>>(std::istream& stream, LogLevel& value); using LoggerType = boost::log::sources::severity_logger_mt; -BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(globalLogger, LoggerType) +BOOST_LOG_GLOBAL_LOGGER(globalLogger, LoggerType) #define LOG(level) \ BOOST_LOG_STREAM_WITH_PARAMS(globalLogger::get(), (::boost::log::keywords::severity = level)) @@ -61,6 +62,8 @@ private: bool isPaused = false; }; -boost::shared_ptr initLogging(); +boost::shared_ptr addPausableStderrSink(LogLevel minLogLevel); + +void addFileSink(const boost::filesystem::path& logFilePath, LogLevel minLogLevel); void logTimedEvent(const std::string& eventName, centiseconds start, centiseconds end, const std::string& value); diff --git a/src/main.cpp b/src/main.cpp index e0343f2..5ecae3b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,6 +16,7 @@ using std::exception; using std::string; +using std::vector; using std::unique_ptr; using std::map; using std::chrono::duration; @@ -23,6 +24,8 @@ using std::chrono::duration_cast; using boost::filesystem::path; using boost::property_tree::ptree; +namespace tclap = TCLAP; + string getMessage(const exception& e) { string result(e.what()); try { @@ -69,36 +72,45 @@ ptree createXmlTree(const path& filePath, const map& phones return tree; } -// Tell TCLAP how to handle boost::optional +// Tell TCLAP how to handle our types namespace TCLAP { template<> - struct ArgTraits> { - typedef TCLAP::StringLike ValueCategory; + struct ArgTraits { + typedef ValueLike ValueCategory; }; } int main(int argc, char *argv[]) { - auto logOutputController = initLogging(); - logOutputController->pause(); + auto pausableStderrSink = addPausableStderrSink(LogLevel::Warning); + pausableStderrSink->pause(); // Define command-line parameters const char argumentValueSeparator = ' '; - TCLAP::CmdLine cmd(appName, argumentValueSeparator, appVersion); + tclap::CmdLine cmd(appName, argumentValueSeparator, appVersion); cmd.setExceptionHandling(false); cmd.setOutput(new NiceCmdLineOutput()); - TCLAP::UnlabeledValueArg inputFileName("inputFile", "The input file. Must be a sound file in WAVE format.", true, "", "string", cmd); - TCLAP::ValueArg> dialog("d", "dialog", "The text of the dialog.", false, boost::optional(), "string", cmd); + auto logLevels = vector(getEnumValues()); + tclap::ValuesConstraint logLevelConstraint(logLevels); + tclap::ValueArg logLevel("", "logLevel", "The minimum log level to log", false, LogLevel::Debug, &logLevelConstraint, cmd); + tclap::ValueArg logFileName("", "logFile", "The log file path.", false, string(), "string", cmd); + tclap::ValueArg dialog("d", "dialog", "The text of the dialog.", false, string(), "string", cmd); + tclap::UnlabeledValueArg inputFileName("inputFile", "The input file. Must be a sound file in WAVE format.", true, "", "string", cmd); try { auto resumeLogging = gsl::finally([&]() { std::cerr << std::endl << std::endl; - logOutputController->resume(); + pausableStderrSink->resume(); std::cerr << std::endl; }); // Parse command line cmd.parse(argc, argv); + // Set up log file + if (logFileName.isSet()) { + addFileSink(path(logFileName.getValue()), logLevel.getValue()); + } + // Detect phones const int columnWidth = 30; std::cerr << std::left; @@ -108,7 +120,7 @@ int main(int argc, char *argv[]) { ProgressBar progressBar; phones = detectPhones( createAudioStream(inputFileName.getValue()), - dialog.getValue(), + dialog.isSet() ? dialog.getValue() : boost::optional(), progressBar); } std::cerr << "Done" << std::endl; @@ -125,12 +137,12 @@ int main(int argc, char *argv[]) { boost::property_tree::write_xml(std::cout, xmlTree, boost::property_tree::xml_writer_settings(' ', 2)); return 0; - } catch (TCLAP::ArgException& e) { + } catch (tclap::ArgException& e) { // Error parsing command-line args. cmd.getOutput()->failure(cmd, e); std::cerr << std::endl; return 1; - } catch (TCLAP::ExitException&) { + } catch (tclap::ExitException&) { // A built-in TCLAP command (like --help) has finished. Exit application. std::cerr << std::endl; return 0;