SPDLOG适配鸿蒙Next,添加控制台sink

最近工作需要,原有的 native 模块需要编译支持 OHOS(BTW, OHOS 真的很容易拼错)。因此,我为 OHOS 增加了一个 console 的 sink,与 Android 基本一致。废话不多说,直接上代码吧。直接复制 android_sink.h 即可,里面的 level 转换和 output 接口改一下就能完美运行。当然,其中的 domain 和失败返回重试还有待商榷。

// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)

#pragma once

#include <spdlog/details/fmt_helper.h>
#include <spdlog/details/null_mutex.h>
#include <spdlog/details/os.h>
#include <spdlog/details/synchronous_factory.h>
#include <spdlog/sinks/base_sink.h>

#include <chrono>
// $OHOS_HOME/sdk/default/openharmony/native/sysroot/usr/include/hilog/log.h
#include <hilog/log.h>
#include <mutex>
#include <string>
#include <thread>

#if !defined(SPDLOG_OHOS_RETRIES)
#define SPDLOG_OHOS_RETRIES 2
#endif

namespace spdlog {
namespace sinks {

/*
 * OHOS sink (logging using OH_LOG_Print)
 */
template<typename Mutex>
class ohos_sink final : public base_sink<Mutex> {
 public:
  explicit ohos_sink(std::string tag = "spdlog", bool use_raw_msg = false)
      : tag_(std::move(tag)), use_raw_msg_(use_raw_msg) {}

 protected:
  void sink_it_(const details::log_msg &msg) override {
    const LogLevel log_level = convert_to_ohos_(msg.level);
    memory_buf_t formatted;
    if (use_raw_msg_) {
      details::fmt_helper::append_string_view(msg.payload, formatted);
    } else {
      base_sink<Mutex>::formatter_->format(msg, formatted);
    }
    formatted.push_back('\0');
    const char *msg_output = formatted.data();

    constexpr unsigned int _DOMAIN = 0X0;

    // See $OHOS_HOME/sdk/default/openharmony/native/sysroot/usr/include/hilog/log.h
    int ret = OH_LOG_Print(LogType::LOG_APP, log_level, _DOMAIN, tag_.c_str(),
                           "%{public}s", msg_output);
    int retry_count = 0;
    while ((ret < 0 /*FAILED*/) && (retry_count < SPDLOG_OHOS_RETRIES)) {
      details::os::sleep_for_millis(5);
      ret = OH_LOG_Print(LogType::LOG_APP, log_level, _DOMAIN, tag_.c_str(),
                         "%{public}s", msg_output);
      retry_count++;
    }

    if (ret < 0) { throw_spdlog_ex("OH_LOG_Print() failed", ret); }
  }

  void flush_() override {}

 private:
  static LogLevel convert_to_ohos_(spdlog::level::level_enum level) {
    switch (level) {
      case spdlog::level::trace:
        return LogLevel::LOG_DEBUG; // NO TRACE LEVEL IN OHOS
      case spdlog::level::debug: return LogLevel::LOG_DEBUG;
      case spdlog::level::info: return LogLevel::LOG_INFO;
      case spdlog::level::warn: return LogLevel::LOG_WARN;
      case spdlog::level::err: return LogLevel::LOG_ERROR;
      case spdlog::level::critical: return LogLevel::LOG_FATAL;
      default: return LogLevel::LOG_INFO;
    }
  }

  std::string tag_;
  bool use_raw_msg_;
};

using ohos_sink_mt = ohos_sink<std::mutex>;
using ohos_sink_st = ohos_sink<details::null_mutex>;
} // namespace sinks

// Create and register ohos syslog logger

template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger>
ohos_logger_mt(const std::string &logger_name,
               const std::string &tag = "spdlog") {
  return Factory::template create<sinks::ohos_sink_mt>(logger_name, tag);
}

template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger>
ohos_logger_st(const std::string &logger_name,
               const std::string &tag = "spdlog") {
  return Factory::template create<sinks::ohos_sink_st>(logger_name, tag);
}

} // namespace spdlog

代码说明

  1. 包含必要的头文件:包括 spdloghilog 的头文件。
  2. 定义 ohos_sink:继承自 base_sink,实现 sink_it_flush_ 方法。
  3. 日志级别转换:将 spdlog 的日志级别转换为 OHOS 的日志级别。
  4. 日志输出:使用 OH_LOG_Print 输出日志,并在失败时重试。
  5. 创建和注册 logger:提供 ohos_logger_mtohos_logger_st 两个工厂方法,分别用于多线程和单线程环境。

这样,我们就为 OHOS 增加了一个 console 的 sink,可以方便地在 OHOS 平台上使用 spdlog 进行日志记录。

`spdlog` 是 C++ 中非常流行的一个高效日志库,支持多种日志输出目标(称为 "sinks")。通过 `sink`,你可以将日志内容发送到文件、控制台等不同的地方。如果你需要将日志内容打印到 **串口** 上,则需要自定义一个基于串口通信的日志 sink。 ### 实现步骤 #### 1. 自定义 Sink 类 首先创建一个新的类继承自 `spdlog::sinks::base_sink<std::mutex>` 或者无锁版本的 `std::atomic_flag`,并实现其虚函数 `_sink_it_` 和 `_flush_` 来处理日志消息和刷新操作。 ```cpp #include <spdlog/sinks/base_sink.h> #include <serialib.h> // 假设使用 serialib 库作为串口通信工具 #include <string> class SerialPortSink final : public spdlog::sinks::base_sink<std::mutex> { public: explicit SerialPortSink(const std::string& port_name, int baud_rate) : _port(port_name.c_str()) { if (!_port.openDevice()) throw std::runtime_error("Failed to open serial device."); _port.setBaudRate(baud_rate); } protected: void sink_it_(const spdlog::details::log_msg& msg) override { // 格式化日志信息为字符串 memory_buffer buffer; formatter_->format(msg, buffer); // 将缓冲区数据写入串口设备 const auto data = buffer.str(); _port.writeString(data.c_str(), static_cast<int>(data.size())); } void flush_() override {} private: serialib _port; // 使用第三方库完成实际串口功能 }; ``` #### 2. 注册新的 Sink 到 Logger 对象中 接下来我们需要构建包含上述定制 Sink 的 logger 并设置格式: ```cpp auto create_serial_logger(const char* name, const std::string& port_name, int baud_rate){ auto ser_port_sink = std::make_shared<SerialPortSink>(port_name, baud_rate); // 创建 logger,并添加我们新构造出来的串口 sink return std::make_shared<spdlog::logger>( name, std::move(ser_port_sink)); } int main(){ try{ // 示例初始化:假设 COM3 端口,9600 波特率 auto myLogger = create_serial_logger("mySerLog", "/dev/ttyS0", 9600); // 设置全局默认模式或其他选项... spdlog::set_default_logger(myLogger); SPDLOG_INFO("Hello from Serial Log!"); } catch (const spdlog::spdlog_ex &ex){ std::cerr << "[Error] During setup of custom loggers: " << ex.what() << '\n'; } } ``` 以上就是如何让 Spdlog 支持向硬件串行端口发送文本记录的基本流程了! ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值