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