1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef LIBTEXTCLASSIFIER_UTILS_BASE_LOGGING_H_
18 #define LIBTEXTCLASSIFIER_UTILS_BASE_LOGGING_H_
19 
20 #include <cassert>
21 #include <string>
22 
23 #include "utils/base/logging_levels.h"
24 #include "utils/base/port.h"
25 
26 
27 namespace libtextclassifier3 {
28 namespace logging {
29 
30 // A tiny code footprint string stream for assembling log messages.
31 struct LoggingStringStream {
LoggingStringStreamLoggingStringStream32   LoggingStringStream() {}
streamLoggingStringStream33   LoggingStringStream &stream() { return *this; }
34   // Needed for invocation in TC3_CHECK macro.
35   explicit operator bool() const { return true; }
36 
37   std::string message;
38 };
39 
40 template <typename T>
41 inline LoggingStringStream &operator<<(LoggingStringStream &stream,
42                                        const T &entry) {
43   stream.message.append(std::to_string(entry));
44   return stream;
45 }
46 
47 inline LoggingStringStream &operator<<(LoggingStringStream &stream,
48                                        const char *message) {
49   stream.message.append(message);
50   return stream;
51 }
52 
53 #if defined(HAS_GLOBAL_STRING)
54 inline LoggingStringStream &operator<<(LoggingStringStream &stream,
55                                        const ::string &message) {
56   stream.message.append(message);
57   return stream;
58 }
59 #endif
60 
61 inline LoggingStringStream &operator<<(LoggingStringStream &stream,
62                                        const std::string &message) {
63   stream.message.append(message);
64   return stream;
65 }
66 
67 // The class that does all the work behind our TC3_LOG(severity) macros.  Each
68 // TC3_LOG(severity) << obj1 << obj2 << ...; logging statement creates a
69 // LogMessage temporary object containing a stringstream.  Each operator<< adds
70 // info to that stringstream and the LogMessage destructor performs the actual
71 // logging.  The reason this works is that in C++, "all temporary objects are
72 // destroyed as the last step in evaluating the full-expression that (lexically)
73 // contains the point where they were created."  For more info, see
74 // http://en.cppreference.com/w/cpp/language/lifetime.  Hence, the destructor is
75 // invoked after the last << from that logging statement.
76 class LogMessage {
77  public:
78   LogMessage(LogSeverity severity, const char *file_name,
79              int line_number) TC3_ATTRIBUTE_NOINLINE;
80 
81   ~LogMessage() TC3_ATTRIBUTE_NOINLINE;
82 
83   // Returns the stream associated with the logger object.
stream()84   LoggingStringStream &stream() { return stream_; }
85 
86  private:
87   const LogSeverity severity_;
88 
89   // Stream that "prints" all info into a string (not to a file).  We construct
90   // here the entire logging message and next print it in one operation.
91   LoggingStringStream stream_;
92 };
93 
94 // Pseudo-stream that "eats" the tokens <<-pumped into it, without printing
95 // anything.
96 class NullStream {
97  public:
NullStream()98   NullStream() {}
stream()99   NullStream &stream() { return *this; }
100 };
101 template <typename T>
102 inline NullStream &operator<<(NullStream &str, const T &) {
103   return str;
104 }
105 
106 }  // namespace logging
107 }  // namespace libtextclassifier3
108 
109 #define TC3_LOG(severity)                                          \
110   ::libtextclassifier3::logging::LogMessage(                       \
111       ::libtextclassifier3::logging::severity, __FILE__, __LINE__) \
112       .stream()
113 
114 // If condition x is true, does nothing.  Otherwise, crashes the program (liek
115 // LOG(FATAL)) with an informative message.  Can be continued with extra
116 // messages, via <<, like any logging macro, e.g.,
117 //
118 // TC3_CHECK(my_cond) << "I think we hit a problem";
119 #define TC3_CHECK(x)                                                           \
120   (x) || TC3_LOG(FATAL) << __FILE__ << ":" << __LINE__ << ": check failed: \"" \
121                         << #x << "\" "
122 
123 #define TC3_CHECK_EQ(x, y) TC3_CHECK((x) == (y))
124 #define TC3_CHECK_LT(x, y) TC3_CHECK((x) < (y))
125 #define TC3_CHECK_GT(x, y) TC3_CHECK((x) > (y))
126 #define TC3_CHECK_LE(x, y) TC3_CHECK((x) <= (y))
127 #define TC3_CHECK_GE(x, y) TC3_CHECK((x) >= (y))
128 #define TC3_CHECK_NE(x, y) TC3_CHECK((x) != (y))
129 
130 #define TC3_NULLSTREAM ::libtextclassifier3::logging::NullStream().stream()
131 
132 // Debug checks: a TC3_DCHECK<suffix> macro should behave like TC3_CHECK<suffix>
133 // in debug mode an don't check / don't print anything in non-debug mode.
134 #ifdef NDEBUG
135 
136 #define TC3_DCHECK(x) TC3_NULLSTREAM
137 #define TC3_DCHECK_EQ(x, y) TC3_NULLSTREAM
138 #define TC3_DCHECK_LT(x, y) TC3_NULLSTREAM
139 #define TC3_DCHECK_GT(x, y) TC3_NULLSTREAM
140 #define TC3_DCHECK_LE(x, y) TC3_NULLSTREAM
141 #define TC3_DCHECK_GE(x, y) TC3_NULLSTREAM
142 #define TC3_DCHECK_NE(x, y) TC3_NULLSTREAM
143 
144 #else  // NDEBUG
145 
146 // In debug mode, each TC3_DCHECK<suffix> is equivalent to TC3_CHECK<suffix>,
147 // i.e., a real check that crashes when the condition is not true.
148 #define TC3_DCHECK(x) TC3_CHECK(x)
149 #define TC3_DCHECK_EQ(x, y) TC3_CHECK_EQ(x, y)
150 #define TC3_DCHECK_LT(x, y) TC3_CHECK_LT(x, y)
151 #define TC3_DCHECK_GT(x, y) TC3_CHECK_GT(x, y)
152 #define TC3_DCHECK_LE(x, y) TC3_CHECK_LE(x, y)
153 #define TC3_DCHECK_GE(x, y) TC3_CHECK_GE(x, y)
154 #define TC3_DCHECK_NE(x, y) TC3_CHECK_NE(x, y)
155 
156 #endif  // NDEBUG
157 
158 #ifdef TC3_VLOG
159 #define TC3_VLOG(severity)                                     \
160   ::libtextclassifier3::logging::LogMessage(                   \
161       ::libtextclassifier3::logging::INFO, __FILE__, __LINE__) \
162       .stream()
163 #else
164 #define TC3_VLOG(severity) TC3_NULLSTREAM
165 #endif
166 
167 #endif  // LIBTEXTCLASSIFIER_UTILS_BASE_LOGGING_H_
168