Tomographer  v1.0a
Tomographer C++ Framework Documentation
loggers.h
Go to the documentation of this file.
1 /* This file is part of the Tomographer project, which is distributed under the
2  * terms of the MIT license.
3  *
4  * The MIT License (MIT)
5  *
6  * Copyright (c) 2015 ETH Zurich, Institute for Theoretical Physics, Philippe Faist
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24  * SOFTWARE.
25  */
26 
27 #ifndef LOGGERS_H
28 #define LOGGERS_H
29 
30 #include <cassert>
31 #include <cstdio>
32 #include <cstdarg>
33 
34 #include <string>
35 #include <sstream> // std::stringstream
36 #include <iostream>
37 #include <functional> // std::function
38 #include <type_traits> // std::enable_if
39 #include <map>
40 
41 #include <boost/algorithm/string.hpp> // to_upper()
42 
43 #include <tomographer/tools/fmt.h>
45 
46 
55 #ifndef TOMOGRAPHER_PARSED_BY_DOXYGEN
56 
57 #define ENABLE_IF_Fn_CALLABLE_OSTREAM \
58  typename std::enable_if<std::is_convertible<Fn,std::function<void(std::ostream&)> >::value, void>::type
59 
60 #endif
61 
62 
63 namespace Tomographer
64 {
65 
69 namespace Logger
70 {
71 
78 {
84  ERROR = 0,
85 
93 
101 
113 
123 
124 
131 
138 };
139 
140 
141 
156 class LogLevel
157 {
158  int _level;
159 public:
161  LogLevel(int level_ = INFO) { setlevel(level_); }
163  LogLevel(const char * s) { setlevel(s); }
165  LogLevel(const std::string& s) { setlevel(s); }
166 
168  inline int level() const { return _level; }
169 
171  inline operator int() const { return _level; }
172 
174  inline std::string levelname() const
175  {
176  switch (_level) {
177  case LONGDEBUG: return std::string("LONGDEBUG");
178  case DEBUG: return std::string("DEBUG");
179  case INFO: return std::string("INFO");
180  case WARNING: return std::string("WARNING");
181  case ERROR: return std::string("ERROR");
182  default: return std::string("<INVALID LEVEL>");
183  };
184  }
185 
187  inline void setlevel(int level)
188  {
189  if (level != LONGDEBUG &&
190  level != DEBUG &&
191  level != INFO &&
192  level != WARNING &&
193  level != ERROR) {
194  throw std::invalid_argument("Invalid level code: "+std::to_string(level));
195  }
196  _level = level;
197  }
198 
200  inline void setlevel(std::string s)
201  {
202  // NOT const string `s`! we need our copy for to_upper():
203  boost::to_upper(s);
204  if (s == "LONGDEBUG") {
205  _level = LONGDEBUG;
206  } else if (s == "DEBUG") {
207  _level = DEBUG;
208  } else if (s == "INFO") {
209  _level = INFO;
210  } else if (s == "WARNING") {
211  _level = WARNING;
212  } else if (s == "ERROR") {
213  _level = ERROR;
214  } else {
215  throw std::invalid_argument("Invalid log level: '"+s+"'");
216  }
217  }
218 };
219 
222 {
223  std::string s;
224  str >> s;
225  l.setlevel(s);
226  return str;
227 }
229 inline std::ostream & operator<<(std::ostream & str, const LogLevel &l)
230 {
231  return str << l.levelname();
232 }
233 
234 
235 
246 inline bool is_at_least_of_severity(int level, int baselevel)
247 {
248  return (level <= baselevel);
249 }
250 
258 template<int Level, int BaseLevel>
260  enum {
261  value = (Level <= BaseLevel)
262  };
263 };
264 
265 
287 {
288  enum {
294  IsThreadSafe = 0,
295 
308  StaticMinimumSeverityLevel = LOWEST_SEVERITY_LEVEL,
309 
321  HasOwnGetLevel = 0,
322 
335  HasFilterByOrigin = 0
336  };
337 };
338 
348 template<typename LoggerType>
350 {
351  // by default, contains nothing and will produce errors if used unspecialized.
352 };
353 
354 
355 
356 namespace tomo_internal {
360 template<bool hasOwnGetLevel>
361 class LoggerRuntimeLevel {
362 public:
363  LoggerRuntimeLevel(int level)
364  : _level(level)
365  {
366  }
367 
368  inline int level() const
369  {
370  return _level;
371  }
372 
373 protected:
374  inline void setLogLevel(int level)
375  {
376  _level = level;
377  }
378 
379 private:
380  int _level;
381 };
382 //
383 // Specialization for those classes which provide their own level() method. Do nothing.
384 //
385 template<>
386 class LoggerRuntimeLevel<true> {
387 public:
388  LoggerRuntimeLevel(int /*level*/)
389  {
390  }
391 
392  inline void setLogLevel(int) {
393  assert(0 && "Call to LoggerRuntimeLevel::setLogLevel() for Logger which defines HasOwnGetLevel=1!");
394  }
395 };
396 }
397 
398 
423 template<typename Derived>
425 #ifndef TOMOGRAPHER_PARSED_BY_DOXYGEN
426  : public tomo_internal::LoggerRuntimeLevel<LoggerTraits<Derived>::HasOwnGetLevel>
427 #endif
428 {
429 public:
432  enum {
441  };
442 
452  LoggerBase(int level_ = INFO)
453  : tomo_internal::LoggerRuntimeLevel<HasOwnGetLevel>(level_)
454  {
455  }
456 
465  inline void error(const char * origin, const char * fmt, ...);
473  inline void error(const char * origin, const std::string & msg);
480  template<typename Fn>
481  inline ENABLE_IF_Fn_CALLABLE_OSTREAM
482  error(const char * origin, Fn f);
483 
492  inline void warning(const char * origin, const char * fmt, ...);
500  inline void warning(const char * origin, const std::string & msg);
507  template<typename Fn>
508  inline ENABLE_IF_Fn_CALLABLE_OSTREAM
509  warning(const char * origin, Fn f);
510 
519  inline void info(const char * origin, const char * fmt, ...);
527  inline void info(const char * origin, const std::string & msg);
534  template<typename Fn>
535  inline ENABLE_IF_Fn_CALLABLE_OSTREAM
536  info(const char * origin, Fn f);
537 
556  inline void debug(const char * origin, const char * fmt, ...);
557 
574  inline void debug(const char * origin, const std::string & msg);
603  template<typename Fn>
604  inline ENABLE_IF_Fn_CALLABLE_OSTREAM
605  debug(const char * origin, Fn f);
606 
615  inline void longdebug(const char * origin, const char * fmt, ...);
623  inline void longdebug(const char * origin, const std::string & msg);
630  template<typename Fn>
631  inline ENABLE_IF_Fn_CALLABLE_OSTREAM
632  longdebug(const char * origin, Fn f);
633 
634 
645  inline void log(int level, const char * origin, const char * fmt, ...);
655  inline void log(int level, const char * origin, const std::string & msg);
665  template<typename Fn>
666  inline ENABLE_IF_Fn_CALLABLE_OSTREAM
667  log(int level, const char * origin, Fn f);
668 
669 
670 
679  template<int Level>
681  inline void log(const char * origin, const char * fmt, ...);
691  template<int Level>
692  inline void log(const char * origin, const char * fmt, va_list ap);
701  template<int Level>
702  inline void log(const char * origin, const std::string & msg);
711  template<int Level, typename Fn>
712  inline ENABLE_IF_Fn_CALLABLE_OSTREAM
713  log(const char * origin, Fn f);
714 
715 
728  static inline bool statically_enabled_for(int level)
729  {
730  return ( is_at_least_of_severity(level, StaticMinimumSeverityLevel) );
731  }
732 
736  template<int Level>
737  static inline bool statically_enabled_for()
738  {
740  Level,
741  StaticMinimumSeverityLevel
742  >::value ) ;
743  }
744 
755  inline bool enabled_for(int level_) const
756  {
757  return derived()->statically_enabled_for(level_) &&
758  is_at_least_of_severity(level_, getLevel());
759  };
760 
761 #ifdef TOMOGRAPHER_PARSED_BY_DOXYGEN
762  // this method is actually implemented by the base class LoggerRuntimeLevel, and is only
763  // exposed in the case where the logger doesn't define its own method. This is important
764  // to avoid "ambigous calls to `level()`".
765 
777  inline int level() const
778  {
779  }
780 #endif
781 
782 
783 protected:
784 
785  // version in case we store our own level
786  template<bool dummy = true,
788  inline int getLevel() const
789  {
790  // use base class' implementation
791  return tomo_internal::LoggerRuntimeLevel<HasOwnGetLevel>::level();
792  }
793  // version in case we delegate to derived class' level()
794  template<bool dummy = true,
796  inline int getLevel() const
797  {
798  return derived()->level();
799  }
800 
801 
802  inline Derived * derived()
803  {
804  return static_cast<Derived*>(this);
805  }
806  inline const Derived * derived() const
807  {
808  return static_cast<const Derived*>(this);
809  }
810 
811 #ifdef TOMOGRAPHER_PARSED_BY_DOXYGEN
812  // this method is actually implemented by the base class LoggerRuntimeLevel, and is only
813  // exposed in the case where the logger can actually set a new level.
814 
828  inline void setLogLevel(int level)
829  {
830  }
831 #endif
832 
833 private:
834 };
835 
836 
837 namespace tomo_internal {
840  template<typename Derived, bool derivedHasFilterByOrigin>
841  struct LoggerBaseHelperFilterByOrigin {
842  static inline bool test_logger_filter(LoggerBase<Derived> * /*loggerbase*/, int /*level*/,
843  const char * /*origin*/)
844  {
845  // do nothing by default
846  return true;
847  }
848  };
849  //
850  // specialization for loggers with filtering by origin
851  //
852  template<typename Derived>
853  struct LoggerBaseHelperFilterByOrigin<Derived, true> {
854  static inline bool test_logger_filter(LoggerBase<Derived> * loggerbase, int level,
855  const char * origin)
856  {
857  return static_cast<const Derived*>(loggerbase)->filter_by_origin(level, origin);
858  }
859  };
865  template<typename Derived>
866  struct LoggerBaseHelperDynamic {
867  static inline void call_emit_log(LoggerBase<Derived> * loggerbase, int level, const char * origin,
868  const std::string & msg) throw()
869  {
870  try {
871  //printf("Calling emit_log(%d,\"%s\",\"%s\") on object %p\n", level, origin, msg.c_str(), loggerbase);
872  static_cast<Derived*>(loggerbase)->emit_log(level, origin, msg);
873  } catch (const std::exception & e) {
874  std::fprintf(stderr,
875  "Warning in LoggerBaseHelperDynamic::call_emit_log(%d, \"%s\", msg):"
876  " Exception caught: %s\n",
877  level, origin, e.what());
878  }
879  }
880  static inline bool test_should_emit(LoggerBase<Derived> * loggerbase, int level, const char * origin)
881  {
882  if ( ! static_cast<const Derived*>(loggerbase)->enabled_for(level) ) {
883  return false;
884  }
885  if ( ! LoggerBaseHelperFilterByOrigin<Derived, LoggerTraits<Derived>::HasFilterByOrigin>
886  ::test_logger_filter(loggerbase, level, origin) ) {
887  return false;
888  }
889  return true;
890  }
891 
892  static inline void test_and_call_emit_log(LoggerBase<Derived> * loggerbase, int level, const char * origin,
893  const std::string & msg) throw()
894  {
895  if ( ! test_should_emit(loggerbase, level, origin) ) {
896  return;
897  }
898 
899  call_emit_log(loggerbase, level, origin, msg);
900  }
901  static inline void test_and_call_emit_log(LoggerBase<Derived> * loggerbase, int level, const char * origin,
902  const char * fmt, va_list ap) throw()
903  {
904  if ( ! test_should_emit(loggerbase, level, origin) ) {
905  return;
906  }
907 
908  try {
909  const std::string msg = Tools::vfmts(fmt, ap);
910  call_emit_log(loggerbase, level, origin, msg);
911  } catch (const std::exception & e) {
912  std::fprintf(stderr,
913  "Warning in LoggerBase::test_and_call_emit_log(%d, \"%s\", \"%s\", ...):"
914  " Exception caught for vfmts(): %s\n",
915  level, origin, fmt, e.what());
916  }
917  }
918  template<typename Fn>
919  static inline
920  ENABLE_IF_Fn_CALLABLE_OSTREAM
921  test_and_call_emit_log(LoggerBase<Derived> * loggerbase, int level, const char * origin, Fn f) throw()
922  {
923  if ( ! test_should_emit(loggerbase, level, origin) ) {
924  return;
925  }
926 
927  try {
928  std::ostringstream sstr;
929  f(sstr);
930  call_emit_log(loggerbase, level, origin, sstr.str());
931  } catch (const std::exception & e) {
932  std::fprintf(stderr, "Warning in LoggerBase::test_and_call_emit_log(%d, \"%s\", f(ostream)):"
933  " Exception caught: %s\n",
934  level, origin, e.what());
935  }
936  }
937  };
938 
945  template<typename Derived, int Level, bool isStaticallyDiscarded = false>
946  struct LoggerBaseHelperStatic2 {
947  template<typename... Args>
948  static inline void test_and_call_emit_log(LoggerBase<Derived> * loggerbase, const char * origin,
949  Args... args) throw()
950  {
951  LoggerBaseHelperDynamic<Derived>::test_and_call_emit_log(loggerbase, Level, origin, args...);
952  }
953  };
954  // specialization for calls where the message should be statically discarded
955  template<typename Derived, int Level>
956  struct LoggerBaseHelperStatic2<Derived, Level, true> {
957  template<typename... Args>
958  static inline void test_and_call_emit_log(LoggerBase<Derived> *, const char *, Args...) throw()
959  {
960  // discard logging message.
961  }
962  };
963 
967  template<typename Derived, int Level>
968  struct LoggerBaseHelperStatic {
969  template<typename... Args>
970  static inline void test_and_call_emit_log(LoggerBase<Derived> * loggerbase, const char * origin,
971  Args... args) throw()
972  {
973  // call the default or specialized version of our second helper, which will either
974  // relay the call the dynamic version or discard the message.
975  //std::fprintf(stderr,
976  // "LoggerBaseHelperStatic<Derived,Level=%d>::test_and_call_emit_log(...). StaticMinimumSeverityLevel=%d\n",
977  // (int)Level, (int)(LoggerTraits<Derived>::StaticMinimumSeverityLevel));
978  LoggerBaseHelperStatic2<
979  Derived, Level,
980  ! static_is_at_least_of_severity<Level, LoggerTraits<Derived>::StaticMinimumSeverityLevel>::value
981  >::test_and_call_emit_log(
982  loggerbase, origin, args...
983  );
984  }
985  };
986 
987 
988 } // namespace tomo_internal
989 
990 
991 template<typename Derived>
992 inline void LoggerBase<Derived>::error(const char * origin, const char * fmt, ...)
993 {
994  va_list ap;
995  va_start(ap, fmt);
996  derived()->template log<ERROR>(origin, fmt, ap);
997  va_end(ap);
998 }
999 
1000 template<typename Derived>
1001 inline void LoggerBase<Derived>::error(const char * origin, const std::string & msg)
1002 {
1003  derived()->template log<ERROR>(origin, msg);
1004 }
1005 
1006 template<typename Derived>
1007 template<typename Fn>
1008 inline ENABLE_IF_Fn_CALLABLE_OSTREAM
1009 LoggerBase<Derived>::error(const char * origin, Fn f)
1010 {
1011  derived()->template log<ERROR>(origin, f);
1012 }
1013 
1014 
1015 template<typename Derived>
1016 inline void LoggerBase<Derived>::warning(const char * origin, const char * fmt, ...)
1017 {
1018  va_list ap;
1019  va_start(ap, fmt);
1020  derived()->template log<WARNING>(origin, fmt, ap);
1021  va_end(ap);
1022 }
1023 
1024 template<typename Derived>
1025 inline void LoggerBase<Derived>::warning(const char * origin, const std::string & msg)
1026 {
1027  derived()->template log<WARNING>(origin, msg);
1028 }
1029 
1030 template<typename Derived>
1031 template<typename Fn>
1032 inline ENABLE_IF_Fn_CALLABLE_OSTREAM
1033 LoggerBase<Derived>::warning(const char * origin, Fn f)
1034 {
1035  derived()->template log<WARNING>(origin, f);
1036 }
1037 
1038 template<typename Derived>
1039 inline void LoggerBase<Derived>::info(const char * origin, const char * fmt, ...)
1040 {
1041  va_list ap;
1042  va_start(ap, fmt);
1043  derived()->template log<INFO>(origin, fmt, ap);
1044  va_end(ap);
1045 }
1046 
1047 template<typename Derived>
1048 inline void LoggerBase<Derived>::info(const char * origin, const std::string & msg)
1049 {
1050  derived()->template log<INFO>(origin, msg);
1051 }
1052 
1053 template<typename Derived>
1054 template<typename Fn>
1055 inline ENABLE_IF_Fn_CALLABLE_OSTREAM
1056 LoggerBase<Derived>::info(const char * origin, Fn f)
1057 {
1058  derived()->template log<INFO>(origin, f);
1059 }
1060 
1061 template<typename Derived>
1062 inline void LoggerBase<Derived>::debug(const char * origin, const char * fmt, ...)
1063 {
1064  va_list ap;
1065  va_start(ap, fmt);
1066  derived()->template log<DEBUG>(origin, fmt, ap);
1067  va_end(ap);
1068 }
1069 
1070 template<typename Derived>
1071 inline void LoggerBase<Derived>::debug(const char * origin, const std::string & msg)
1072 {
1073  derived()->template log<DEBUG>(origin, msg);
1074 }
1075 
1076 template<typename Derived>
1077 template<typename Fn>
1078 inline ENABLE_IF_Fn_CALLABLE_OSTREAM
1079 LoggerBase<Derived>::debug(const char * origin, Fn f)
1080 {
1081  derived()->template log<DEBUG>(origin, f);
1082 }
1083 
1084 template<typename Derived>
1085 inline void LoggerBase<Derived>::longdebug(const char * origin, const char * fmt, ...)
1086 {
1087  va_list ap;
1088  va_start(ap, fmt);
1089  derived()->template log<LONGDEBUG>(origin, fmt, ap);
1090  va_end(ap);
1091 }
1092 
1093 template<typename Derived>
1094 inline void LoggerBase<Derived>::longdebug(const char * origin, const std::string & msg)
1095 {
1096  derived()->template log<LONGDEBUG>(origin, msg);
1097 }
1098 
1099 template<typename Derived>
1100 template<typename Fn>
1101 inline ENABLE_IF_Fn_CALLABLE_OSTREAM
1102 LoggerBase<Derived>::longdebug(const char * origin, Fn f)
1103 {
1104  derived()->template log<LONGDEBUG>(origin, f);
1105 }
1106 
1107 
1108 template<typename Derived>
1109 inline void LoggerBase<Derived>::log(int level, const char * origin, const char * fmt, ...)
1110 {
1111  va_list ap;
1112  va_start(ap, fmt);
1113  tomo_internal::LoggerBaseHelperDynamic<Derived>::test_and_call_emit_log(this, level, origin, fmt, ap);
1114  va_end(ap);
1115 }
1116 
1117 template<typename Derived>
1118 inline void LoggerBase<Derived>::log(int level, const char * origin, const std::string & msg)
1119 {
1120  tomo_internal::LoggerBaseHelperDynamic<Derived>::test_and_call_emit_log(this, level, origin, msg);
1121 }
1122 
1123 template<typename Derived>
1124 template<typename Fn>
1125 inline ENABLE_IF_Fn_CALLABLE_OSTREAM
1126 LoggerBase<Derived>::log(int level, const char * origin, Fn f)
1127 {
1128  tomo_internal::LoggerBaseHelperDynamic<Derived>::test_and_call_emit_log(this, level, origin, f);
1129 }
1130 
1131 
1132 template<typename Derived>
1133 template<int Level>
1134 inline void LoggerBase<Derived>::log(const char * origin, const char * fmt, ...)
1135 {
1136  va_list ap;
1137  va_start(ap, fmt);
1138  tomo_internal::LoggerBaseHelperStatic<Derived, Level>::test_and_call_emit_log(this, origin, fmt, ap);
1139  va_end(ap);
1140 }
1141 
1142 template<typename Derived>
1143 template<int Level>
1144 inline void LoggerBase<Derived>::log(const char * origin, const char * fmt, va_list ap)
1145 {
1146  tomo_internal::LoggerBaseHelperStatic<Derived, Level>::test_and_call_emit_log(this, origin, fmt, ap);
1147 }
1148 
1149 template<typename Derived>
1150 template<int Level>
1151 inline void LoggerBase<Derived>::log(const char * origin, const std::string & msg)
1152 {
1153  tomo_internal::LoggerBaseHelperStatic<Derived, Level>::test_and_call_emit_log(this, origin, msg);
1154 }
1155 
1156 template<typename Derived>
1157 template<int Level, typename Fn>
1158 inline ENABLE_IF_Fn_CALLABLE_OSTREAM
1159 LoggerBase<Derived>::log(const char * origin, Fn f)
1160 {
1161  tomo_internal::LoggerBaseHelperStatic<Derived, Level>::test_and_call_emit_log(this, origin, f);
1162 }
1163 
1164 
1165 
1166 
1167 class FileLogger;
1172 template<>
1174 {
1176  enum {
1177  IsThreadSafe = 1
1178  };
1179 };
1180 
1191 class FileLogger : public LoggerBase<FileLogger>
1192 {
1193 public:
1194  FileLogger(FILE * fp_, int level = INFO, bool display_origin_ = true)
1195  : LoggerBase<FileLogger>(level), fp(fp_), display_origin(display_origin_)
1196  {
1197  }
1198 
1202  inline void setFp(FILE * fp_)
1203  {
1204  fp = fp_;
1205  }
1206 
1210  inline void setLevel(int level)
1211  {
1212  setLogLevel(level);
1213  }
1214 
1218  inline void setDisplayOrigin(bool display_origin_)
1219  {
1220  display_origin = display_origin_;
1221  }
1222 
1223  inline void emit_log(int level, const char * origin, const std::string & msg)
1224  {
1225  static const std::string level_prefixes[] = {
1226  std::string("\n\n*** ERROR -- "),
1227  std::string("\n*** Warning: ")
1228  };
1229 
1230  std::string finalmsg = (
1231  ( (level >= 0 && level < (int)std::extent<decltype(level_prefixes)>::value)
1232  ? level_prefixes[level] : std::string() )
1233  + ((display_origin && origin && origin[0]) ? "["+std::string(origin)+"] " : std::string())
1234  + msg
1235  );
1236 
1237  // display the log message
1238  std::fprintf(fp, "%s\n", finalmsg.c_str());
1239 
1240  // force output also on stderr for warnings and errors if we are being redirected to a file
1241  if (fp != stdout && fp != stderr && level <= WARNING) {
1242  std::fprintf(stderr, "%s\n", finalmsg.c_str());
1243  }
1244  }
1245 
1246 private:
1247  std::FILE * fp;
1248  bool display_origin;
1249 };
1250 
1251 
1252 
1253 
1254 
1255 class VacuumLogger;
1260 template<>
1262 {
1264  enum {
1265  IsThreadSafe = 1,
1266  StaticMinimumSeverityLevel = -1
1267  };
1268 };
1269 
1274 class VacuumLogger : public LoggerBase<VacuumLogger>
1275 {
1276 public:
1277  inline void emit_log(int /*level*/, const char * /*origin*/, const std::string & /*msg*/)
1278  {
1279  }
1280 
1281 };
1282 
1288 static VacuumLogger vacuum_logger;
1289 
1290 
1291 
1292 
1293 class BufferLogger;
1298 template<>
1300 {
1302  enum {
1303  IsThreadSafe = 0
1304  };
1305 };
1306 
1312 class BufferLogger : public LoggerBase<BufferLogger>
1313 {
1314  std::ostringstream buffer;
1315 public:
1316  BufferLogger(int level)
1317  : LoggerBase<BufferLogger>(level)
1318  {
1319  }
1320 
1321  inline void emit_log(int /*level*/, const char * origin, const std::string& msg)
1322  {
1323  buffer << (origin&&origin[0] ? "["+std::string(origin)+"] " : std::string())
1324  << msg.c_str() << "\n";
1325  }
1326 
1331  inline void setLevel(int level)
1332  {
1333  setLogLevel(level);
1334  }
1335 
1341  inline void clear()
1342  {
1343  buffer.clear();
1344  buffer.str(std::string());
1345  }
1346 
1351  inline std::string get_contents() const
1352  {
1353  return buffer.str();
1354  }
1355 };
1356 
1357 
1358 
1359 
1360 template<typename, int> class MinimumSeverityLogger;
1364 template<typename BaseLogger, int Level>
1365 struct LoggerTraits<MinimumSeverityLogger<BaseLogger,Level> > : public LoggerTraits<BaseLogger>
1366 {
1368  enum {
1369 
1371  StaticMinimumSeverityLevel = Level,
1372 
1374  HasOwnGetLevel = 1
1375 
1376  // Note: filter by origin flag is inherited from base logger.
1377  };
1378 };
1379 
1389 template<typename BaseLogger, int Level>
1390 class MinimumSeverityLogger : public LoggerBase<MinimumSeverityLogger<BaseLogger,Level> >
1391 {
1393  BaseLogger & baselogger;
1394 public:
1400  MinimumSeverityLogger(BaseLogger & baselogger_)
1401  : LoggerBase<MinimumSeverityLogger<BaseLogger,Level> >(), baselogger(baselogger_)
1402  {
1403  }
1404 
1406  inline void emit_log(int level, const char * origin, const std::string& msg)
1407  {
1408  baselogger.emit_log(level, origin, msg);
1409  }
1410 
1412  inline int level() const
1413  {
1414  return baselogger.level();
1415  }
1416 
1418  template<bool dummy = true>
1420  filter_by_origin(int level, const char * origin)
1421  {
1422  return baselogger.filter_by_origin(level, origin);
1423  }
1424 };
1425 
1426 
1427 namespace tomo_internal {
1428 
1434 inline int matched_length(const std::string & s, const std::string & pattern)
1435 {
1436  std::size_t k = 0;
1437  while (s[k] == pattern[k]) {
1438  ++k;
1439  }
1440  return k;
1441 }
1442 
1443 } // namespace tomo_internal
1444 
1445 template<typename BaseLogger> class OriginFilteredLogger;
1449 template<typename BaseLogger>
1450 struct LoggerTraits<OriginFilteredLogger<BaseLogger> > : public LoggerTraits<BaseLogger>
1451 {
1453  enum {
1454  HasOwnGetLevel = 1,
1455  HasFilterByOrigin = 1
1456  };
1457 };
1458 
1479 template<typename BaseLogger>
1480 class OriginFilteredLogger : public Tomographer::Logger::LoggerBase<OriginFilteredLogger<BaseLogger> >
1481 {
1483  BaseLogger & baselogger;
1484 
1486  std::map<std::string,int> levels_set;
1487 public:
1488 
1494  OriginFilteredLogger(BaseLogger & baselogger_)
1495  : Tomographer::Logger::LoggerBase<OriginFilteredLogger<BaseLogger> >(),
1496  baselogger(baselogger_),
1497  levels_set()
1498  {
1499  }
1500 
1510  inline void setDomainLevel(const std::string& origin_pattern, int level)
1511  {
1512  levels_set[origin_pattern] = level;
1513  }
1514 
1520  inline void removeDomainSetting(const std::string& s)
1521  {
1522  auto it = levels_set.find(s);
1523  if (it == levels_set.end()) {
1524  this->warning("OriginFilteredLogger<BaseLogger>::removeDomainSetting", "domain not set: `%s'", s.c_str());
1525  return;
1526  }
1527  levels_set.erase(it);
1528  }
1529 
1538  inline int level() const
1539  {
1540  return LOWEST_SEVERITY_LEVEL;
1541  }
1542 
1547  inline void emit_log(int level, const char * origin, const std::string& msg)
1548  {
1549  baselogger.emit_log(level, origin, msg);
1550  }
1551 
1559  inline bool filter_by_origin(int level, const char * origin) const
1560  {
1561  typedef std::map<std::string, int>::const_iterator ConstIterator;
1562 
1563  std::string s(origin);
1564 
1565  int loglevel = -1;
1566  int last_matched_length = 0;
1567  for (ConstIterator it = levels_set.begin(); it != levels_set.end(); ++it) {
1568  const int mlen = tomo_internal::matched_length((*it).first, s);
1569  if (mlen > last_matched_length) {
1570  loglevel = (*it).second;
1571  last_matched_length = mlen;
1572  }
1573  }
1574  if (loglevel == -1) {
1575  // default logger level
1576  loglevel = baselogger.level();
1577  }
1578  return is_at_least_of_severity(level, loglevel);
1579  }
1580 
1581 };
1582 
1583 
1584 
1585 
1586 
1587 // --------------------------------------------------
1588 
1589 
1590 
1591 
1592 
1601 
1604  : origin_prefix(s), origin_prefix_add(""), glue(gl) { }
1606  constexpr LocalLoggerOriginSpec(const Tools::conststr& s, const Tools::conststr& s2, const Tools::conststr& gl)
1607  : origin_prefix(s), origin_prefix_add(s2), glue(gl) { }
1608 };
1609 
1610 namespace tomo_internal {
1612 struct extractTomoOrigin_helper {
1613  static inline constexpr LocalLoggerOriginSpec step2(const Tools::conststr fn, std::size_t last_doublecolons,
1614  std::size_t after_prelast_doublecolons)
1615  {
1616  return ( fn.substr_e(after_prelast_doublecolons, last_doublecolons) == fn.substr(last_doublecolons+2)
1617  // fn is a constructor, so keep class name and use "::" as glue
1618  ? LocalLoggerOriginSpec(fn.substr(last_doublecolons+2), "::")
1619  // looks like a method name. Strip off the class name. Also use an internal
1620  // glue to indicate a logical level.
1621  : LocalLoggerOriginSpec(fn.substr(last_doublecolons+2), "()", "/")
1622  );
1623  }
1624  static inline constexpr std::size_t afterprelast_doublecolons(std::size_t prelast_doublecolons_found)
1625  {
1626  return (prelast_doublecolons_found == std::string::npos) ? 0 : prelast_doublecolons_found+2;
1627  }
1628  static inline constexpr LocalLoggerOriginSpec step1(const Tools::conststr fn, std::size_t last_doublecolons)
1629  {
1630  return last_doublecolons == std::string::npos || last_doublecolons == 0
1631  ? LocalLoggerOriginSpec(fn, "()", "/") // looks like simple function name with no parent scope
1632  : step2(fn, last_doublecolons, afterprelast_doublecolons(fn.rfind("::", last_doublecolons-1)));
1633  }
1634 
1635  static inline constexpr LocalLoggerOriginSpec extract_from_func_name(const Tools::conststr fn)
1636  {
1637  return step1(fn, fn.rfind("::"));
1638  }
1639 };
1640 
1642 constexpr const LocalLoggerOriginSpec extractTomoOrigin(const Tools::conststr fn)
1643 {
1644  return extractTomoOrigin_helper::extract_from_func_name(Tomographer::Tools::extractFuncName(fn));
1645 }
1646 
1647 } // namespace tomo_internal
1648 
1649 
1652 #define TOMO_ORIGIN Tomographer::Logger::tomo_internal::extractTomoOrigin(TOMO_FUNCTION)
1653 
1654 
1655 
1656 
1657 template<typename BaseLoggerType_> class LocalLogger;
1658 
1660 template<typename BaseLoggerType_>
1661 struct LoggerTraits<LocalLogger<BaseLoggerType_> > : LoggerTraits<BaseLoggerType_>
1662 {
1663  enum {
1665  HasOwnGetLevel = 1
1666  };
1667 };
1668 
1669 
1715 template<typename BaseLoggerType_>
1716 class LocalLogger : public Tomographer::Logger::LoggerBase<LocalLogger<BaseLoggerType_> >
1717 {
1718 public:
1719  typedef BaseLoggerType_ BaseLoggerType;
1720 
1721 private:
1723 
1724  const std::string _origin_prefix;
1725  const std::string _glue;
1726 
1727  BaseLoggerType & _baselogger;
1728 
1729 public:
1730  LocalLogger(const std::string & origin_fn_name, BaseLoggerType & logger_)
1731  : _origin_prefix(origin_fn_name), _glue("::"), _baselogger(logger_)
1732  {
1733  }
1734  LocalLogger(const std::string & origin_prefix, const std::string & glue, BaseLoggerType & logger_)
1735  : _origin_prefix(origin_prefix), _glue(glue), _baselogger(logger_)
1736  {
1737  }
1738  LocalLogger(const LocalLoggerOriginSpec & spec, BaseLoggerType & logger_)
1739  : _origin_prefix(spec.origin_prefix.to_string()+spec.origin_prefix_add.to_string()),
1740  _glue(spec.glue.to_string()), _baselogger(logger_)
1741  {
1742  }
1743 
1744  inline std::string origin_prefix() const { return _origin_prefix; }
1745  inline std::string glue() const { return _glue; }
1746 
1747  inline BaseLoggerType & baselogger() { return _baselogger; };
1748 
1749  inline LocalLogger<LocalLogger<BaseLoggerType> > sublogger(const std::string & new_prefix)
1750  {
1751  return LocalLogger<LocalLogger<BaseLoggerType> >(new_prefix, *this);
1752  }
1753  inline LocalLogger<LocalLogger<BaseLoggerType> > sublogger(const std::string & new_prefix,
1754  const std::string & new_glue)
1755  {
1756  return LocalLogger<LocalLogger<BaseLoggerType> >(new_prefix, new_glue, *this);
1757  }
1758  inline LocalLogger<LocalLogger<BaseLoggerType> > sublogger(const LocalLoggerOriginSpec & spec)
1759  {
1760  return LocalLogger<LocalLogger<BaseLoggerType> >(spec, *this);
1761  }
1762 
1763  PRINTF2_ARGS_SAFE inline void longdebug(const char * fmt, ...)
1764  { va_list ap; va_start(ap, fmt); log<LONGDEBUG>(fmt, ap); va_end(ap); }
1765  PRINTF2_ARGS_SAFE inline void debug(const char * fmt, ...)
1766  { va_list ap; va_start(ap, fmt); log<DEBUG>(fmt, ap); va_end(ap); }
1767  PRINTF2_ARGS_SAFE inline void info(const char * fmt, ...)
1768  { va_list ap; va_start(ap, fmt); log<INFO>(fmt, ap); va_end(ap); }
1769  PRINTF2_ARGS_SAFE inline void warning(const char * fmt, ...)
1770  { va_list ap; va_start(ap, fmt); log<WARNING>(fmt, ap); va_end(ap); }
1771  PRINTF2_ARGS_SAFE inline void error(const char * fmt, ...)
1772  { va_list ap; va_start(ap, fmt); log<ERROR>(fmt, ap); va_end(ap); }
1773 
1774  template<typename... Args>
1775  inline void longdebug(Args... a) { log<Tomographer::Logger::LONGDEBUG>(a...); }
1776  template<typename... Args>
1777  inline void debug(Args... a) { log<Tomographer::Logger::DEBUG>(a...); }
1778  template<typename... Args>
1779  inline void info(Args... a) { log<Tomographer::Logger::INFO>(a...); }
1780  template<typename... Args>
1781  inline void warning(Args... a) { log<Tomographer::Logger::WARNING>(a...); }
1782  template<typename... Args>
1783  inline void error(Args... a) { log<Tomographer::Logger::ERROR>(a...); }
1784 
1785  template<int Level, typename... Args>
1786  inline void log(Args... args)
1787  {
1788  Base_::template log<Level>("", args...);
1789  }
1790 
1791 
1792  // relay calls to base logger
1793 
1794  inline std::string get_origin(const char * origin) const
1795  {
1796  return ( origin == NULL || origin[0] == 0
1797  ? _origin_prefix
1798  : _origin_prefix + _glue + origin );
1799  }
1800 
1802  inline void emit_log(int level, const char * origin, const std::string& msg)
1803  {
1804  // this might also be called if we have a sublogger. In that case, if we have a
1805  // sublogger, then use their prefix.
1806  _baselogger.emit_log(level, get_origin(origin).c_str(), msg);
1807  }
1808 
1810  inline int level() const
1811  {
1812  return _baselogger.level();
1813  }
1814 
1817  inline bool filter_by_origin(int level, const char * origin)
1818  {
1819  return _baselogger.filter_by_origin(level, get_origin(origin).c_str());
1820  }
1821 };
1822 
1823 
1824 
1825 
1826 
1827 } // namespace Logger
1828 } // namespace Tomographer
1829 
1830 
1831 
1832 #endif
Utilities for formatting strings.
Simple logger class which logs everything into a given FILE pointer.
Definition: loggers.h:1191
void setlevel(std::string s)
Set the level to the given level name. See class doc.
Definition: loggers.h:200
Local logger: avoid having to repeat origin at each emitted message.
Definition: loggers.h:1657
Lowest severity possible.
Definition: loggers.h:137
Log messages into an internal memory buffer.
Definition: loggers.h:1312
Base namespace for the Tomographer project.
Definition: dmmhrw.h:51
constexpr LocalLoggerOriginSpec(const Tools::conststr &s, const Tools::conststr &s2, const Tools::conststr &gl)
complete constructor.
Definition: loggers.h:1606
void clear()
Clears the internal memory buffer.
Definition: loggers.h:1341
bool filter_by_origin(int level, const char *origin) const
Message filtering by origin implementation.
Definition: loggers.h:1559
void setDomainLevel(const std::string &origin_pattern, int level)
Set a rule to log messages based on their origin.
Definition: loggers.h:1510
std::string vfmts(const char *fmt, va_list vl)
printf- formatting to a std::string, with va_list pointer
Definition: fmt.h:85
int level() const
Get the log level set for this logger.
Definition: loggers.h:777
T log(T...args)
void setlevel(int level)
Set the level to the given level code. See class doc and LogLevelCode.
Definition: loggers.h:187
T to_string(T...args)
void log(int level, const char *origin, const char *fmt,...)
emit a log message at the given log level.
Definition: loggers.h:1109
Highest severity possible.
Definition: loggers.h:130
Error logging level.
Definition: loggers.h:84
const Tools::conststr glue
the glue to use in the local logger
Definition: loggers.h:1600
Logger that discards all messages.
Definition: loggers.h:1274
bool is_at_least_of_severity(int level, int baselevel)
Helper to compare severity levels.
Definition: loggers.h:246
Object which stores a log level and can initialize from a string.
Definition: loggers.h:156
T end(T...args)
Base logger class.
Definition: loggers.h:424
int level() const
Get the base logger's set level.
Definition: loggers.h:1412
void error(const char *origin, const char *fmt,...)
emit an error message
Definition: loggers.h:992
bool enabled_for(int level_) const
Check whether messages at the given log level are enabled.
Definition: loggers.h:755
OriginFilteredLogger(BaseLogger &baselogger_)
Constructor based on a base logger reference.
Definition: loggers.h:1494
std::string get_contents() const
get the contents of the internal buffer
Definition: loggers.h:1351
STL class.
void removeDomainSetting(const std::string &s)
Remove a rule set by setDomainLevel()
Definition: loggers.h:1520
std::string levelname() const
Get the stored level name.
Definition: loggers.h:174
void setLogLevel(int level)
Store a new run-time log level.
Definition: loggers.h:828
STL class.
#define PRINTF3_ARGS_SAFE
See PRINTF1_ARGS_SAFE.
Definition: util.h:78
std::enable_if< dummy &&LoggerTraits< BaseLogger >::HasFilterByOrigin, bool > filter_by_origin(int level, const char *origin)
If relevant for the base logger, filter the messages by origin from the base logger.
Definition: loggers.h:1420
T what(T...args)
Logger which statically discards any messages less important than a fixed severity.
Definition: loggers.h:1360
void emit_log(int level, const char *origin, const std::string &msg)
Emit a log by relaying to the base logger.
Definition: loggers.h:1406
constexpr LocalLoggerOriginSpec(const Tools::conststr &s, const Tools::conststr &gl)
constructor. origin_prefix_add is left blank.
Definition: loggers.h:1603
int level() const
Get the base logger's set level.
Definition: loggers.h:1810
MinimumSeverityLogger(BaseLogger &baselogger_)
Constructor from a base logger.
Definition: loggers.h:1400
T erase(T...args)
void setDisplayOrigin(bool display_origin_)
Definition: loggers.h:1218
Tool to specify arguments to LocalLogger.
Definition: loggers.h:1594
const Tools::conststr origin_prefix
Origin prefix for the local logger.
Definition: loggers.h:1596
std::ostream & operator<<(std::ostream &str, const LogLevel &l)
C++ output stream operator for LogLevel.
Definition: loggers.h:229
void warning(const char *origin, const char *fmt,...)
emit a warning message
Definition: loggers.h:1016
A constexpr string type, suitable for basic compile-time string processing.
T clear(T...args)
void debug(const char *origin, const char *fmt,...)
emit an debug message
Definition: loggers.h:1062
STL class.
Default traits for Logger implementations.
Definition: loggers.h:286
int level() const
Get the stored level code. See LogLevelCode.
Definition: loggers.h:168
T find(T...args)
void longdebug(const char *origin, const char *fmt,...)
emit a very verbose debugging message
Definition: loggers.h:1085
void setLevel(int level)
Changes the runtime log level to a new value.
Definition: loggers.h:1331
void info(const char *origin, const char *fmt,...)
emit an information/notice message
Definition: loggers.h:1039
T begin(T...args)
Traits template struct to be specialized for specific Logger implementations.
Definition: loggers.h:349
void emit_log(int level, const char *origin, const std::string &msg)
Emit a log message (relay to base logger).
Definition: loggers.h:1547
T c_str(T...args)
Warning logging level.
Definition: loggers.h:92
Helper for statically determining if Level is at least as severe as BaseLevel.
Definition: loggers.h:259
#define PRINTF2_ARGS_SAFE
See PRINTF1_ARGS_SAFE.
Definition: util.h:75
void emit_log(int level, const char *origin, const std::string &msg)
Emit a log by relaying to the base logger.
Definition: loggers.h:1802
std::istream & operator>>(std::istream &str, LogLevel &l)
C++ input stream operator for LogLevel.
Definition: loggers.h:221
LoggerBase(int level_=INFO)
Construct the base logger object.
Definition: loggers.h:452
LogLevelCode
Possible logging levels.
Definition: loggers.h:77
const Tools::conststr origin_prefix_add
optionally some string to append to origin_prefix
Definition: loggers.h:1598
#define PRINTF4_ARGS_SAFE
See PRINTF1_ARGS_SAFE.
Definition: util.h:81
Long Debug logging level.
Definition: loggers.h:122
constexpr conststr extractFuncName(const conststr &funcname)
Extract the function name from its signature.
Definition: util.h:350
LogLevel(const char *s)
Construct a level using a string level name.
Definition: loggers.h:163
LogLevel(int level_=INFO)
Construct a level using an integer level code. See LogLevelCode.
Definition: loggers.h:161
LogLevel(const std::string &s)
Construct a level using a string level name.
Definition: loggers.h:165
int level() const
Unconditionally return LOWEST_SEVERITY_LEVEL for the underlying logging engine.
Definition: loggers.h:1538
A constexpr string type.
Definition: conststr.h:54
T fprintf(T...args)
STL class.
Debug logging level.
Definition: loggers.h:112
Information logging level.
Definition: loggers.h:100
static bool statically_enabled_for()
Static version of statically_enabled_for()
Definition: loggers.h:737
A logger which filters entries according to where they originated from.
Definition: loggers.h:1445
static bool statically_enabled_for(int level)
Check whether the logger is statically disabled for some levels.
Definition: loggers.h:728