1 /*
2  * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 #ifndef __LOG_H_INCLUDED_
27 #define __LOG_H_INCLUDED_
28 
29 #include "PlatformLogEvent.h"
30 #include "tstrings.h"
31 
32 
33 /* Default logger (Logger::defaultLogger()) writes log messages to
34  * the default log file.
35  * Common scenario:
36  *   - main() function configures default logger:
37  *       FileLogAppender appender(_T("my_log_filename.log"));
38  *       Logger::defaultLogger().setAppender(appender);
39  *       Logger::defaultLogger().setLogLevel(LOG_INFO);
40  * If the default file name and log level are not set,
41  *  _T("jusched.log")/LOG_TRACE are used.
42  *
43  * Logger fileName specifies only file name,
44  * full path for the log file depends on the platform
45  * (usually value of the TMP env. var)
46  */
47 
48 class Logger;
49 class StreamLogAppender;
50 
51 struct LogEvent: public PlatformLogEvent {
52     tstring logLevel;
53     tstring fileName;
54     int lineNum;
55     tstring funcName;
56     tstring message;
57 
58     LogEvent();
59 
60     friend class Logger;
61     friend class StreamLogAppender;
62 
63 private:
64     static void init(PlatformLogEvent& logEvent);
65     static void appendFormatted(const PlatformLogEvent& logEvent,
66             tstring& buffer);
67 };
68 
69 
70 class LogAppender {
71 public:
~LogAppender()72     virtual ~LogAppender() {
73     }
74     virtual void append(const LogEvent& v) = 0;
75 };
76 
77 
78 class NopLogAppender: public LogAppender {
79 public:
append(const LogEvent & v)80     virtual void append(const LogEvent& v) {};
81 };
82 
83 
84 class TeeLogAppender: public LogAppender {
85 public:
TeeLogAppender(LogAppender * first,LogAppender * second)86     TeeLogAppender(LogAppender* first, LogAppender* second):
87             first(first), second(second) {
88     }
~TeeLogAppender()89     virtual ~TeeLogAppender() {
90     }
append(const LogEvent & v)91     virtual void append(const LogEvent& v) {
92         if (first) {
93             first->append(v);
94         }
95         if (second) {
96             second->append(v);
97         }
98     }
99 private:
100     LogAppender* first;
101     LogAppender* second;
102 };
103 
104 
105 /**
106  * Writes log events to the given std::ostream.
107  * Supposed to be used with std::cout or std::cerr
108  */
109 class StreamLogAppender: public LogAppender {
110 public:
StreamLogAppender(std::ostream & consumer)111     explicit StreamLogAppender(std::ostream& consumer) : consumer(&consumer) {
112     }
113 
114     virtual void append(const LogEvent& v);
115 
116 private:
117     std::ostream* consumer;
118 };
119 
120 
121 class Logger {
122 public:
123     enum LogLevel {
124         LOG_TRACE,
125         LOG_INFO,
126         LOG_WARNING,
127         LOG_ERROR
128     };
129 
130     static Logger& defaultLogger();
131 
132     explicit Logger(LogAppender& appender, LogLevel logLevel = LOG_TRACE);
133     ~Logger();
134 
setAppender(LogAppender & v)135     LogAppender& setAppender(LogAppender& v) {
136         LogAppender& oldAppender = *appender;
137         appender = &v;
138         return oldAppender;
139     }
140 
getAppender()141     LogAppender& getAppender() const {
142         return *appender;
143     }
144 
145     void setLogLevel(LogLevel logLevel);
146 
147     bool isLoggable(LogLevel logLevel) const ;
148     void log(LogLevel logLevel, LPCTSTR fileName, int lineNum,
149             LPCTSTR funcName, const tstring& message) const;
log(LogLevel logLevel,LPCTSTR fileName,int lineNum,LPCTSTR funcName,const tstrings::any & message)150     void log(LogLevel logLevel, LPCTSTR fileName, int lineNum,
151             LPCTSTR funcName, const tstrings::any& message) const {
152         return log(logLevel, fileName, lineNum, funcName, message.tstr());
153     }
log(LogLevel logLevel,LPCTSTR fileName,int lineNum,LPCTSTR funcName,tstring::const_pointer message)154     void log(LogLevel logLevel, LPCTSTR fileName, int lineNum,
155             LPCTSTR funcName, tstring::const_pointer message) const {
156         return log(logLevel, fileName, lineNum, funcName, tstring(message));
157     }
158 
159     // internal class for scope tracing
160     class ScopeTracer {
161     public:
162         ScopeTracer(Logger &logger, LogLevel logLevel, LPCTSTR fileName,
163                 int lineNum, LPCTSTR funcName, const tstring& scopeName);
164         ~ScopeTracer();
165 
166     private:
167         const Logger &log;
168         const LogLevel level;
169         const tstring file;
170         const int line;
171         const tstring func;
172         const tstring scope;
173         const bool needLog;
174     };
175 
176 private:
177     static void initializingLogging();
178     static void initializeLogging();
179 
180 private:
181     LogLevel level;
182     LogAppender* appender;
183 };
184 
185 
186 class WithExtraLogAppender {
187 public:
WithExtraLogAppender(LogAppender & logAppender)188     WithExtraLogAppender(LogAppender& logAppender):
189             oldLogAppender(Logger::defaultLogger().getAppender()),
190             newLogAppender(&Logger::defaultLogger().getAppender(),
191                     &logAppender) {
192         Logger::defaultLogger().setAppender(newLogAppender);
193     }
194 
~WithExtraLogAppender()195     virtual ~WithExtraLogAppender() {
196         Logger::defaultLogger().setAppender(oldLogAppender);
197     }
198 
199 private:
200     LogAppender& oldLogAppender;
201     TeeLogAppender newLogAppender;
202 };
203 
204 
205 // base logging macro
206 #define LOGGER_LOG(logger, logLevel, message) \
207     do { \
208         if (logger.isLoggable(logLevel)) { \
209             logger.log(logLevel, _T(__FILE__), __LINE__, _T(__FUNCTION__), message); \
210         } \
211     } while(false)
212 
213 
214 // custom logger macros
215 #define LOGGER_TRACE(logger, message)   LOGGER_LOG(logger, Logger::LOG_TRACE, message)
216 #define LOGGER_INFO(logger, message)    LOGGER_LOG(logger, Logger::LOG_INFO, message)
217 #define LOGGER_WARNING(logger, message) LOGGER_LOG(logger, Logger::LOG_WARNING, message)
218 #define LOGGER_ERROR(logger, message)   LOGGER_LOG(logger, Logger::LOG_ERROR, message)
219 // scope tracing macros
220 #define LOGGER_TRACE_SCOPE(logger, scopeName) \
221     Logger::ScopeTracer tracer__COUNTER__(logger, Logger::LOG_TRACE, _T(__FILE__), __LINE__, _T(__FUNCTION__), scopeName)
222 #define LOGGER_TRACE_FUNCTION(logger)   LOGGER_TRACE_SCOPE(logger, _T(__FUNCTION__))
223 
224 
225 // default logger macros
226 #define LOG_TRACE(message)              LOGGER_LOG(Logger::defaultLogger(), Logger::LOG_TRACE, message)
227 #define LOG_INFO(message)               LOGGER_LOG(Logger::defaultLogger(), Logger::LOG_INFO, message)
228 #define LOG_WARNING(message)            LOGGER_LOG(Logger::defaultLogger(), Logger::LOG_WARNING, message)
229 #define LOG_ERROR(message)              LOGGER_LOG(Logger::defaultLogger(), Logger::LOG_ERROR, message)
230 // scope tracing macros
231 // logs (_T("Entering ") + scopeName) at the beging, (_T("Exiting ") + scopeName) at the end of scope
232 #define LOG_TRACE_SCOPE(scopeName)      LOGGER_TRACE_SCOPE(Logger::defaultLogger(), scopeName)
233 // logs (_T("Entering ") + functionName) at the beging, (_T("Exiting ") + __FUNCTION__) at the end of scope
234 #define LOG_TRACE_FUNCTION()            LOGGER_TRACE_FUNCTION(Logger::defaultLogger())
235 
236 
237 #endif // __LOG_H_INCLUDED_
238