Home | History | Annotate | Download | only in base
      1 /*
      2  * Copyright (C) 2017 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_UTIL_BASE_LOGGING_H_
     18 #define LIBTEXTCLASSIFIER_UTIL_BASE_LOGGING_H_
     19 
     20 #include <cassert>
     21 #include <string>
     22 
     23 #include "util/base/logging_levels.h"
     24 #include "util/base/port.h"
     25 
     26 
     27 namespace libtextclassifier2 {
     28 namespace logging {
     29 
     30 // A tiny code footprint string stream for assembling log messages.
     31 struct LoggingStringStream {
     32   LoggingStringStream() {}
     33   LoggingStringStream &stream() { return *this; }
     34   // Needed for invocation in TC_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 TC_LOG(severity) macros.  Each
     68 // TC_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) TC_ATTRIBUTE_NOINLINE;
     80 
     81   ~LogMessage() TC_ATTRIBUTE_NOINLINE;
     82 
     83   // Returns the stream associated with the logger object.
     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:
     98   NullStream() {}
     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 libtextclassifier2
    108 
    109 #define TC_LOG(severity)                                           \
    110   ::libtextclassifier2::logging::LogMessage(                       \
    111       ::libtextclassifier2::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 // TC_CHECK(my_cond) << "I think we hit a problem";
    119 #define TC_CHECK(x)                                                           \
    120   (x) || TC_LOG(FATAL) << __FILE__ << ":" << __LINE__ << ": check failed: \"" \
    121                        << #x
    122 
    123 #define TC_CHECK_EQ(x, y) TC_CHECK((x) == (y))
    124 #define TC_CHECK_LT(x, y) TC_CHECK((x) < (y))
    125 #define TC_CHECK_GT(x, y) TC_CHECK((x) > (y))
    126 #define TC_CHECK_LE(x, y) TC_CHECK((x) <= (y))
    127 #define TC_CHECK_GE(x, y) TC_CHECK((x) >= (y))
    128 #define TC_CHECK_NE(x, y) TC_CHECK((x) != (y))
    129 
    130 #define TC_NULLSTREAM ::libtextclassifier2::logging::NullStream().stream()
    131 
    132 // Debug checks: a TC_DCHECK<suffix> macro should behave like TC_CHECK<suffix>
    133 // in debug mode an don't check / don't print anything in non-debug mode.
    134 #ifdef NDEBUG
    135 
    136 #define TC_DCHECK(x) TC_NULLSTREAM
    137 #define TC_DCHECK_EQ(x, y) TC_NULLSTREAM
    138 #define TC_DCHECK_LT(x, y) TC_NULLSTREAM
    139 #define TC_DCHECK_GT(x, y) TC_NULLSTREAM
    140 #define TC_DCHECK_LE(x, y) TC_NULLSTREAM
    141 #define TC_DCHECK_GE(x, y) TC_NULLSTREAM
    142 #define TC_DCHECK_NE(x, y) TC_NULLSTREAM
    143 
    144 #else  // NDEBUG
    145 
    146 // In debug mode, each TC_DCHECK<suffix> is equivalent to TC_CHECK<suffix>,
    147 // i.e., a real check that crashes when the condition is not true.
    148 #define TC_DCHECK(x) TC_CHECK(x)
    149 #define TC_DCHECK_EQ(x, y) TC_CHECK_EQ(x, y)
    150 #define TC_DCHECK_LT(x, y) TC_CHECK_LT(x, y)
    151 #define TC_DCHECK_GT(x, y) TC_CHECK_GT(x, y)
    152 #define TC_DCHECK_LE(x, y) TC_CHECK_LE(x, y)
    153 #define TC_DCHECK_GE(x, y) TC_CHECK_GE(x, y)
    154 #define TC_DCHECK_NE(x, y) TC_CHECK_NE(x, y)
    155 
    156 #endif  // NDEBUG
    157 
    158 #ifdef LIBTEXTCLASSIFIER_VLOG
    159 #define TC_VLOG(severity)                                      \
    160   ::libtextclassifier2::logging::LogMessage(                   \
    161       ::libtextclassifier2::logging::INFO, __FILE__, __LINE__) \
    162       .stream()
    163 #else
    164 #define TC_VLOG(severity) TC_NULLSTREAM
    165 #endif
    166 
    167 #endif  // LIBTEXTCLASSIFIER_UTIL_BASE_LOGGING_H_
    168