美文网首页
LevelDB 跨平台编程(三)Logger

LevelDB 跨平台编程(三)Logger

作者: wayyyy | 来源:发表于2024-02-04 01:53 被阅读0次

    与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信息可采取两种不同的操作模式,从而实现空间与时间资源消耗的平衡。

    相关文章

      网友评论

          本文标题:LevelDB 跨平台编程(三)Logger

          本文链接:https://www.haomeiwen.com/subject/gziokltx.html