【NextPilot日志移植】日志写入流程

📝 文件后端日志写入流程详解

当后端选择文件时,日志写入过程主要涉及 LogWriter 和 LogWriterFile 类的协作。以下是详细的日志写入过程解释及涉及的代码:

1. LogWriter 类初始化

LogWriter 类的构造函数中,如果配置的后端包含 BackendFile,则会创建一个 LogWriterFile 对象。

cpp 复制代码
LogWriter::LogWriter(Backend configured_backend, size_t file_buffer_size) :
    _backend(configured_backend) {
    if (configured_backend & BackendFile) {
        _log_writer_file_for_write = _log_writer_file = new LogWriterFile(file_buffer_size);

        if (!_log_writer_file) {
            PX4_ERR("LogWriterFile allocation failed");
        }
    }
    // ... 其他代码
}

2. 启动文件日志

Logger 类中,调用 start_log_file 方法启动文件日志。该方法会获取日志文件名,设置加密参数,调用 LogWriterstart_log_file 方法启动日志,并写入日志头、版本信息、格式信息等。

cpp 复制代码
void Logger::start_log_file(LogType type) {
    if (_writer.is_started(type, LogWriter::BackendFile) || (_writer.backend() & LogWriter::BackendFile) == 0) {
        return;
    }

    // ... 其他代码

    char file_name[LOG_DIR_LEN] = "";

    if (get_log_file_name(type, file_name, sizeof(file_name), type == LogType::Full)) {
        PX4_ERR("failed to get log file name");
        return;
    }

#if defined(PX4_CRYPTO)
    _writer.set_encryption_parameters((px4_crypto_algorithm_t)_param_sdlog_crypto_algorithm.get(),
                                      _param_sdlog_crypto_key.get(), _param_sdlog_crypto_exchange_key.get());
#endif

    _writer.start_log_file(type, file_name);
    _writer.select_write_backend(LogWriter::BackendFile);
    _writer.set_need_reliable_transfer(true);

    write_header(type);
    write_version(type);
    write_formats(type);

    // ... 其他代码

    _writer.set_need_reliable_transfer(false);
    _writer.unselect_write_backend();
    _writer.notify();

    // ... 其他代码
}

3. LogWriter 调用 LogWriterFile 启动日志

LogWriterstart_log_file 方法会调用 LogWriterFilestart_log 方法。

cpp 复制代码
void LogWriter::start_log_file(LogType type, const char *filename) {
    if (_log_writer_file) {
        _log_writer_file->start_log(type, filename);
    }
}

4. LogWriterFile 启动日志

LogWriterFilestart_log 方法会打开文件并设置 _should_run 标志。

cpp 复制代码
bool LogWriterFile::LogFileBuffer::start_log(const char *filename) {
    _fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0666);

    if (_fd < 0) {
        PX4_ERR("failed to open log file (%s)", filename);
        return false;
    }

    _should_run = true;
    return true;
}

5. 写入日志消息

Logger 类中,调用 write_message 方法写入日志消息。该方法会调用 LogWriterwrite_message 方法。

cpp 复制代码
bool Logger::write_message(LogType type, void *ptr, size_t size) {
    _writer.lock();
    bool ret = _writer.write_message(type, ptr, size) == 0;
    _writer.unlock();
    return ret;
}

6. LogWriter 调用 LogWriterFile 写入消息

LogWriterwrite_message 方法会调用 LogWriterFilewrite_message 方法。

cpp 复制代码
int LogWriter::write_message(LogType type, void *ptr, size_t size, uint64_t dropout_start) {
    int ret_file = 0;

    if (_log_writer_file_for_write) {
        ret_file = _log_writer_file_for_write->write_message(type, ptr, size, dropout_start);
    }

    // ... 其他代码

    return ret_file;
}

7. LogWriterFile 写入消息

LogWriterFilewrite_message 方法会将消息写入缓冲区。

