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
27namespace libtextclassifier2 {
28namespace logging {
29
30// A tiny code footprint string stream for assembling log messages.
31struct 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
40template <typename T>
41inline LoggingStringStream &operator<<(LoggingStringStream &stream,
42                                       const T &entry) {
43  stream.message.append(std::to_string(entry));
44  return stream;
45}
46
47inline LoggingStringStream &operator<<(LoggingStringStream &stream,
48                                       const char *message) {
49  stream.message.append(message);
50  return stream;
51}
52
53#if defined(HAS_GLOBAL_STRING)
54inline LoggingStringStream &operator<<(LoggingStringStream &stream,
55                                       const ::string &message) {
56  stream.message.append(message);
57  return stream;
58}
59#endif
60
61inline 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.
76class 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.
96class NullStream {
97 public:
98  NullStream() {}
99  NullStream &stream() { return *this; }
100};
101template <typename T>
102inline 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