1# init运行时日志规范化
2## 概述
3### 功能简介
4日志的基本功能就是记录init启动中的关键节点,以及定位故障问题。
5- 基于日志可以定位故障问题,可以查看各子系统启动时长,命令执行时长等。
6- 可以查看不同模块的日志tag,如param、uevent、module等。
7- 输出关键阶段日志,如第一阶段启动日志、required partition设备节点、uevent创建日志、服务启动日志等。
8- 日志等级可控,根据需要输出不同级别日志,目前日志级别分为INIT_DEBUG、INIT_INFO、INIT_WARN、INIT_ERROR、INIT_FATAL。
9
10### 基本概念
11
12init日志根据OpenHarmony版本不同实现方式不同。
13- 对于OpenHarmony标准系统版本,init日志采用内核的dmesg log实现。
14- 对于OpenHarmony LiteOS小型系统版本init日志采用hilog接口实现。
15- 对于OpenHarmony LiteOS轻量系统版本init日志采用printf接口实现。
16
17### 日志类型介绍
18  Init的日志记录主要分为hilog 和demsg, hilog主要记录系统业务流程相关的日志,demsg 记录内核相关的日志。
19### 日志级别的控制
20  init 日志级别分为五级,可以通过设置(INIT_DEBUG_LEVEL)persist.init.debug.loglevel参数来控制
21
22  ```
23  InitLogLevel:
24    INIT_DEBUG = 0,
25    INIT_INFO,
26    INIT_WARN,
27    INIT_ERROR,
28    INIT_FATAL
29  ```
30
31  Kmsg 日志级别:
32
33  ```
34  "<7>" =====> "DEBUG"
35  "<6>" =====> "INFO"
36  "<4>" =====> "WARNING"
37  "<3>" =====> "ERROR"
38  "<3>" =====> "FATAL"
39  ```
40### 日志控制的关键宏
41  INIT_DMESG 控制是否记录内核日志 /dev/kmsg
42  INIT_FILE  控制是否将日志写入文件 /data/init_agent/begetctl.log
43  INIT_AGENT 控制是否走Hilog记录日志
44
45  关键接口:
46  ```
47    void EnableInitLog(InitLogLevel level) 使能log
48
49    void SetInitLogLevel(InitLogLevel level) 设置log 的级别,控制日志的输出
50
51    void StartupLog(InitLogLevel logLevel, uint32_t domain, const char *tag, const char *fmt, ...) 是init log的入口
52  ```
53  STARTUP_LOGI 是对StartupLog 定义的宏,在头文件/base/startup/init/interfaces/innerkits/include/beget_ext.h中定义,其他log都是基于STARTUP_LOGI这个宏重定义的:
54
55  - deviceInfo模块(init/device_info/idevice_info.h):
56  ```
57    DINFO_LOGI
58    DINFO_LOGE
59    DINFO_LOGV
60  ```
61  - Paran Js接口模块(init/interfaces/kits/jskits/src/native_parameters_js.h):
62  ```
63    PARAM_JS_LOGI
64    PARAM_JS_LOGE
65    PARAM_JS_LOGV
66    PARAM_JS_LOGW
67  ```
68  - Shell模块(init/services/begetctl/shell/shell_utils.h):
69  ```
70    BSH_LOGI
71    BSH_LOGE
72    BSH_LOGV
73  ```
74  - LoopEvent模块(init/services/loopevent/utils/le_utils.h):
75  ```
76    LE_LOGI
77    LE_LOGE
78    LE_LOGV
79  ```
80  - Plugin 模块(init/services/modules/plugin_adapter.h):
81  ```
82    PLUGIN_LOGI
83    PLUGIN_LOGE
84    PLUGIN_LOGV
85    PLUGIN_LOGW
86  ```
87  - Param 模块(init/services/param/include/param_utils.h):
88  ```
89    PARAM_LOGI
90    PARAM_LOGE
91    PARAM_LOGV
92    PARAM_LOGW
93  ```
94  - ParameWatcher 模块(init/services/param/watcher/include/watcher_utils.h):
95  ```
96    WATCHER_LOGI
97    WATCHER_LOGE
98    WATCHER_LOGV
99  ```
100  - Init 流程部分的log 也是直接基于StartupLog 函数定义的宏,主要有(init/services/log/init_log.h):
101  ```
102    INIT_LOGV
103    INIT_LOGI
104    INIT_LOGW
105    INIT_LOGE
106    INIT_LOGF
107  ```
108
109### 约束与限制
110111
112## 开发指导
113### 场景介绍
114init log主要应用在init的启动过程中,启动相关模块(param、ueventd、module等)中,以及对外提供的begetutils接口中。
115
116### 接口说明
117
118**表1** log接口说明
119   | 接口 | 接口格式和示例 | 说明 |
120   | -------- | -------- | -------- |
121   | INIT_LOGV | INIT_LOGV("Add %s to job %s", service->name, jobName); | 输出debug log。 |
122   | INIT_LOGI | INIT_LOGI("Start init first stage."); | 输出info log。 |
123   | INIT_LOGW | INIT_LOGW("initialize signal handler failed"); | 输出warning log。 |
124   | INIT_LOGE | INIT_LOGE("Failed to format other opt"); | 输出err log。 |
125   | INIT_LOGF | INIT_LOGF("Failed to init system"); | 输出fatal log。 |
126   | INIT_ERROR_CHECK | INIT_ERROR_CHECK(ctx != NULL, return NULL, "Failed to get cmd args "); | 判断 ctx != NULL 不成立的情况下输出log,同时执行 return NULL。 |
127   | INIT_INFO_CHECK | INIT_INFO_CHECK(sockopt != NULL, return SERVICE_FAILURE, "Failed to malloc for service %s", service->name); | 判断 sockopt != NULL 不成立的情况下输出log,同时执行 return SERVICE_FAILURE。 |
128   | INIT_WARNING_CHECK | INIT_WARNING_CHECK(argsCount <= SPACES_CNT_IN_CMD_MAX, argsCount = SPACES_CNT_IN_CMD_MAX, "Too much arguments for command, max number is %d", SPACES_CNT_IN_CMD_MAX); | 判断 argsCount <= SPACES_CNT_IN_CMD_MAX 不成立的情况下输出log,同时执行 argsCount = SPACES_CNT_IN_CMD_MAX。 |
129   | INIT_CHECK | INIT_CHECK(arg != NULL, return NULL); | 判断arg != NULL 不成立的情况下执行 return NULL。 |
130   | INIT_CHECK_RETURN_VALUE | INIT_CHECK_RETURN_VALUE(errno == 0, -1); | 判断errno == 0 不成立的情况下执行 return -1。 |
131   | INIT_CHECK_ONLY_RETURN | INIT_CHECK_ONLY_RETURN(cmd != NULL); | 判断cmd != NULL 不成立的情况下执行 return。 |
132   | INIT_CHECK_ONLY_ELOG | INIT_CHECK_ONLY_ELOG(execv(argv[0], argv) == 0, "execv %s failed! err %d.", argv[0], errno); | 判断execv(argv[0], argv) == 0 不成立的情况下只打印log "execv %s failed! err %d."。 |
133
134### 开发实例
135
136   1. 调用接口打印日志
137
138      例如在 //base/startup/init/services/init/standard/init.c中调用接口INIT_LOGI("Start init first stage.")打印日志。
139       ```c
140       void SystemPrepare(void)
141       {
142          MountBasicFs();
143          CreateDeviceNode();
144          LogInit();
145          // Make sure init log always output to /dev/kmsg.
146          EnableDevKmsg();
147          INIT_LOGI("Start init first stage.");
148          // Only ohos normal system support
149          // two stages of init.
150          // If we are in updater mode, only one stage of init.
151          if (InUpdaterMode() == 0) {
152              StartInitSecondStage();
153          }
154       }
155       ```
156      通过dmesg可以查看打印的log,"Start init first stage."。
157
158  2. 通过命令设置日志等级
159
160     通过命令begetctl setloglevel level,其中level对应log的等级0~4,即INIT_DEBUG,INIT_INFO,INIT_WARN,INIT_ERROR,INIT_FATAL。
161
162     设置完成之后init的g_logLevel等级立即生效,上述log接口中log等级大于等于g_logLevel才会打印日志。例如:begetctl setloglevel 3,即设置log等级为INIT_ERROR,则上述的log接口中只有INIT_LOGE、INIT_LOGF才会打印log。
163
164     系统重启之后在init.cfg中"load_persist_params "命令之后生效设置的log等级。
165
166