cpp 复制代码
int LogWriterFile::write_message(LogType type, void *ptr, size_t size, uint64_t dropout_start) {
    LogFileBuffer &buffer = _buffers[(int)type];

    if (buffer.available() < size) {
        return -1; // 缓冲区空间不足
    }

    buffer.write_no_check(ptr, size);
    notify(); // 通知写入线程有新数据

    return 0;
}

8. LogWriterFile 写入线程处理

LogWriterFilerun 方法是一个循环,会不断检查缓冲区是否有数据,并将数据写入文件。

cpp 复制代码
void LogWriterFile::run() {
    while (!_exit_thread.load()) {
        // ... 其他代码

        while (true) {
            const hrt_abstime now = hrt_absolute_time();

            // ... 其他代码

            int i = (int)LogType::Count - 1;

            while (i >= 0) {
                void          *read_ptr;
                bool           is_part;
                LogFileBuffer &buffer    = _buffers[i];
                size_t         available = buffer.get_read_ptr(&read_ptr, &is_part);

                // ... 其他代码

                if (available >= min_available[i] || is_part || (!buffer._should_run && available > 0)) {
                    pthread_mutex_unlock(&_mtx);

                    // ... 其他代码

                    int written = buffer.write_to_file(read_ptr, available, call_fsync);

                    // ... 其他代码

                    if (written >= 0) {
                        pthread_mutex_lock(&_mtx);
                        buffer.mark_read(written);

                        // ... 其他代码
                    } else {
                        // ... 其他代码
                    }
                }

                // ... 其他代码
            }

            // ... 其他代码
        }

        // ... 其他代码
    }
}

9. LogWriterFile 写入文件

LogWriterFileLogFileBuffer 类的 write_to_file 方法会将数据写入文件。

cpp 复制代码
ssize_t LogWriterFile::LogFileBuffer::write_to_file(const void *buffer, size_t size, bool call_fsync) const {
    perf_begin(_perf_write);
    ssize_t ret = ::write(_fd, buffer, size);
    perf_end(_perf_write);

    if (call_fsync) {
        fsync();
    }

    return ret;
}

总结

整个文件日志写入过程可以概括为:

  1. LogWriter 初始化时创建 LogWriterFile 对象。
  2. Logger 调用 start_log_file 方法启动文件日志。
  3. LogWriter 调用 LogWriterFilestart_log 方法打开文件。
  4. Logger 调用 write_message 方法写入日志消息。
  5. LogWriter 调用 LogWriterFilewrite_message 方法将消息写入缓冲区。
  6. LogWriterFile 的写入线程不断检查缓冲区,将数据写入文件。

通过以上步骤,实现了文件后端的日志写入功能。

相关推荐
A charmer18 天前
C++ 日志系统实战第三步:熟悉掌握各种设计模式
c++·日志系统
迷茫运维路4 个月前
Graylog采集MySQL慢日志实战
运维·mysql·graylog·日志系统
program-learner8 个月前
日志系统扩展一:日志落地数据库:MySQL、SQLite3
数据库·mysql·sqlite·项目·日志系统
program-learner8 个月前
日志系统第二弹:设计模式介绍,C和C++不定参函数的介绍
c++·设计模式·项目·日志系统
一只小松许️8 个月前
项目日志——日志落地模块的设计、实现、测试
日志系统
SunkingYang1 年前
python如何通过自身日志系统读写日志文件
python·logging·日志文件·日志系统·读写日志文件·如何写日志
花想云2 年前
C++项目实战——基于多设计模式下的同步&异步日志系统-⑪-日志器管理类与全局建造者类设计(单例模式)
c++·单例模式·设计模式·日志系统·c++项目
花想云2 年前
C++项目实战——基于多设计模式下的同步&异步日志系统-⑫-日志宏&全局接口设计(代理模式)
c++·设计模式·代理模式·日志系统·c++项目
花想云2 年前
C++项目实战——基于多设计模式下的同步&异步日志系统-⑦-日志输出格式化类设计
c++·设计模式·日志系统·c++项目