Bootstrap

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 进行日志记录。

;