LevelDB 跨平台编程(三)Logger

2024-02-04  本文已影响0人  wayyyy

与Log 文件操作接口类相关定义主要存在两个文件中,其中 env.h 定义了 Logger 抽象接口类,而 posix_logger.h 定义了在 POSIX 平台下 Logger 类的派生实现。
log 抽象类接口:

class Logger
{
    public:
        Logger() = default;

        Logger(const Logger &) = delete;
        Logger &operator=(const Logger &) = delete;

        virtual ~Logger();

        // Write an entry to the log file with the specified format.
        virtual void Logv(const char *format, std::va_list ap) = 0;
};
class PosixLogger final : public Logger
{
private:
    std::FILE *const fp_;

public:
    explicit PosixLogger(std::FILE *fp) : fp_(fp) { assert(fp != nullptr); }

    ~PosixLogger() override { std::fclose(fp_); }

    void Logv(const char *format, std::va_list arguments) override
    {
        // 时间
        struct ::timeval now_timeval;
        ::gettimeofday(&now_timeval, nullptr);
        const std::time_t now_seconds = now_timeval.tv_sec;
        struct std::tm now_components;
        ::localtime_r(&now_seconds, &now_components);

        // 线程ID
        constexpr const int kMaxThreadIdSize = 32;
        std::ostringstream thread_stream;
        thread_stream << std::this_thread::get_id();
        std::string thread_id = thread_stream.str();
        if (thread_id.size() > kMaxThreadIdSize)
        {
            thread_id.resize(kMaxThreadIdSize);
        }

        constexpr const int kStackBufferSize = 512;
        char stack_buffer[kStackBufferSize];
        static_assert(sizeof(stack_buffer) == static_cast<size_t>(kStackBufferSize),
                      "sizeof(char) is expected to be 1 in C++");

        int dynamic_buffer_size = 0; // Computed in the first iteration.
        for (int iteration = 0; iteration < 2; ++iteration)
        {
            const int buffer_size = (iteration == 0) ? kStackBufferSize : dynamic_buffer_size;
            char *const buffer = (iteration == 0) ? stack_buffer : new char[dynamic_buffer_size];

            int buffer_offset = std::snprintf(
                buffer, buffer_size, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %s ",
                now_components.tm_year + 1900, now_components.tm_mon + 1,
                now_components.tm_mday, now_components.tm_hour, now_components.tm_min,
                now_components.tm_sec, static_cast<int>(now_timeval.tv_usec),
                thread_id.c_str());

            assert(buffer_offset <= 28 + kMaxThreadIdSize);
            static_assert(28 + kMaxThreadIdSize < kStackBufferSize,
                          "stack-allocated buffer may not fit the message header");
            assert(buffer_offset < buffer_size);

            std::va_list arguments_copy;
            va_copy(arguments_copy, arguments);
            buffer_offset += std::vsnprintf(buffer + buffer_offset, buffer_size - buffer_offset, format, arguments_copy);
            va_end(arguments_copy);

            if (buffer_offset >= buffer_size - 1)
            {
                if (iteration == 0)
                {
                    dynamic_buffer_size = buffer_offset + 2;
                    continue;
                }

                assert(false);
                buffer_offset = buffer_size - 1;
            }

            if (buffer[buffer_offset - 1] != '\n')
            {
                buffer[buffer_offset] = '\n';
                ++buffer_offset;
            }
            assert(buffer_offset <= buffer_size);
            std::fwrite(buffer, 1, buffer_offset, fp_);
            std::fflush(fp_);

            if (iteration != 0)
            {
                delete[] buffer;
            }
            break;
        }
    }
};

Logv 方法每写一条Log 信息,需要保存以下信息:

  1. 线程id
  2. 当前时间
  3. Log 文本信息

Logv 方法中定义了一个循环,该循环最多执行2次,区别在于:第一次缓冲是在栈中,大小为500字节,第二次缓冲区在堆中,大小为30000字节。如果500字节能保存所有信息,满足Log信息的写入要求,则循环体直接执行break,否则进入第二次循环,并动态申请一个30000字节的缓冲,以实现相应的功能。

使用这种方式的好处是:对不同的长度的Log信息可采取两种不同的操作模式,从而实现空间与时间资源消耗的平衡。

上一篇下一篇

猜你喜欢

热点阅读