C++程序  |  212行  |  7.14 KB

/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef NETUTILS_LOG_H
#define NETUTILS_LOG_H

#include <chrono>
#include <deque>
#include <shared_mutex>
#include <string>
#include <type_traits>
#include <vector>

#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>

#include <netdutils/Status.h>

namespace android {
namespace netdutils {

class LogEntry {
  public:
    LogEntry() = default;
    LogEntry(const LogEntry&) = default;
    LogEntry(LogEntry&&) = default;
    ~LogEntry() = default;
    LogEntry& operator=(const LogEntry&) = default;
    LogEntry& operator=(LogEntry&&) = default;

    std::string toString() const;

    ///
    // Helper methods that make it easy to build up a LogEntry message.
    // If performance becomes a factor the implementations could be inlined.
    ///
    LogEntry& message(const std::string& message);

    // For calling with __FUNCTION__.
    LogEntry& function(const std::string& function_name);
    // For calling with __PRETTY_FUNCTION__.
    LogEntry& prettyFunction(const std::string& pretty_function);

    // Convenience methods for each of the common types of function arguments.
    LogEntry& arg(const std::string& val);
    // Intended for binary buffers, formats as hex
    LogEntry& arg(const std::vector<uint8_t>& val);
    LogEntry& arg(const std::vector<int32_t>& val);
    LogEntry& arg(const std::vector<std::string>& val);
    template <typename IntT, typename = std::enable_if_t<std::is_arithmetic_v<IntT>>>
    LogEntry& arg(IntT val) {
        mArgs.push_back(std::to_string(val));
        return *this;
    }
    // Not using a plain overload here to avoid the implicit conversion from
    // any pointer to bool, which causes string literals to print as 'true'.
    template <>
    LogEntry& arg<>(bool val);

    template <typename... Args>
    LogEntry& args(const Args&... a) {
        // Cleverness ahead: we throw away the initializer_list filled with
        // zeroes, all we care about is calling arg() for each argument.
        (void) std::initializer_list<int>{(arg(a), 0)...};
        return *this;
    }

    // Some things can return more than one value, or have multiple output
    // parameters, so each of these adds to the mReturns vector.
    LogEntry& returns(const std::string& rval);
    LogEntry& returns(const Status& status);
    LogEntry& returns(bool rval);
    template <class T>
    LogEntry& returns(T val) {
        mReturns.push_back(std::to_string(val));
        return *this;
    }

    LogEntry& withUid(uid_t uid);

    // Append the duration computed since the creation of this instance.
    LogEntry& withAutomaticDuration();
    // Append the string-ified duration computed by some other means.
    LogEntry& withDuration(const std::string& duration);

  private:
    std::chrono::steady_clock::time_point mStart = std::chrono::steady_clock::now();
    std::string mMsg{};
    std::string mFunc{};
    std::vector<std::string> mArgs{};
    std::vector<std::string> mReturns{};
    std::string mUid{};
    std::string mDuration{};
};

class Log {
  public:
    Log() = delete;
    Log(const std::string& tag) : Log(tag, MAX_ENTRIES) {}
    Log(const std::string& tag, size_t maxEntries) : mTag(tag), mMaxEntries(maxEntries) {}
    Log(const Log&) = delete;
    Log(Log&&) = delete;
    ~Log();
    Log& operator=(const Log&) = delete;
    Log& operator=(Log&&) = delete;

    LogEntry newEntry() const { return LogEntry(); }

    // Record a log entry in internal storage only.
    void log(const std::string& entry) { record(Level::LOG, entry); }
    template <size_t n>
    void log(const char entry[n]) { log(std::string(entry)); }
    void log(const LogEntry& entry) { log(entry.toString()); }
    void log(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
        using ::android::base::StringAppendV;
        std::string result;
        va_list ap;
        va_start(ap, fmt);
        StringAppendV(&result, fmt, ap);
        va_end(ap);
        log(result);
    }

    // Record a log entry in internal storage and to ALOGI as well.
    void info(const std::string& entry) { record(Level::INFO, entry); }
    template <size_t n>
    void info(const char entry[n]) { info(std::string(entry)); }
    void info(const LogEntry& entry) { info(entry.toString()); }
    void info(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
        using ::android::base::StringAppendV;
        std::string result;
        va_list ap;
        va_start(ap, fmt);
        StringAppendV(&result, fmt, ap);
        va_end(ap);
        info(result);
    }

    // Record a log entry in internal storage and to ALOGW as well.
    void warn(const std::string& entry) { record(Level::WARN, entry); }
    template <size_t n>
    void warn(const char entry[n]) { warn(std::string(entry)); }
    void warn(const LogEntry& entry) { warn(entry.toString()); }
    void warn(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
        using ::android::base::StringAppendV;
        std::string result;
        va_list ap;
        va_start(ap, fmt);
        StringAppendV(&result, fmt, ap);
        va_end(ap);
        warn(result);
    }

    // Record a log entry in internal storage and to ALOGE as well.
    void error(const std::string& entry) { record(Level::ERROR, entry); }
    template <size_t n>
    void error(const char entry[n]) { error(std::string(entry)); }
    void error(const LogEntry& entry) { error(entry.toString()); }
    void error(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
        using ::android::base::StringAppendV;
        std::string result;
        va_list ap;
        va_start(ap, fmt);
        StringAppendV(&result, fmt, ap);
        va_end(ap);
        error(result);
    }

    // Iterates over every entry in the log in chronological order. Operates
    // on a copy of the log entries, and so perEntryFn may itself call one of
    // the logging functions if needed.
    void forEachEntry(const std::function<void(const std::string&)>& perEntryFn) const;

  private:
    static constexpr const size_t MAX_ENTRIES = 750U;
    const std::string mTag;
    const size_t mMaxEntries;

    // The LOG level adds an entry to mEntries but does not output the message
    // to the system log. All other levels append to mEntries and output to the
    // the system log.
    enum class Level {
        LOG,
        INFO,
        WARN,
        ERROR,
    };

    void record(Level lvl, const std::string& entry);

    mutable std::shared_mutex mLock;
    std::deque<const std::string> mEntries;  // GUARDED_BY(mLock), when supported
};

}  // namespace netdutils
}  // namespace android

#endif /* NETUTILS_LOG_H */