Skip to content
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: 5 additions & 1 deletion apps/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,11 @@ if(BUILD_SERVER_APP)
add_executable(osvr_server
osvr_server.cpp
${OSVR_SERVER_RESOURCE})
target_link_libraries(osvr_server osvrServer JsonCpp::JsonCpp)
target_link_libraries(osvr_server
osvrServer
boost_program_options
boost_filesystem
JsonCpp::JsonCpp)
set_target_properties(osvr_server PROPERTIES
FOLDER "OSVR Stock Applications")
install(TARGETS osvr_server
Expand Down
91 changes: 83 additions & 8 deletions apps/osvr_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,22 @@
#include <osvr/Server/RegisterShutdownHandler.h>
#include <osvr/Util/Logger.h>
#include <osvr/Util/LogNames.h>
#include <osvr/Util/LogRegistry.h>

// Library/third-party includes
// - none
#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/optional.hpp>

// Standard includes
#include <exception>
#include <fstream>
#include <iostream>

namespace opt = boost::program_options;
namespace fs = boost::filesystem;

static osvr::server::ServerPtr server;
using ::osvr::util::log::OSVR_SERVER_LOG;

Expand All @@ -50,16 +57,84 @@ void handleShutdown() {
int main(int argc, char *argv[]) {
auto log = ::osvr::util::log::make_logger(OSVR_SERVER_LOG);

std::string configName(osvr::server::getDefaultConfigFilename());
if (argc > 1) {
configName = argv[1];
std::string configName; // server configuration filename

opt::options_description optionsAll("All Options");
opt::options_description optionsVisible("Command Line Options");
opt::positional_options_description optionsPositional;

optionsPositional.add("config", -1);
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this make the config option the default option when only one argument is passed (like when you drag a config file over the server in windows explorer)? Just making sure we don't change the current behavior in that case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. So if you run osvr_server blah.json then it will use blah.json as the configuration file. The same thing happens if you drag and drop blah.json onto the osvr_server.exe program in Windows.

optionsAll.add_options()(
"config",
opt::value<std::string>(&configName)
->default_value(osvr::server::getDefaultConfigFilename()),
"server configuration filename");
optionsVisible.add_options()
("help", "display this help message")
("verbose,v", "enable verbose logging")
("debug,d", "enable debug logging");
optionsAll.add(optionsVisible);

opt::variables_map values;
try {
opt::store(opt::command_line_parser(argc, argv)
.options(optionsAll)
.positional(optionsPositional)
.run(),
values);
opt::notify(values);
} catch (opt::invalid_command_line_syntax &e) {
log->error() << e.what();
return 1;
} catch (opt::unknown_option &e) {
log->error() << e.what(); // may want to replace boost msg
return 1;
}

if (values.count("help")) {
std::cout << optionsVisible << std::endl;
return 0;
}

if (values.count("debug")) {
osvr::util::log::LogRegistry::instance().setLevel(osvr::util::log::LogLevel::trace);
osvr::util::log::LogRegistry::instance().setConsoleLevel(osvr::util::log::LogLevel::trace);
log->trace("Debug logging enabled.");
} else if (values.count("verbose")) {
osvr::util::log::LogRegistry::instance().setLevel(osvr::util::log::LogLevel::debug);
osvr::util::log::LogRegistry::instance().setConsoleLevel(osvr::util::log::LogLevel::debug);
log->debug("Verbose logging enabled.");
}

configName = values["config"].as<std::string>();
Copy link
Contributor

Choose a reason for hiding this comment

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

what does this line do if no command line arguments are passed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@JeroMiya If no configuration file is specified, it will check to see if the file returned by osvr::server::getDefaultConfigFilename() exists and is readable. If it is, then we'll use it. Otherwise, we'll start the server with an empty { } configuration.


boost::optional<fs::path> configPath(configName);
try {
if (!fs::exists(*configPath)) {
log->warn() << "File '" << configName
<< "' not found. Using empty configuration.";
configPath = boost::none;
} else {
if (fs::is_directory(*configPath)) {
log->error() << "'" << configName << "' is a directory.";
return -1;
} else if (!fs::is_regular_file(*configPath)) {
log->error() << "'" << configName << "' is special file.";
return -1;
}
}
} catch (fs::filesystem_error &e) {
log->error() << "Could not open config file at '" << configName << "'.";
log->error() << "Reason " << e.what() << ".";
configPath = boost::none;
}

if (configPath) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm assuming this check fails if you provide an empty string to boost::optional<fs::path> configPath(configName) on line 111 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mars979 I think so, since I would expect fs::exists() to return false on an empty string.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, sounds good.

server = osvr::server::configureServerFromFile(configName);
} else {
log->info()
<< "Using default config file - pass a filename on the command "
"line to use a different one.";
server = osvr::server::configureServerFromString("{ }");
Copy link
Contributor

Choose a reason for hiding this comment

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

We need to also check for a file called osvr::server::getDefaultConfigFilename() in the current working directory, so that we don't break server auto-start and double-click behavior (i.e. starting with no arguments should load the default config file name from the CWD). Only if the config is not specified, and the default-named config file is not found in the CWD, should we be using a blank config as is done here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If no configuration file is specified on the command line, we assume the user specified the filename returned by osvr::server::getDefaultConfigFilename(). If that file exists, we'll use it. If it doesn't, then we'll start the server with an empty { } configuration.

}

server = osvr::server::configureServerFromFile(configName);
if (!server) {
return -1;
}
Expand Down
34 changes: 23 additions & 11 deletions inc/osvr/Server/ConfigureServerFromFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include <exception>
#include <fstream>
#include <iostream>
#include <sstream>

namespace osvr {
namespace server {
Expand All @@ -48,24 +49,15 @@ namespace server {
/// @brief This is the basic common code of a server app's setup, ripped out
/// of the main server app to make alternate server-acting apps simpler to
/// develop.
inline ServerPtr configureServerFromFile(std::string const &configName) {
inline ServerPtr configureServerFromString(std::string const &json) {
auto log =
::osvr::util::log::make_logger(::osvr::util::log::OSVR_SERVER_LOG);

ServerPtr ret;
log->info() << "Using config file '" << configName << "'.";
std::ifstream config(configName);
if (!config.good()) {
log->error() << "Could not open config file!";
log->error() << "Searched in the current directory; file may be "
"misspelled, missing, or in a different directory.";
return nullptr;
}

osvr::server::ConfigureServer srvConfig;
log->info() << "Constructing server as configured...";
try {
srvConfig.loadConfig(config);
srvConfig.loadConfig(json);
ret = srvConfig.constructServer();
} catch (std::exception &e) {
log->error()
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we log the std::exception error here ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mars979 We are logging the exception here. (Or are you suggesting we shouldn't log it?)

Copy link
Contributor

Choose a reason for hiding this comment

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

@godbyk , I meant to ask if log->error() automatically logs the std::exception &e or do you need to explicitly add log->error(e.what()) (or something similar)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mars979 Ah, gotcha. We're logging it a couple lines down. (clang-format's line-wrapping hinders the readability of the line.)

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, it was just wrapped to next line. My bad.

Expand Down Expand Up @@ -151,6 +143,26 @@ namespace server {
return ret;
}

/// @Brief Convenience wrapper for configureServerFromString().
inline ServerPtr configureServerFromFile(std::string const &configName) {
auto log =
::osvr::util::log::make_logger(::osvr::util::log::OSVR_SERVER_LOG);

ServerPtr ret;
log->info() << "Using config file '" << configName << "'.";
std::ifstream config(configName);
if (!config.good()) {
log->error() << "Could not open config file!";
log->error() << "Searched in the current directory; file may be "
"misspelled, missing, or in a different directory.";
return nullptr;
}

std::stringstream sstr;
sstr << config.rdbuf();
return configureServerFromString(sstr.str());
}

} // namespace server
} // namespace osvr

Expand Down
33 changes: 17 additions & 16 deletions inc/osvr/Util/LogRegistry.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
// Internal Includes
#include <osvr/Util/Log.h> // for LoggerPtr forward declaration
#include <osvr/Util/LogLevel.h>
#include <osvr/Util/Export.h>

// Library/third-party includes
// - none
Expand Down Expand Up @@ -57,12 +58,12 @@ namespace util {

class LogRegistry {
public:
LogRegistry(LogRegistry const &) = delete; // copy construct
LogRegistry(LogRegistry &&) = delete; // move construct
LogRegistry &operator=(LogRegistry const &) = delete; // copy assign
LogRegistry &operator=(LogRegistry &&) = delete; // move assign
OSVR_UTIL_EXPORT LogRegistry(LogRegistry const &) = delete; // copy construct
OSVR_UTIL_EXPORT LogRegistry(LogRegistry &&) = delete; // move construct
OSVR_UTIL_EXPORT LogRegistry &operator=(LogRegistry const &) = delete; // copy assign
OSVR_UTIL_EXPORT LogRegistry &operator=(LogRegistry &&) = delete; // move assign

static LogRegistry &instance(std::string const * = nullptr);
OSVR_UTIL_EXPORT static LogRegistry &instance(std::string const * = nullptr);

/**
* @brief Gets or creates a logger named @c logger_name.
Expand All @@ -73,7 +74,7 @@ namespace util {
* @param logger_name The name of the logger.
*
*/
LoggerPtr getOrCreateLogger(const std::string &logger_name);
OSVR_UTIL_EXPORT LoggerPtr getOrCreateLogger(const std::string &logger_name);

/**
* @brief Drops a logger from the registry.
Expand All @@ -82,47 +83,47 @@ namespace util {
* (e.g., goes out of scope). This function is useful if you want to
* destroy a logger before the program terminates.
*/
void drop(const std::string &name);
OSVR_UTIL_EXPORT void drop(const std::string &name);

/**
* @brief Removes all the registered loggers from the registry.
*
* Each logger will survive until the last copy of it is destroyed
* (e.g., goes out of scope).
*/
void dropAll();
OSVR_UTIL_EXPORT void dropAll();

/**
* @brief Flush all sinks manually.
*/
void flush();
OSVR_UTIL_EXPORT void flush();

/**
* @brief Sets the output pattern on all registered loggers.
*/
void setPattern(const std::string &pattern);
OSVR_UTIL_EXPORT void setPattern(const std::string &pattern);

/**
* @brief Sets the minimum level of messages to be logged on all
* registered loggers.
*/
void setLevel(LogLevel severity);
OSVR_UTIL_EXPORT void setLevel(LogLevel severity);

/**
* @brief Sets the minimum level of messages to be logged to the
* console.
*/
void setConsoleLevel(LogLevel severity);
OSVR_UTIL_EXPORT void setConsoleLevel(LogLevel severity);

std::string const &getLogFileBaseName() const {
OSVR_UTIL_EXPORT std::string const &getLogFileBaseName() const {
return logFileBaseName_;
}

bool couldOpenLogFile() const { return sinks_.size() > 1; }
OSVR_UTIL_EXPORT bool couldOpenLogFile() const { return sinks_.size() > 1; }

protected:
LogRegistry(std::string const &logFileBaseName);
~LogRegistry();
OSVR_UTIL_EXPORT LogRegistry(std::string const &logFileBaseName);
OSVR_UTIL_EXPORT ~LogRegistry();

private:
void setLevelImpl(LogLevel severity);
Expand Down