1 // Copyright (c) 2018 IIS (The Internet Foundation in Sweden) 2 // Written by Göran Andersson <initgoran@gmail.com> 3 4 // This is a simple logger. All classes that want to write to the global log 5 // file should inherit from this class. 6 // 7 // By default, logs will be written to cerr. To log elsewhere, you must 8 // call the static function setLogFile with a stream object (e.g. an ofstream 9 // or an ostringstream) which the logs will be written to. The stream will be 10 // used globally. You must make sure the setLogFile stream never is destroyed, 11 // at least not until setLogFile is called with another stream. 12 // 13 // This class has also a TimePoint typedef and some helper functions to measure 14 // relative time, based on std::chrono::steady_clock. 15 // The useful (static) time functions are timeNow(), timeAfter(double s), 16 // secondsSince(const TimePoint &t), secondsTo(const TimePoint &t). 17 18 #pragma once 19 20 #ifdef _WIN32 21 #ifndef NOMINMAX 22 #define NOMINMAX 23 #endif 24 #ifdef max 25 #undef max 26 #endif 27 #else 28 #include <string.h> 29 #endif 30 31 #include <iostream> 32 #include <sstream> 33 #include <chrono> 34 #include <thread> 35 36 #ifdef __ANDROID_API__ 37 #include <android/log.h> 38 #endif 39 40 class DummyStream { 41 public: 42 template<class T> 43 DummyStream &operator<<(T ) { return *this; } 44 #ifdef __ANDROID_API__ 45 DummyStream &operator<<(const char *s) { 46 __android_log_print(ANDROID_LOG_VERBOSE, "BBK", "%s", s); 47 return *this; } 48 DummyStream &operator<<(std::string s) { 49 __android_log_print(ANDROID_LOG_VERBOSE, "BBK", "%s", s.c_str()); 50 return *this; } 51 DummyStream &operator<<(int i) { 52 __android_log_print(ANDROID_LOG_VERBOSE, "BBK", "%d", i); 53 return *this; } 54 DummyStream &operator<<(double x) { 55 __android_log_print(ANDROID_LOG_VERBOSE, "BBK", "%f", x); 56 return *this; } 57 #endif 58 DummyStream& operator<<(std::ostream &(*)(std::ostream &) ) { 59 return *this; 60 } 61 virtual ~DummyStream(); 62 private: 63 }; 64 65 typedef std::chrono::steady_clock::time_point TimePoint; 66 67 class Logger { 68 public: Logger(std::string label)69 Logger(std::string label) : 70 _label(label) { 71 // TODO: single initialisation 72 _blackHole.clear(std::istream::eofbit); 73 } 74 static void setLogFile(std::ostream &stream); 75 76 // If current log is a file (ofstream), reopen it with new filename: 77 static void reopenLogFile(const std::string &filename); 78 79 // Max number of lines of log/warn/err: 80 static void setLogLimit(unsigned int loglines = 100000, 81 unsigned int warnlines = 10000, 82 unsigned int errlines = 10000); 83 84 static void sayTime(std::ostream &stream); inError()85 static bool inError() { 86 return in_error; 87 } err_log(const std::string & label)88 static std::ostream &err_log(const std::string &label) { 89 if (err_count) { 90 in_error = true; 91 --err_count; 92 *_logFile << "\n" << elapsed() << ' ' << label << " *** " 93 << (err_count ? "ERROR ***: " : "LAST ERR ***: "); 94 return *_logFile; 95 } else { 96 return _blackHole; 97 } 98 } warn_log(const std::string & label)99 static std::ostream &warn_log(const std::string &label) { 100 if (warn_count) { 101 --warn_count; 102 *_logFile << "\n" << elapsed() << ' ' << label << " *** " 103 << (warn_count ? "WARNING ***: " : "LAST WARN ***: "); 104 return *_logFile; 105 } else { 106 return _blackHole; 107 } 108 } log(const std::string & label)109 static std::ostream &log(const std::string &label) { 110 if (log_count) { 111 --log_count; 112 *_logFile << "\n" << elapsed() << ' ' << label << ": "; 113 if (!log_count) 114 *_logFile << "LAST LOG: "; 115 return *_logFile; 116 } else { 117 return _blackHole; 118 } 119 } 120 121 // Calling this often will be bad for performance: flushLogFile()122 static void flushLogFile() { 123 *_logFile << std::endl; 124 } 125 pauseLogging()126 static void pauseLogging() { 127 _logFile = &_blackHole; 128 } 129 static double secondsSince(const TimePoint &t); 130 static double secondsTo(const TimePoint &t); 131 static long msSince(const TimePoint &t); 132 static long msTo(const TimePoint &t); hasExpired(const TimePoint & t)133 static bool hasExpired(const TimePoint &t) { 134 return secondsSince(t) >= 0; 135 } 136 // What time is it? timeNow()137 static TimePoint timeNow() { 138 return std::chrono::steady_clock::now(); 139 } 140 // What time will it be after s seconds? timeAfter(double s)141 static TimePoint timeAfter(double s) { 142 return timeNow() + std::chrono::microseconds(toUs(s)); 143 } timeMax()144 static TimePoint timeMax() { 145 return TimePoint::max(); 146 } toUs(double t)147 static std::chrono::microseconds::rep toUs(double t) { 148 return static_cast<std::chrono::microseconds::rep>(1e6*t); 149 } 150 static std::string dateString(time_t t = 0); 151 static std::string dateString2(time_t t = 0); 152 153 // Create string of length random hex chars from system's random number 154 // generator. The length should be a multiple of 4. 155 static std::string createHashKey(unsigned int length = 20); 156 label()157 std::string label() const { 158 return _label; 159 } 160 resetLabel(const std::string & new_label)161 void resetLabel(const std::string &new_label) { 162 _label = new_label; 163 } 164 165 protected: 166 167 #ifdef TASKRUNNER_LOGERR 168 std::ostream &errno_log() const; err_log()169 std::ostream &err_log() const { 170 return err_log(_label); 171 } 172 #else 173 DummyStream &errno_log() const; 174 static DummyStream &err_log() { 175 return _dummyLog; 176 } 177 #endif 178 179 #ifdef TASKRUNNER_LOGWARN warn_log()180 std::ostream &warn_log() const { 181 return warn_log(_label); 182 } 183 #else warn_log()184 static DummyStream &warn_log() { 185 return _dummyLog; 186 } 187 #endif 188 #ifdef TASKRUNNER_LOGINFO log()189 std::ostream &log() const { 190 return log(_label); 191 } 192 #else log()193 static DummyStream &log() { 194 return _dummyLog; 195 } 196 #endif 197 #ifdef TASKRUNNER_LOGDBG dbg_log()198 std::ostream &dbg_log() const { 199 *_logFile << "\n" << elapsed() << ' ' << _label << ": "; 200 return *_logFile; 201 } 202 #else dbg_log()203 static DummyStream &dbg_log() { 204 return _dummyLog; 205 } 206 #endif 207 private: elapsed()208 static long elapsed() { 209 return msSince(start_time); 210 } 211 std::string _label; 212 213 #ifdef USE_THREADS 214 thread_local 215 #endif 216 static bool in_error; 217 #ifdef USE_THREADS 218 thread_local 219 #endif 220 static TimePoint start_time; 221 #ifdef USE_THREADS 222 thread_local 223 #endif 224 static std::ostream *_logFile; 225 #ifdef USE_THREADS 226 thread_local 227 #endif 228 static std::ostringstream _blackHole; 229 #ifdef USE_THREADS 230 thread_local 231 #endif 232 static unsigned int log_count, warn_count, err_count; 233 static DummyStream _dummyLog; 234 }; 235