OSVR Framework (Internal Development Docs)  0.6-1962-g59773924
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
LogRegistry.cpp
Go to the documentation of this file.
1 
12 // Copyright 2016 Sensics, Inc.
13 //
14 // Licensed under the Apache License, Version 2.0 (the "License");
15 // you may not use this file except in compliance with the License.
16 // You may obtain a copy of the License at
17 //
18 // http://www.apache.org/licenses/LICENSE-2.0
19 //
20 // Unless required by applicable law or agreed to in writing, software
21 // distributed under the License is distributed on an "AS IS" BASIS,
22 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 // See the License for the specific language governing permissions and
24 // limitations under the License.
25 
26 // Internal Includes
27 #include <osvr/Util/LogRegistry.h>
29 
30 #include "LogDefaults.h"
31 #include "LogLevelTranslate.h"
32 #include "LogSinks.h"
33 #include "LogUtils.h"
34 
36 #include <osvr/Util/Log.h>
37 #include <osvr/Util/LogLevel.h>
38 #include <osvr/Util/LogNames.h>
39 #include <osvr/Util/Logger.h>
41 
42 // Library/third-party includes
43 #include <boost/filesystem.hpp>
44 #include <spdlog/common.h>
45 #include <spdlog/spdlog.h>
46 
47 // Standard includes
48 #include <iostream>
49 #include <utility>
50 
51 namespace osvr {
52 namespace util {
53  namespace log {
54 
55  static const auto LOG_FILE_BASENAME = "osvr";
56 
57  static const auto LOG_FILE_EXTENSION = "log";
58 
62  static inline std::string computeDefaultBasename() {
63  namespace fs = boost::filesystem;
64  auto binLoc = getBinaryLocation();
65  if (binLoc.empty()) {
66  return LOG_FILE_BASENAME;
67  }
68  auto exePath = fs::path{binLoc};
69  auto fn = exePath.filename().stem().string();
70 
71  auto sanitized = sanitizeFilenamePiece(fn);
72  if (sanitized.empty()) {
73  return LOG_FILE_BASENAME;
74  }
75 
76  return sanitized;
77  }
78 
79  LogRegistry &LogRegistry::instance(std::string const *baseNamePtr) {
80  static LogRegistry instance_{
81  baseNamePtr ? *baseNamePtr : computeDefaultBasename()};
82  return instance_;
83  }
84 
85  LoggerPtr
86  LogRegistry::getOrCreateLogger(const std::string &logger_name) {
87  // If logger already exists, return a copy of it
88  auto spd_logger = spdlog::get(logger_name);
89  if (!spd_logger) {
90  // Bummer, it didn't exist. We'll create one from scratch.
91  try {
92  spd_logger = spdlog::details::registry::instance().create(
93  logger_name, begin(sinks_), end(sinks_));
94  spd_logger->set_pattern(DEFAULT_PATTERN);
96  spd_logger->set_level(convertToLevelEnum(minLevel_));
97  spd_logger->flush_on(
98  convertToLevelEnum(DEFAULT_FLUSH_LEVEL));
99  } catch (const std::exception &e) {
100  generalPurposeLog_->error()
101  << "Caught exception attempting to create logger: "
102  << e.what();
103  generalPurposeLog_->error() << "Error creating logger. "
104  "Will only log to the "
105  "console.";
106  } catch (...) {
107  generalPurposeLog_->error() << "Error creating logger. "
108  "Will only log to the "
109  "console.";
110  }
111  }
112 
113  return Logger::makeFromExistingImplementation(logger_name,
114  spd_logger);
115  }
116 
117  void LogRegistry::drop(const std::string &logger_name) {
118  spdlog::drop(logger_name);
119  }
120 
121  void LogRegistry::dropAll() { spdlog::drop_all(); }
122 
124  for (auto &sink : sinks_) {
125  try {
126  sink->flush();
127  } catch (...) {
128  // fail silently
129  }
130  }
131  }
132 
133  void LogRegistry::setPattern(const std::string &pattern) {
134  spdlog::set_pattern(pattern.c_str());
135  }
136 
137  void LogRegistry::setLevel(LogLevel severity) {
138  if (severity > consoleLevel_) {
139  // our min level is going above our previous console level...
141  }
142  setLevelImpl(severity);
143  }
144 
145  void LogRegistry::setConsoleLevel(LogLevel severity) {
146  if (severity < minLevel_) {
149  setLevelImpl(severity);
150  }
151  setConsoleLevelImpl(severity);
152  }
153 
154  LogRegistry::LogRegistry(std::string const &logFileBaseName)
155  : minLevel_(std::min(DEFAULT_LEVEL, DEFAULT_CONSOLE_LEVEL)),
156  consoleLevel_(std::max(DEFAULT_LEVEL, DEFAULT_CONSOLE_LEVEL)),
157  logFileBaseName_(logFileBaseName) {
158  // Set default pattern and level
159  spdlog::set_pattern(DEFAULT_PATTERN);
160  spdlog::set_level(convertToLevelEnum(minLevel_));
161 
162  // Instantiate console and file sinks. These sinks will be used with
163  // each logger that is created via the registry.
164 
165 #if defined(OSVR_ANDROID)
166  // Android doesn't have a console, it has logcat.
167  // We won't filter because we won't log to file on Android.
168  auto android_sink = getDefaultUnfilteredSink();
169  sinks_.push_back(android_sink);
170  auto &main_sink = android_sink;
171 #else
172  // Console sink
173  console_filter_ =
174  getDefaultFilteredSink(convertToLevelEnum(consoleLevel_));
175  sinks_.push_back(console_filter_);
176  auto &main_sink = console_filter_;
177 #endif
178  consoleOnlyLog_ =
179  Logger::makeWithSink(OSVR_GENERAL_LOG_NAME, main_sink);
180  generalPurposeLog_ = consoleOnlyLog_.get();
181 
182  createFileSink();
183 
184  auto binLoc = getBinaryLocation();
185  if (!binLoc.empty()) {
186  generalPurposeLog_->notice("Logging for ") << binLoc;
187  }
188  }
189 
190  LogRegistry::~LogRegistry() {
191  // do nothing but flush
192  flush();
193  }
194 
195  void LogRegistry::setLevelImpl(LogLevel severity) {
196  spdlog::set_level(convertToLevelEnum(severity));
197  minLevel_ = severity;
198  }
199 
200  void LogRegistry::setConsoleLevelImpl(LogLevel severity) {
201  consoleLevel_ = severity;
202  if (console_filter_) {
203  console_filter_->set_level(convertToLevelEnum(severity));
204  }
205  }
206 
207  static inline bool shouldLogToFile() {
209  auto fileLoggingEnabled = getEnvironmentVariable("OSVR_FILE_LOGGING_ENABLED");
210  if (!fileLoggingEnabled || *fileLoggingEnabled == "0") {
211  return false;
212  }
213  return true;
214  }
215 
216  void LogRegistry::createFileSink() {
217 #if defined(OSVR_ANDROID)
218  // Not logging to file on Android.
219  return;
220 #else
221  if (!shouldLogToFile()) {
222  return;
223  }
224 
225  // File sink - rotates daily
226  std::string logDir;
227  try {
228  size_t q_size = 64; // queue size must be power of 2
229  spdlog::set_async_mode(q_size);
230  namespace fs = boost::filesystem;
231  auto base_name = fs::path(getLoggingDirectory(true));
232  if (!base_name.empty()) {
233  logDir = base_name.string();
234  base_name /= logFileBaseName_;
235  auto daily_file_sink =
236  std::make_shared<spdlog::sinks::daily_file_sink_mt>(
237  base_name.string().c_str(), LOG_FILE_EXTENSION, 0,
238  0);
239  sinks_.push_back(daily_file_sink);
240  }
241  } catch (const std::exception &e) {
242  if (consoleOnlyLog_) {
243  consoleOnlyLog_->error()
244  << "Error creating log file sink: " << e.what()
245  << ". Will log to console only.";
246  } else {
247  std::cerr
248  << "[OSVR] Error creating log file sink: " << e.what()
249  << ". Will log to console only." << std::endl;
250  }
251  return;
252  } catch (...) {
253  if (consoleOnlyLog_) {
254  consoleOnlyLog_->error(
255  "Error creating log file sink. Will log to "
256  "console only.");
257  } else {
258  std::cerr
259  << "[OSVR] Error creating log file sink. Will log to "
260  "console only."
261  << std::endl;
262  }
263  return;
264  }
265 
266  // If we succeeded in making a file sink, make a general logger that
267  // uses both sinks, then announce as much as useful about the log
268  // file location to it.
269  generalLog_ = Logger::makeWithSinks(OSVR_GENERAL_LOG_NAME,
270  {sinks_[0], sinks_[1]});
271 
272  if (generalLog_) {
273  generalPurposeLog_ = generalLog_.get();
274  } else {
275  // this shouldn't happen...
276  consoleOnlyLog_->error(
277  "Could not make a general log with both sinks!");
278  }
279 
280  generalPurposeLog_->notice() << "Log file created in " << logDir;
281  generalPurposeLog_->notice() << "Log file name starts with \""
282  << logFileBaseName_ << "\"";
283 #endif // OSVR_ANDROID
284  }
285 
286  } // end namespace log
287 } // end namespace util
288 } // end namespace osvr
LoggerPtr getOrCreateLogger(const std::string &logger_name)
Gets or creates a logger named logger_name.
Definition: LogRegistry.cpp:86
static LoggerPtr makeFromExistingImplementation(std::string const &name, std::shared_ptr< spdlog::logger > logger)
Definition: Logger.cpp:89
void setConsoleLevel(LogLevel severity)
Sets the minimum level of messages to be logged to the console.
STL namespace.
Header for basic internal log reference. To actually log to the produced loggers, include
Header.
static LoggerPtr makeWithSinks(std::string const &name, spdlog::sinks_init_list sinks)
Definition: Logger.cpp:117
void flush()
Flush all sinks manually.
static LoggerPtr makeWithSink(std::string const &name, spdlog::sink_ptr sink)
Definition: Logger.cpp:101
Auto-configured header.
Header.
Regstry to maintain instantiated loggers and global settings.
std::string getBinaryLocation()
boost::optional< std::string > getEnvironmentVariable(std::string const &var)
Gets an environment variable's value. On systems that don't distinguish between having a variable def...
Header to include for OSVR-internal usage of the logging mechanism: provides the needed definition of...
void setPattern(const std::string &pattern)
Sets the output pattern on all registered loggers.
void setLevel(LogLevel severity)
Sets the minimum level of messages to be logged on all registered loggers.
void drop(const std::string &name)
Drops a logger from the registry.
Header.
void dropAll()
Removes all the registered loggers from the registry.
Header.