Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(logging): add logging namespace and create logging::init method #2336

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1184,7 +1184,7 @@ namespace config {
auto line = argv[x];

if (line == "--help"sv) {
print_help(*argv);
logging::print_help(*argv);
return 1;
}
#ifdef _WIN32
Expand All @@ -1204,7 +1204,7 @@ namespace config {
break;
}
if (apply_flags(line + 1)) {
print_help(*argv);
logging::print_help(*argv);
return -1;
}
}
Expand All @@ -1218,7 +1218,7 @@ namespace config {
else {
TUPLE_EL(var, 1, parse_option(line, line_end));
if (!var) {
print_help(*argv);
logging::print_help(*argv);
return -1;
}

Expand Down
6 changes: 3 additions & 3 deletions src/entry_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,12 @@
*
* EXAMPLES:
* ```cpp
* print_help("sunshine", 0, nullptr);
* help("sunshine", 0, nullptr);
* ```
*/
int
help(const char *name, int argc, char *argv[]) {
print_help(name);
logging::print_help(name);

Check warning on line 95 in src/entry_handler.cpp

View check run for this annotation

Codecov / codecov/patch

src/entry_handler.cpp#L95

Added line #L95 was not covered by tests
return 0;
}

Expand All @@ -109,7 +109,7 @@
*/
int
version(const char *name, int argc, char *argv[]) {
std::cout << PROJECT_NAME << " version: v" << PROJECT_VER << std::endl;
// version was already logged at startup
return 0;
}

Expand Down
225 changes: 184 additions & 41 deletions src/logging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
*/

// standard includes
#include <fstream>
#include <iostream>

// lib includes
#include <boost/core/null_deleter.hpp>
#include <boost/log/attributes/clock.hpp>
#include <boost/log/common.hpp>
#include <boost/log/expressions.hpp>
Expand All @@ -16,6 +18,10 @@
// local includes
#include "logging.h"

extern "C" {
#include <libavutil/log.h>
}

using namespace std::literals;

namespace bl = boost::log;
Expand All @@ -29,45 +35,182 @@
bl::sources::severity_logger<int> error(4); // Recoverable errors
bl::sources::severity_logger<int> fatal(5); // Unrecoverable errors

/**
* @brief Flush the log.
*
* EXAMPLES:
* ```cpp
* log_flush();
* ```
*/
void
log_flush() {
sink->flush();
}
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", int)

/**
* @brief Print help to stdout.
* @param name The name of the program.
*
* EXAMPLES:
* ```cpp
* print_help("sunshine");
* ```
*/
void
print_help(const char *name) {
std::cout
<< "Usage: "sv << name << " [options] [/path/to/configuration_file] [--cmd]"sv << std::endl
<< " Any configurable option can be overwritten with: \"name=value\""sv << std::endl
<< std::endl
<< " Note: The configuration will be created if it doesn't exist."sv << std::endl
<< std::endl
<< " --help | print help"sv << std::endl
<< " --creds username password | set user credentials for the Web manager"sv << std::endl
<< " --version | print the version of sunshine"sv << std::endl
<< std::endl
<< " flags"sv << std::endl
<< " -0 | Read PIN from stdin"sv << std::endl
<< " -1 | Do not load previously saved state and do retain any state after shutdown"sv << std::endl
<< " | Effectively starting as if for the first time without overwriting any pairings with your devices"sv << std::endl
<< " -2 | Force replacement of headers in video stream"sv << std::endl
<< " -p | Enable/Disable UPnP"sv << std::endl
<< std::endl;
}
namespace logging {
/**
* @brief A destructor that restores the initial state.
*/
deinit_t::~deinit_t() {
deinit();
}

/**
* @brief Deinitialize the logging system.
*
* EXAMPLES:
* ```cpp
* deinit();
* ```
*/
void
deinit() {
log_flush();
ReenigneArcher marked this conversation as resolved.
Show resolved Hide resolved
bl::core::get()->remove_sink(sink);
sink.reset();
}

/**
* @brief Initialize the logging system.
* @param min_log_level The minimum log level to output.
* @param log_file The log file to write to.
* @returns A deinit_t object that will deinitialize the logging system when it goes out of scope.
*
* EXAMPLES:
* ```cpp
* log_init(2, "sunshine.log");
* ```
*/
[[nodiscard]] std::unique_ptr<deinit_t>
init(int min_log_level, const std::string &log_file) {
ReenigneArcher marked this conversation as resolved.
Show resolved Hide resolved
if (sink) {
// Deinitialize the logging system before reinitializing it. This can probably only ever be hit in tests.
deinit();
}

Check warning on line 79 in src/logging.cpp

View check run for this annotation

Codecov / codecov/patch

src/logging.cpp#L78-L79

Added lines #L78 - L79 were not covered by tests

setup_av_logging(min_log_level);

sink = boost::make_shared<text_sink>();

boost::shared_ptr<std::ostream> stream { &std::cout, boost::null_deleter() };
sink->locked_backend()->add_stream(stream);
sink->locked_backend()->add_stream(boost::make_shared<std::ofstream>(log_file));
sink->set_filter(severity >= min_log_level);

sink->set_formatter([](const bl::record_view &view, bl::formatting_ostream &os) {
constexpr const char *message = "Message";
constexpr const char *severity = "Severity";
constexpr int DATE_BUFFER_SIZE = 21 + 2 + 1; // Full string plus ": \0"

auto log_level = view.attribute_values()[severity].extract<int>().get();

std::string_view log_type;
switch (log_level) {
case 0:
log_type = "Verbose: "sv;
break;
case 1:
log_type = "Debug: "sv;
break;
case 2:
log_type = "Info: "sv;
break;
case 3:
log_type = "Warning: "sv;
break;
case 4:
log_type = "Error: "sv;
break;
case 5:
log_type = "Fatal: "sv;
break;

Check warning on line 116 in src/logging.cpp

View check run for this annotation

Codecov / codecov/patch

src/logging.cpp#L114-L116

Added lines #L114 - L116 were not covered by tests
};

char _date[DATE_BUFFER_SIZE];
std::time_t t = std::time(nullptr);
strftime(_date, DATE_BUFFER_SIZE, "[%Y:%m:%d:%H:%M:%S]: ", std::localtime(&t));

os << _date << log_type << view.attribute_values()[message].extract<std::string>();
});

// Flush after each log record to ensure log file contents on disk isn't stale.
// This is particularly important when running from a Windows service.
sink->locked_backend()->auto_flush(true);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is enabled, why are we even flushing manually?

Copy link
Member Author

@ReenigneArcher ReenigneArcher Apr 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, perhaps the manual flush was just left over from before auto_flush was added? Maybe @cgutman has some idea?


bl::core::get()->add_sink(sink);
return std::make_unique<deinit_t>();
}

/**
* @brief Setup AV logging.
* @param min_log_level The log level.
*/
void
setup_av_logging(int min_log_level) {
if (min_log_level >= 1) {
av_log_set_level(AV_LOG_QUIET);
}
else {
av_log_set_level(AV_LOG_DEBUG);
}
av_log_set_callback([](void *ptr, int level, const char *fmt, va_list vl) {
static int print_prefix = 1;

Check warning on line 147 in src/logging.cpp

View check run for this annotation

Codecov / codecov/patch

src/logging.cpp#L147

Added line #L147 was not covered by tests
char buffer[1024];

av_log_format_line(ptr, level, fmt, vl, buffer, sizeof(buffer), &print_prefix);
if (level <= AV_LOG_ERROR) {
// We print AV_LOG_FATAL at the error level. FFmpeg prints things as fatal that
// are expected in some cases, such as lack of codec support or similar things.
BOOST_LOG(error) << buffer;
}
else if (level <= AV_LOG_WARNING) {
BOOST_LOG(warning) << buffer;
}
else if (level <= AV_LOG_INFO) {
BOOST_LOG(info) << buffer;
}
else if (level <= AV_LOG_VERBOSE) {
// AV_LOG_VERBOSE is less verbose than AV_LOG_DEBUG
BOOST_LOG(debug) << buffer;
}

Check warning on line 165 in src/logging.cpp

View check run for this annotation

Codecov / codecov/patch

src/logging.cpp#L165

Added line #L165 was not covered by tests
else {
BOOST_LOG(verbose) << buffer;
}
});
}

/**
* @brief Flush the log.
*
* EXAMPLES:
* ```cpp
* log_flush();
* ```
*/
void
log_flush() {
if (sink) {
sink->flush();
}
}

/**
* @brief Print help to stdout.
* @param name The name of the program.
*
* EXAMPLES:
* ```cpp
* print_help("sunshine");
* ```
*/
void
print_help(const char *name) {
std::cout
<< "Usage: "sv << name << " [options] [/path/to/configuration_file] [--cmd]"sv << std::endl
<< " Any configurable option can be overwritten with: \"name=value\""sv << std::endl
<< std::endl
<< " Note: The configuration will be created if it doesn't exist."sv << std::endl
<< std::endl
<< " --help | print help"sv << std::endl
<< " --creds username password | set user credentials for the Web manager"sv << std::endl
<< " --version | print the version of sunshine"sv << std::endl
<< std::endl
<< " flags"sv << std::endl
<< " -0 | Read PIN from stdin"sv << std::endl
<< " -1 | Do not load previously saved state and do retain any state after shutdown"sv << std::endl
<< " | Effectively starting as if for the first time without overwriting any pairings with your devices"sv << std::endl
<< " -2 | Force replacement of headers in video stream"sv << std::endl
<< " -p | Enable/Disable UPnP"sv << std::endl
<< std::endl;
}
} // namespace logging
23 changes: 17 additions & 6 deletions src/logging.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include <boost/log/common.hpp>
#include <boost/log/sinks.hpp>

extern boost::shared_ptr<boost::log::sinks::asynchronous_sink<boost::log::sinks::text_ostream_backend>> sink;
using text_sink = boost::log::sinks::asynchronous_sink<boost::log::sinks::text_ostream_backend>;

extern boost::log::sources::severity_logger<int> verbose;
Expand All @@ -20,8 +19,20 @@ extern boost::log::sources::severity_logger<int> warning;
extern boost::log::sources::severity_logger<int> error;
extern boost::log::sources::severity_logger<int> fatal;

// functions
void
log_flush();
void
print_help(const char *name);
namespace logging {
class deinit_t {
public:
~deinit_t();
};

void
deinit();
[[nodiscard]] std::unique_ptr<deinit_t>
init(int min_log_level, const std::string &log_file);
void
setup_av_logging(int min_log_level);
void
log_flush();
void
print_help(const char *name);
} // namespace logging
Loading
Loading