Commit f870db4b authored by Paal Kvamme's avatar Paal Kvamme
Browse files

Optional timestamps and output to file for OPENZGY_VERBOSE.

parent 6d0968f0
......@@ -16,18 +16,74 @@
#include "environment.h"
#include <iostream>
#include <fstream>
#include <iomanip>
#include <mutex>
#include <atomic>
#include <memory>
#include <chrono>
namespace InternalZGY {
#if 0
}
#endif
/**
* Static class so it doesn't actually need to be attached as a pimpl.
* The methods and data is used by standardCallback().
*/
class LoggerBaseImpl
{
public:
static std::atomic<int> next_id_;
static std::mutex mutex_;
public:
static std::shared_ptr<std::ostream> getVerboseFileFromEnv(int id);
static int getVerboseDetailsFromEnv();
static double timestamp();
};
std::atomic<int> LoggerBaseImpl::next_id_{1};
std::mutex LoggerBaseImpl::mutex_{};
int
LoggerBase::getVerboseFromEnv(const char *envname)
{
return Environment::getNumericEnv(envname, 0);
}
std::shared_ptr<std::ostream>
LoggerBaseImpl::getVerboseFileFromEnv(int id)
{
std::string name = Environment::getStringEnv("OPENZGY_VERBOSE_LOGFILE");
if (!name.empty()) {
std::size_t pos = name.find("{}");
if (pos != std::string::npos)
name = name.substr(0, pos) + std::to_string(id) + name.substr(pos+2);
return std::make_shared<std::ofstream>(name, std::ofstream::app);
}
else {
return std::shared_ptr<std::ostream>(&std::cerr, [](std::ostream*){});
}
}
int
LoggerBaseImpl::getVerboseDetailsFromEnv()
{
return Environment::getNumericEnv("OPENZGY_VERBOSE_DETAILS", 0);
}
double
LoggerBaseImpl::timestamp()
{
// Remember the good old days with ::time(nullptr) ?
typedef std::chrono::high_resolution_clock clock;
static constexpr double factor =
static_cast<double>(clock::duration::period::num) /
static_cast<double>(clock::duration::period::den);
return clock::now().time_since_epoch().count() * factor;
}
/**
* Invoke the specified logger function with a string argument.
* This isn't much different from invoking the callback directly.
......@@ -77,18 +133,23 @@ LoggerBase::emptyCallback()
* Level < 0 will never show anything. Not even messages with pri < 0.
*
* Suggested use of priority levels:
* -1 => Serious errors, always show unless messages disbled completely.
* 0 => Shown during normal execution. Also in production mode.
* 1+ => For debugging and testing.
* - -1 => Serious errors, always show unless messages disbled completely.
* - 0 => Shown during normal execution. Also in production mode.
* - 1+ => For debugging and testing.
*
* By default the following environment variables are recognized:
* - OPENZGY_VERBOSE (choose how much is dumped)
* - OPENZGY_VERBOSE_LOGFILE (redirect output to a file)
* - OPENZGY_VERBOSE_DETAILS (bitmask to turn on optional features)
*
* It is the caller of standardCallback that looks up OPENZGY_VERBOSE
* and it can choose to use some other mechanism to enable or disable.
* The other two are currently local to this file and can only be set
* by these environment variables.
*
* Thread safety: May be used concurrently from different threads.
* The underlying std::cerr also allows this. There may be a visual
* issue with output from different logging statements being
* interleaved. The function tries to mitigate this by writing full
* lines at a time in the hope that only lines, not individual
* characters, might be interleaved. There is no guarantee that this
* will work. This is just a minor issue since log messages are for
* debugging only.
* Uses a global lock. The lock might be skipped is std::ios is thread
* safe, but that would technically trigger undefined behavior.
*/
LoggerBase::LoggerFn
LoggerBase::standardCallback(int currentlevel, const std::string& prefix_in, const std::string& suffix_in)
......@@ -104,13 +165,31 @@ LoggerBase::standardCallback(int currentlevel, const std::string& prefix_in, con
else {
std::string prefix(prefix_in); // Avoid capturing just a reference.
std::string suffix(suffix_in + "\n");
return [currentlevel, prefix, suffix](int pri, const std::string& msg) {
int id = LoggerBaseImpl::next_id_++;
int details = LoggerBaseImpl::getVerboseDetailsFromEnv();
std::shared_ptr<std::ostream> os =
LoggerBaseImpl::getVerboseFileFromEnv(id);
if (details & 2)
prefix = prefix + "<" + std::to_string(id) + "> ";
return [currentlevel, prefix, suffix, os, details](int pri, const std::string& msg) {
if (!msg.empty() && pri <= currentlevel) {
std::istringstream f(msg);
std::string s;
while (std::getline(f, s, '\n'))
if (!s.empty())
std::cerr << (prefix + s + suffix); // Only one <<; full lines out.
std::lock_guard<std::mutex> lk(LoggerBaseImpl::mutex_);
while (std::getline(f, s, '\n')) {
if (!s.empty()) {
if (details & 1) {
std::stringstream ss;
ss << std::setprecision(3) << std::fixed
<< LoggerBaseImpl::timestamp() << ": ";
ss << prefix << s << suffix;
*os << ss.str();
}
else {
*os << (prefix + s + suffix);
}
}
}
}
return pri <= currentlevel;
};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment