1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include <cerrno>
16 #include <cstdarg>
17 #include <cstdio>
18 #include <ctime>
19 #include <fstream>
20 #include <iostream>
21 #include <mutex>
22 #include <securec.h>
23 #include <fcntl.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26
27 #ifdef __LINUX__
28 #include <atomic>
29 #endif
30
31 #ifndef __WINDOWS__
32 #include <sys/syscall.h>
33 #include <sys/types.h>
34 #else
35 #include <windows.h>
36 #include <memory.h>
37 #endif
38 #include <unistd.h>
39
40 #include "log_timestamp.h"
41 #include "hilog_trace.h"
42 #include "hilog_inner.h"
43 #include "hilog/log.h"
44 #include "hilog_common.h"
45 #include "vsnprintf_s_p.h"
46 #include "log_utils.h"
47
48 #if not (defined( __WINDOWS__ ) || defined( __MAC__ ) || defined( __LINUX__ ))
49 #include "properties.h"
50 #include "hilog_input_socket_client.h"
51 #else
52 #include "log_print.h"
53 #endif
54
55 using namespace std;
56 using namespace OHOS::HiviewDFX;
57 static RegisterFunc g_registerFunc = nullptr;
58 static LogCallback g_logCallback = nullptr;
59 static int g_logLevel = LOG_LEVEL_MIN;
60 static atomic_int g_hiLogGetIdCallCount = 0;
61 // protected by static lock guard
62 static char g_hiLogLastFatalMessage[MAX_LOG_LEN] = { 0 }; // MAX_lOG_LEN : 1024
63
64 HILOG_PUBLIC_API
GetLastFatalMessage()65 extern "C" const char* GetLastFatalMessage()
66 {
67 return g_hiLogLastFatalMessage;
68 }
69
HiLogRegisterGetIdFun(RegisterFunc registerFunc)70 int HiLogRegisterGetIdFun(RegisterFunc registerFunc)
71 {
72 if (g_registerFunc != nullptr) {
73 return -1;
74 }
75 g_registerFunc = registerFunc;
76 return 0;
77 }
78
HiLogUnregisterGetIdFun(RegisterFunc registerFunc)79 void HiLogUnregisterGetIdFun(RegisterFunc registerFunc)
80 {
81 if (g_registerFunc != registerFunc) {
82 return;
83 }
84
85 g_registerFunc = nullptr;
86 while (atomic_load(&g_hiLogGetIdCallCount) != 0) {
87 /* do nothing, just wait current callback return */
88 }
89
90 return;
91 }
92
LOG_SetCallback(LogCallback callback)93 void LOG_SetCallback(LogCallback callback)
94 {
95 g_logCallback = callback;
96 }
97
HiLogSetAppMinLogLevel(LogLevel level)98 void HiLogSetAppMinLogLevel(LogLevel level)
99 {
100 g_logLevel = level;
101 }
102
GetFinalLevel(unsigned int domain,const std::string & tag)103 static uint16_t GetFinalLevel(unsigned int domain, const std::string& tag)
104 {
105 // Priority: TagLevel > DomainLevel > GlobalLevel
106 // LOG_LEVEL_MIN is default Level
107 #if not (defined( __WINDOWS__ ) || defined( __MAC__ ) || defined( __LINUX__ ))
108 uint16_t tagLevel = GetTagLevel(tag);
109 if (tagLevel != LOG_LEVEL_MIN) {
110 return tagLevel;
111 }
112 uint16_t domainLevel = GetDomainLevel(domain);
113 if (domainLevel != LOG_LEVEL_MIN) {
114 return domainLevel;
115 }
116 // domain within the range of [DOMAIN_APP_MIN, DOMAIN_APP_MAX] is a js log,
117 // if this js log comes from debuggable hap, set the default level.
118 if ((domain >= DOMAIN_APP_MIN) && (domain <= DOMAIN_APP_MAX)) {
119 static bool isDebuggableHap = IsDebuggableHap();
120 if (isDebuggableHap) {
121 return LOG_LEVEL_MIN;
122 }
123 }
124 return GetGlobalLevel();
125 #else
126 return LOG_LEVEL_MIN;
127 #endif
128 }
129
130 #if not (defined( __WINDOWS__ ) || defined( __MAC__ ) || defined( __LINUX__ ))
HiLogFlowCtrlProcess(int len,const struct timespec & ts)131 static int HiLogFlowCtrlProcess(int len, const struct timespec &ts)
132 {
133 static uint32_t processQuota = 0;
134 static atomic_int gSumLen = 0;
135 static atomic_int gDropped = 0;
136 static atomic<LogTimeStamp> gStartTime;
137 static LogTimeStamp period(1, 0);
138 static std::atomic_flag isFirstFlag = ATOMIC_FLAG_INIT;
139 if (!isFirstFlag.test_and_set()) {
140 processQuota = GetProcessQuota(GetProgName());
141 }
142 LogTimeStamp tsStart = atomic_load(&gStartTime);
143 LogTimeStamp tsNow(ts);
144 tsStart += period;
145 /* in statistic period(1 second) */
146 if (tsNow > tsStart) { /* new statistic period, return how many lines were dropped */
147 int dropped = atomic_exchange_explicit(&gDropped, 0, memory_order_relaxed);
148 atomic_store(&gStartTime, tsNow);
149 atomic_store(&gSumLen, len);
150 return dropped;
151 } else {
152 uint32_t sumLen = (uint32_t)atomic_load(&gSumLen);
153 if (sumLen > processQuota) { /* over quota, -1 means don't print */
154 atomic_fetch_add_explicit(&gDropped, 1, memory_order_relaxed);
155 return -1;
156 } else { /* under quota, 0 means do print */
157 atomic_fetch_add_explicit(&gSumLen, len, memory_order_relaxed);
158 }
159 }
160 return 0;
161 }
162
IsNeedProcFlowCtr(const LogType type)163 static bool IsNeedProcFlowCtr(const LogType type)
164 {
165 if (type != LOG_APP) {
166 return false;
167 }
168 //debuggable hap don't perform process flow control
169 static bool isDebuggableHap = IsDebuggableHap();
170 if (IsProcessSwitchOn() && !isDebuggableHap) {
171 return true;
172 }
173 return false;
174 }
175 #else
PrintLog(HilogMsg & header,const char * tag,uint16_t tagLen,const char * fmt,uint16_t fmtLen)176 static int PrintLog(HilogMsg& header, const char *tag, uint16_t tagLen, const char *fmt, uint16_t fmtLen)
177 {
178 LogContent content = {
179 .level = header.level,
180 .type = header.type,
181 .pid = header.pid,
182 .tid = header.tid,
183 .domain = header.domain,
184 .tv_sec = header.tv_sec,
185 .tv_nsec = header.tv_nsec,
186 .mono_sec = header.mono_sec,
187 .tag = tag,
188 .log = fmt,
189 };
190 LogFormat format = {
191 .colorful = false,
192 .timeFormat = FormatTime::TIME,
193 .timeAccuFormat = FormatTimeAccu::MSEC,
194 .year = false,
195 .zone = false,
196 };
197 LogPrintWithFormat(content, format);
198 return RET_SUCCESS;
199 }
200 #endif
201
LogToKmsg(const LogLevel level,const char * tag,const char * info)202 static int LogToKmsg(const LogLevel level, const char *tag, const char* info)
203 {
204 static int fd = open("/dev/kmsg", O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
205 if (fd < 0) {
206 printf("open /dev/kmsg failed, fd=%d. \n", fd);
207 return -1;
208 }
209 char logInfo[MAX_LOG_LEN] = {0};
210 if (snprintf_s(logInfo, sizeof(logInfo), sizeof(logInfo) - 1, "<%d>%s: %s\n", level, tag, info) == -1) {
211 logInfo[sizeof(logInfo) - 2] = '\n'; // 2 add \n to tail
212 logInfo[sizeof(logInfo) - 1] = '\0';
213 }
214 #ifdef __LINUX__
215 return TEMP_FAILURE_RETRY(write(fd, logInfo, strlen(logInfo)));
216 #else
217 return write(fd, logInfo, strlen(logInfo));
218 #endif
219 }
220
HiLogPrintArgs(const LogType type,const LogLevel level,const unsigned int domain,const char * tag,const char * fmt,va_list ap)221 int HiLogPrintArgs(const LogType type, const LogLevel level, const unsigned int domain, const char *tag,
222 const char *fmt, va_list ap)
223 {
224 if ((type != LOG_APP) && ((domain < DOMAIN_OS_MIN) || (domain > DOMAIN_OS_MAX))) {
225 return -1;
226 }
227 if (!HiLogIsLoggable(domain, tag, level)) {
228 return -1;
229 }
230 if (type == LOG_KMSG) {
231 char tmpFmt[MAX_LOG_LEN] = {0};
232 // format va_list info to char*
233 if (vsnprintfp_s(tmpFmt, sizeof(tmpFmt), sizeof(tmpFmt) - 1, true, fmt, ap) == -1) {
234 tmpFmt[sizeof(tmpFmt) - 2] = '\n'; // 2 add \n to tail
235 tmpFmt[sizeof(tmpFmt) - 1] = '\0';
236 }
237 return LogToKmsg(level, tag, tmpFmt);
238 }
239
240 HilogMsg header = {0};
241 struct timespec ts = {0};
242 (void)clock_gettime(CLOCK_REALTIME, &ts);
243 struct timespec ts_mono = {0};
244 (void)clock_gettime(CLOCK_MONOTONIC, &ts_mono);
245 header.tv_sec = static_cast<uint32_t>(ts.tv_sec);
246 header.tv_nsec = static_cast<uint32_t>(ts.tv_nsec);
247 header.mono_sec = static_cast<uint32_t>(ts_mono.tv_sec);
248
249 char buf[MAX_LOG_LEN] = {0};
250 char *logBuf = buf;
251 int traceBufLen = 0;
252 int ret;
253 /* print traceid */
254 if (g_registerFunc != nullptr) {
255 uint64_t chainId = 0;
256 uint32_t flag = 0;
257 uint64_t spanId = 0;
258 uint64_t parentSpanId = 0;
259 ret = -1; /* default value -1: invalid trace id */
260 atomic_fetch_add_explicit(&g_hiLogGetIdCallCount, 1, memory_order_relaxed);
261 RegisterFunc func = g_registerFunc;
262 if (g_registerFunc != nullptr) {
263 ret = func(&chainId, &flag, &spanId, &parentSpanId);
264 }
265 atomic_fetch_sub_explicit(&g_hiLogGetIdCallCount, 1, memory_order_relaxed);
266 if (ret == 0) { /* 0: trace id with span id */
267 traceBufLen = snprintf_s(logBuf, MAX_LOG_LEN, MAX_LOG_LEN - 1, "[%llx, %llx, %llx] ",
268 (unsigned long long)chainId, (unsigned long long)spanId, (unsigned long long)parentSpanId);
269 } else if (ret != -1) { /* trace id without span id, -1: invalid trace id */
270 traceBufLen = snprintf_s(logBuf, MAX_LOG_LEN, MAX_LOG_LEN - 1, "[%llx] ",
271 (unsigned long long)chainId);
272 }
273 if (traceBufLen > 0) {
274 logBuf += traceBufLen;
275 } else {
276 traceBufLen = 0;
277 }
278 }
279
280 /* format log string */
281 #if not (defined( __WINDOWS__ ) || defined( __MAC__ ) || defined( __LINUX__ ))
282 bool debug = IsDebugOn();
283 bool priv = (!debug) && IsPrivateSwitchOn();
284 #else
285 bool priv = true;
286 #endif
287
288 #ifdef __clang__
289 /* code specific to clang compiler */
290 #pragma clang diagnostic push
291 #pragma clang diagnostic ignored "-Wformat-nonliteral"
292 #elif __GNUC__
293 /* code for GNU C compiler */
294 #pragma GCC diagnostic push
295 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
296 #endif
297 vsnprintfp_s(logBuf, MAX_LOG_LEN - traceBufLen, MAX_LOG_LEN - traceBufLen - 1, priv, fmt, ap);
298 if (g_logCallback != nullptr) {
299 g_logCallback(type, level, domain, tag, logBuf);
300 }
301 #ifdef __clang__
302 #pragma clang diagnostic pop
303 #elif __GNUC__
304 #pragma GCC diagnostic pop
305 #endif
306
307 /* fill header info */
308 auto tagLen = strnlen(tag, MAX_TAG_LEN - 1);
309 auto logLen = strnlen(buf, MAX_LOG_LEN - 1);
310 header.type = type;
311 header.level = level;
312 #ifndef __RECV_MSG_WITH_UCRED_
313 #if defined(is_ohos) && is_ohos
314 header.pid = static_cast<uint32_t>(getprocpid());
315 #elif not defined(__WINDOWS__)
316 header.pid = getpid();
317 #else
318 header.pid = static_cast<uint32_t>(GetCurrentProcessId());
319 #endif
320 #endif
321 #ifdef __WINDOWS__
322 header.tid = static_cast<uint32_t>(GetCurrentThreadId());
323 #elif defined(__MAC__)
324 uint64_t tid;
325 pthread_threadid_np(NULL, &tid);
326 header.tid = static_cast<uint32_t>(tid);
327 #elif defined(__OHOS__)
328 header.tid = static_cast<uint32_t>(getproctid());
329 #else
330 header.tid = static_cast<uint32_t>(syscall(SYS_gettid));
331 #endif
332 header.domain = domain;
333
334 if (level == LOG_FATAL) {
335 static std::mutex fatalMessageBufMutex;
336 std::lock_guard<std::mutex> lock(fatalMessageBufMutex);
337 (void)memcpy_s(g_hiLogLastFatalMessage, sizeof(g_hiLogLastFatalMessage), buf, sizeof(buf));
338 }
339
340 #if not (defined( __WINDOWS__ ) || defined( __MAC__ ) || defined( __LINUX__ ))
341 /* flow control */
342 if (!debug && IsNeedProcFlowCtr(type)) {
343 ret = HiLogFlowCtrlProcess(tagLen + logLen - traceBufLen, ts_mono);
344 if (ret < 0) {
345 return ret;
346 } else if (ret > 0) {
347 static const char P_LIMIT_TAG[] = "LOGLIMIT";
348 uint16_t level = header.level;
349 header.level = LOG_WARN;
350 char dropLogBuf[MAX_LOG_LEN] = {0};
351 if (snprintf_s(dropLogBuf, MAX_LOG_LEN, MAX_LOG_LEN - 1,
352 "==LOGS OVER PROC QUOTA, %d DROPPED==", ret) > 0) {
353 HilogWriteLogMessage(&header, P_LIMIT_TAG, strlen(P_LIMIT_TAG) + 1, dropLogBuf,
354 strnlen(dropLogBuf, MAX_LOG_LEN - 1) + 1);
355 }
356 header.level = level;
357 }
358 }
359 return HilogWriteLogMessage(&header, tag, tagLen + 1, buf, logLen + 1);
360 #else
361 return PrintLog(header, tag, tagLen + 1, buf, logLen + 1);
362 #endif
363 }
364
HiLogPrint(LogType type,LogLevel level,unsigned int domain,const char * tag,const char * fmt,...)365 int HiLogPrint(LogType type, LogLevel level, unsigned int domain, const char *tag, const char *fmt, ...)
366 {
367 int ret;
368 va_list ap;
369 va_start(ap, fmt);
370 ret = HiLogPrintArgs(type, level, domain, tag, fmt, ap);
371 va_end(ap);
372 return ret;
373 }
374
IsAppDomain(const unsigned int domain)375 static bool IsAppDomain(const unsigned int domain)
376 {
377 return ((domain >= DOMAIN_APP_MIN) && (domain <= DOMAIN_APP_MAX));
378 }
379
HiLogIsLoggable(unsigned int domain,const char * tag,LogLevel level)380 bool HiLogIsLoggable(unsigned int domain, const char *tag, LogLevel level)
381 {
382 if (IsAppDomain(domain) && level < g_logLevel) {
383 return false;
384 }
385 if ((level <= LOG_LEVEL_MIN) || (level >= LOG_LEVEL_MAX) || (tag == nullptr) || (domain >= DOMAIN_OS_MAX)) {
386 return false;
387 }
388 if (level < GetFinalLevel(domain, tag)) {
389 return false;
390 }
391 return true;
392 }
393