SOPT
Sparse OPTimisation
logging.cc
Go to the documentation of this file.
1 #include "sopt/logging.h"
2 #include <ctime>
3 #include <iostream>
4 #include <ostream>
5 #include <unistd.h>
6 
7 using namespace std;
8 
9 namespace sopt::logging {
10 
11 
12  thread_local Log::LogMap Log::existingLogs;
13  thread_local Log::LevelMap Log::defaultLevels;
14  bool Log::showTimestamp = false;
15  bool Log::showLogLevel = true;
16  bool Log::showLoggerName = true;
17  bool Log::useShellColors = true;
18  const int Log::end_color;
19 
20 
21  Log::Log(const string& name)
22  : _name(name), _level(info) { }
23 
24 
25  Log::Log(const string& name, int level)
26  : _name(name), _level(level) { }
27 
28 
30  void _updateLevels(const Log::LevelMap& defaultLevels, Log::LogMap& existingLogs) {
32  for (Log::LevelMap::const_iterator lev = defaultLevels.begin(); lev != defaultLevels.end(); ++lev) {
33  for (Log::LogMap::iterator log = existingLogs.begin(); log != existingLogs.end(); ++log) {
34  if (log->first.find(lev->first) == 0) {
35  log->second.setLevel(lev->second);
36  }
37  }
38  }
39  }
40 
41 
42  void Log::setLevel(const string& name, int level) {
43  defaultLevels[name] = level;
44  //cout << name << " -> " << level << '\n';
45  _updateLevels(defaultLevels, existingLogs);
46  }
47 
48 
49  void Log::setLevels(const LevelMap& logLevels) {
50  for (LevelMap::const_iterator lev = logLevels.begin(); lev != logLevels.end(); ++lev) {
51  defaultLevels[lev->first] = lev->second;
52  }
53  _updateLevels(defaultLevels, existingLogs);
54  }
55 
56 
57  Log& Log::getLog(const string& name) {
58  auto theLog = existingLogs.find(name);
59  if (theLog == existingLogs.end()) {
60  int level = info;
61  // Try running through all parent classes to find an existing level
62  string tmpname = name;
63  bool triedAllParents = false;
64  while (! triedAllParents) {
65  // Is there a default level?
66  if (defaultLevels.find(tmpname) != defaultLevels.end()) {
67  level = defaultLevels.find(tmpname)->second;
68  break;
69  }
70  // Is there already such a logger? (NB. tmpname != name in later iterations)
71  if (existingLogs.find(tmpname) != existingLogs.end()) {
72  level = existingLogs.find(tmpname)->second.getLevel();
73  break;
74  }
75  // Crop the string back to the next parent level
76  size_t lastDot = tmpname.find_last_of(".");
77  if (lastDot != string::npos) {
78  tmpname = tmpname.substr(0, lastDot);
79  } else {
80  triedAllParents = true;
81  }
82  }
83  // for (LevelMap::const_iterator l = defaultLevels.begin(); l != defaultLevels.end(); ++l) {
84  //
85  // }
86 
87  // emplace returns pair<iterator,bool>
88  auto result = existingLogs.emplace(name, Log(name, level));
89  theLog = result.first;
90  }
91  return theLog->second;
92  }
93 
94 
95  string Log::getLevelName(int level) {
96  switch(level) {
97  case trace:
98  return "trace";
99  case debug:
100  return "debug";
101  case info:
102  return "info";
103  case warn:
104  return "warn";
105  case error:
106  return "error";
107  case critical:
108  return "critical";
109  default:
110  return "";
111  }
112  }
113 
114 
115  string Log::getColorCode(int level) {
116  // Skip codes if
117  if (!Log::useShellColors) return "";
118  const static bool IS_TTY = isatty(1);
119  if (!IS_TTY) return "";
120 
121  static const ColorCodes TTY_CODES = {
122  {trace, "\033[0;36m"},
123  {debug, "\033[0;34m"},
124  {info, "\033[0;32m"},
125  {warn, "\033[0;33m"},
126  {error, "\033[0;31m"},
127  {critical, "\033[0;31m"},
128  {end_color, "\033[0m"} // end-color code
129  };
130  try {
131  return TTY_CODES.at(level);
132  } catch (...) {
133  return "";
134  }
135  }
136 
137 
138  Log::Level Log::getLevelFromName(const string& level) {
139  if (level == "trace") return trace;
140  if (level == "debug") return debug;
141  if (level == "info") return info;
142  if (level == "warn") return warn;
143  if (level == "error") return error;
144  if (level == "critical") return critical;
145  SOPT_THROW("Couldn't create a log level from string '" + level + "'");
146  }
147 
148 
149  string Log::formatMessage(int level, const string& message) {
150  string out;
151  out += getColorCode(level);
152 
153  if (Log::showLoggerName) {
154  out += getName();
155  //out += ": ";
156  }
157 
158  if (Log::showLogLevel) {
159  out += Log::getLevelName(level);
160  out += " ";
161  }
162 
163  if (Log::showTimestamp) {
164  time_t rawtime;
165  time(&rawtime);
166  char* timestr = ctime(&rawtime);
167  timestr[24] = ' ';
168  out += timestr;
169  out += " ";
170  }
171 
172  out += getColorCode(end_color);
173  out += " ";
174  out += message;
175 
176  return out;
177  }
178 
179 
180  void Log::log(int level, const string& message) {
181  if (isActive(level)) {
182  if (level > warning)
183  cerr << formatMessage(level, message) << '\n';
184  else
185  cout << formatMessage(level, message) << '\n';
186  }
187  }
188 
189 
190  ostream& operator<<(Log& log, int level) {
191  if (log.isActive(level)) {
192  if (level > Log::warning) {
193  cerr << log.formatMessage(level, "");
194  return cerr;
195  } else {
196  cout << log.formatMessage(level, "");
197  return cout;
198  }
199  } else {
200  static ostream devNull(nullptr);
201  return devNull;
202  }
203  }
204 
205 }
206 
Logging system for controlled & formatted writing to stdout.
Definition: logging.h:14
std::map< std::string, int > LevelMap
Typedef for a collection of named log levels.
Definition: logging.h:33
bool isActive(int level) const
Will this log level produce output on this logger at the moment?
Definition: logging.h:118
std::map< std::string, Log > LogMap
Typedef for a collection of named logs.
Definition: logging.h:30
int getLevel() const
Get the priority level of this logger.
Definition: logging.h:90
Level
Log priority levels.
Definition: logging.h:18
#define SOPT_THROW(MSG)
Definition: exception.h:46
Log & getLog()
Access method to default Log object.
Definition: logging.h:151
ostream & operator<<(Log &log, int level)
Streaming output to a logger must have a Log::Level/int as its first argument.
Definition: logging.cc:190
void _updateLevels(const Log::LevelMap &defaultLevels, Log::LogMap &existingLogs)
Definition: logging.cc:30