Paddle-Mobile中的日志打印

2019-06-13  本文已影响0人  石器时代小古董

编译时添加宏定义

一、Paddle-Mobile 的日志开关

1.Paddle-Mobile 的日志开关在 cmakeList 中进行配置的。
通过 option 定义了日志的开关。如果 WITH_LOGGING 开启,将通过 add_definitions 定义 PADDLE_MOBILE_DEBUG 的宏。

option(WITH_LOGGING     "print logging for debug"        ON)
if(WITH_LOGGING)
    message(STATUS "Debugging mode")
    add_definitions(-DPADDLE_MOBILE_DEBUG)
else()

2.在编译时添加 ANDROID 宏定义

   cmake .. \
        -B"../build/release/${PLATFORM}" \
        .....
        -DANDROID=true \
        -DNET="${NETS}" \
        -D"${ARM_PLATFORM}"=true

也可以在 cmakelist 中添加

IF (op_android MATCHES ON)
    SET(tag android)
    ADD_DEFINITIONS(-DANDROID)
ENDIF()

二、避免引入不需要的库

1.根据宏定义来决定引入什么库

include <vector>
#ifdef PADDLE_MOBILE_DEBUG
#include <cstring>
#include <iostream>
#include <sstream>
#include <string>
#endif

2.如果还定义了 ANDROID,引入Android的日志文件
这样做的好处时可以避免引入不需要的库

#ifdef ANDROID
#include <android/log.h>
#endif

三、日志打印的核心

1.宏定义日志打印方法,简化代码量

#ifdef ANDROID

static const char *ANDROID_LOG_TAG =
    "paddle_mobile LOG built on " __DATE__ " " __TIME__;

#define ANDROIDLOGI(...)                                               \
  __android_log_print(ANDROID_LOG_INFO, ANDROID_LOG_TAG, __VA_ARGS__); \
  fprintf(stderr, "%s\n", __VA_ARGS__);                                \
  fflush(stderr)
.....
#else
#define ANDROIDLOGI(...)
#define ANDROIDLOGW(...)
#define ANDROIDLOGD(...)
#define ANDROIDLOGE(...)

#endif

2.fprintf和fflush
fprintf函数根据输入的格式需求将日志输入到文件中,但是在 Paddle-Mobile 中,fprintf 传入的是 stderr 参数,这样会使 fprintf输出的内容到控制台的屏幕上。
**fflush 强制刷新缓冲区,可以让日志打印内容更快的输出到屏幕上
对 ToLog 对象是友好的,可以让ToLog对象轻松的访问到私有的 buffer_对象

3.日志打印对象
Paddle-Mobile定义了两个日志打印对象(结构体),Print和ToLog,Print是实际调用定义好的宏去打印日志。

1.std::cerr 输出错误类型的信息到控制台
2. ostringstream 可以更加安全的进行类型转换
3.重写了<< 运算符,可以更方便的使用

struct Print {
  friend struct ToLog;

  template <typename T>
  Print &operator<<(T const &value) {
    buffer_ << value;
    return *this;
  }

 private:
  void print(LogLevel level) {
    // buffer_ << std::endl;
    if (level == kLOG_ERROR) {
#ifdef ANDROID
      ANDROIDLOGE(buffer_.str().c_str());
#else
      std::cerr << buffer_.str() << std::endl;
#endif
    } else {
#ifdef ANDROID
      ANDROIDLOGI(buffer_.str().c_str());
#else
      std::cout << buffer_.str() << std::endl;
#endif
    }
  }
  std::ostringstream buffer_;
};

ostringstream的例子

ostringstream oss;//创建一个流

oss<<t;//把值传递如流中

result=oss.str();//获取转换后的字符转并将其写入result

3.ToLog对象

1.explicit防止隐士的赋值
2.重写了<<云算符,并且将输入定义成模板方法,因为这些输入最后是交给 print 对象,而 print 对象最终是交给 ostringsteam 对象,模板类型可以将各种输入更好的交给ostringstream对象以便打印

struct ToLog {
  explicit ToLog(LogLevel level = kLOG_DEBUG, const std::string &info = "")
      : level_(level) {
    unsigned blanks =
        (unsigned)(level > kLOG_DEBUG ? (level - kLOG_DEBUG) * 4 : 1);
    printer_ << logs[level] << " " << info << ":" << std::string(blanks, ' ');
  }

  template <typename T>
  ToLog &operator<<(T const &value) {
    printer_ << value;
    return *this;
  }

  ~ToLog() { printer_.print(level_); }

 private:
  LogLevel level_;
  Print printer_;
};

4.定义了规范的日志打印方式
这种方式会具体定位到某个文件的某一行

#define LOG(level)                                                           \
  if (level > paddle_mobile::log_level) {                                    \
  } else                                                                     \
    paddle_mobile::ToLog(                                                    \
        level, static_cast<const std::stringstream &>(                       \
                   std::stringstream()                                       \
                   << "[file: "                                              \
                   << (strrchr(__FILE__, '/') ? (strrchr(__FILE__, '/') + 1) \
                                              : __FILE__)                    \
                   << "] [line: " << __LINE__ << "] ")                       \
                   .str())

五、如果没有开启 Debug 开关

Paddle-Mobile 对象上面的所有方法都在没有定义 PADDLE_DEBUG宏时重新定义了一份,保证在没有定义开关时,不会出现任何问题

上一篇 下一篇

猜你喜欢

热点阅读