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 NETUTILS_LOG_H
18 #define NETUTILS_LOG_H
19 
20 #include <chrono>
21 #include <deque>
22 #include <functional>
23 #include <shared_mutex>
24 #include <string>
25 #include <type_traits>
26 #include <vector>
27 
28 #include <android-base/stringprintf.h>
29 #include <android-base/thread_annotations.h>
30 
31 #include <netdutils/Status.h>
32 
33 namespace android {
34 namespace netdutils {
35 
36 class LogEntry {
37   public:
38     LogEntry() = default;
39     LogEntry(const LogEntry&) = default;
40     LogEntry(LogEntry&&) = default;
41     ~LogEntry() = default;
42     LogEntry& operator=(const LogEntry&) = default;
43     LogEntry& operator=(LogEntry&&) = default;
44 
45     std::string toString() const;
46 
47     ///
48     // Helper methods that make it easy to build up a LogEntry message.
49     // If performance becomes a factor the implementations could be inlined.
50     ///
51     LogEntry& message(const std::string& message);
52 
53     // For calling with __FUNCTION__.
54     LogEntry& function(const std::string& function_name);
55     // For calling with __PRETTY_FUNCTION__.
56     LogEntry& prettyFunction(const std::string& pretty_function);
57 
58     // Convenience methods for each of the common types of function arguments.
59     LogEntry& arg(const std::string& val);
60     // Intended for binary buffers, formats as hex
61     LogEntry& arg(const std::vector<uint8_t>& val);
62     LogEntry& arg(const std::vector<int32_t>& val);
63     LogEntry& arg(const std::vector<std::string>& val);
64     template <typename IntT, typename = std::enable_if_t<std::is_arithmetic_v<IntT>>>
arg(IntT val)65     LogEntry& arg(IntT val) {
66         mArgs.push_back(std::to_string(val));
67         return *this;
68     }
69     // Not using a plain overload here to avoid the implicit conversion from
70     // any pointer to bool, which causes string literals to print as 'true'.
71     template <>
72     LogEntry& arg<>(bool val);
73 
74     template <typename... Args>
args(const Args &...a)75     LogEntry& args(const Args&... a) {
76         // Cleverness ahead: we throw away the initializer_list filled with
77         // zeroes, all we care about is calling arg() for each argument.
78         (void) std::initializer_list<int>{(arg(a), 0)...};
79         return *this;
80     }
81 
82     // Some things can return more than one value, or have multiple output
83     // parameters, so each of these adds to the mReturns vector.
84     LogEntry& returns(const std::string& rval);
85     LogEntry& returns(const Status& status);
86     LogEntry& returns(bool rval);
87     template <class T>
returns(T val)88     LogEntry& returns(T val) {
89         mReturns.push_back(std::to_string(val));
90         return *this;
91     }
92 
93     LogEntry& withUid(uid_t uid);
94 
95     // Append the duration computed since the creation of this instance.
96     LogEntry& withAutomaticDuration();
97     // Append the string-ified duration computed by some other means.
98     LogEntry& withDuration(const std::string& duration);
99 
100   private:
101     std::chrono::steady_clock::time_point mStart = std::chrono::steady_clock::now();
102     std::string mMsg{};
103     std::string mFunc{};
104     std::vector<std::string> mArgs{};
105     std::vector<std::string> mReturns{};
106     std::string mUid{};
107     std::string mDuration{};
108 };
109 
110 class Log {
111   public:
112     Log() = delete;
Log(const std::string & tag)113     Log(const std::string& tag) : Log(tag, MAX_ENTRIES) {}
Log(const std::string & tag,size_t maxEntries)114     Log(const std::string& tag, size_t maxEntries) : mTag(tag), mMaxEntries(maxEntries) {}
115     Log(const Log&) = delete;
116     Log(Log&&) = delete;
117     ~Log();
118     Log& operator=(const Log&) = delete;
119     Log& operator=(Log&&) = delete;
120 
newEntry()121     LogEntry newEntry() const { return LogEntry(); }
122 
123     // Record a log entry in internal storage only.
log(const std::string & entry)124     void log(const std::string& entry) { record(Level::LOG, entry); }
125     template <size_t n>
log(const char entry[n])126     void log(const char entry[n]) { log(std::string(entry)); }
log(const LogEntry & entry)127     void log(const LogEntry& entry) { log(entry.toString()); }
log(const char * fmt,...)128     void log(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
129         using ::android::base::StringAppendV;
130         std::string result;
131         va_list ap;
132         va_start(ap, fmt);
133         StringAppendV(&result, fmt, ap);
134         va_end(ap);
135         log(result);
136     }
137 
138     // Record a log entry in internal storage and to ALOGI as well.
info(const std::string & entry)139     void info(const std::string& entry) { record(Level::INFO, entry); }
140     template <size_t n>
info(const char entry[n])141     void info(const char entry[n]) { info(std::string(entry)); }
info(const LogEntry & entry)142     void info(const LogEntry& entry) { info(entry.toString()); }
info(const char * fmt,...)143     void info(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
144         using ::android::base::StringAppendV;
145         std::string result;
146         va_list ap;
147         va_start(ap, fmt);
148         StringAppendV(&result, fmt, ap);
149         va_end(ap);
150         info(result);
151     }
152 
153     // Record a log entry in internal storage and to ALOGW as well.
warn(const std::string & entry)154     void warn(const std::string& entry) { record(Level::WARN, entry); }
155     template <size_t n>
warn(const char entry[n])156     void warn(const char entry[n]) { warn(std::string(entry)); }
warn(const LogEntry & entry)157     void warn(const LogEntry& entry) { warn(entry.toString()); }
warn(const char * fmt,...)158     void warn(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
159         using ::android::base::StringAppendV;
160         std::string result;
161         va_list ap;
162         va_start(ap, fmt);
163         StringAppendV(&result, fmt, ap);
164         va_end(ap);
165         warn(result);
166     }
167 
168     // Record a log entry in internal storage and to ALOGE as well.
error(const std::string & entry)169     void error(const std::string& entry) { record(Level::ERROR, entry); }
170     template <size_t n>
error(const char entry[n])171     void error(const char entry[n]) { error(std::string(entry)); }
error(const LogEntry & entry)172     void error(const LogEntry& entry) { error(entry.toString()); }
error(const char * fmt,...)173     void error(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
174         using ::android::base::StringAppendV;
175         std::string result;
176         va_list ap;
177         va_start(ap, fmt);
178         StringAppendV(&result, fmt, ap);
179         va_end(ap);
180         error(result);
181     }
182 
183     // Iterates over every entry in the log in chronological order. Operates
184     // on a copy of the log entries, and so perEntryFn may itself call one of
185     // the logging functions if needed.
186     void forEachEntry(const std::function<void(const std::string&)>& perEntryFn) const;
187 
188   private:
189     static constexpr const size_t MAX_ENTRIES = 750U;
190     const std::string mTag;
191     const size_t mMaxEntries;
192 
193     // The LOG level adds an entry to mEntries but does not output the message
194     // to the system log. All other levels append to mEntries and output to the
195     // the system log.
196     enum class Level {
197         LOG,
198         INFO,
199         WARN,
200         ERROR,
201     };
202 
203     void record(Level lvl, const std::string& entry);
204 
205     mutable std::shared_mutex mLock;
206     std::deque<const std::string> mEntries;  // GUARDED_BY(mLock), when supported
207 };
208 
209 }  // namespace netdutils
210 }  // namespace android
211 
212 #endif /* NETUTILS_LOG_H */
213