1/*
2 * Copyright (c) 2022 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 <sys/time.h>
16#include <iomanip>
17#include <map>
18#include <securec.h>
19#include <hilog/log.h>
20
21#include "hilog_common.h"
22#include "log_utils.h"
23#include "log_print.h"
24
25namespace OHOS {
26namespace HiviewDFX {
27using namespace std;
28
29static constexpr int COLOR_BLUE = 75;
30static constexpr int COLOR_DEFAULT = 231;
31static constexpr int COLOR_GREEN = 40;
32static constexpr int COLOR_ORANGE = 166;
33static constexpr int COLOR_RED = 196;
34static constexpr int COLOR_YELLOW = 226;
35static constexpr int TM_YEAR_BASE = 1900;
36static constexpr int DT_WIDTH = 2;
37static constexpr long long NS2US = 1000LL;
38static constexpr long long NS2MS = 1000000LL;
39static constexpr int MONO_WIDTH = 8;
40static constexpr int EPOCH_WIDTH = 10;
41static constexpr int MSEC_WIDTH = 3;
42static constexpr int USEC_WIDTH = 6;
43static constexpr int NSEC_WIDTH = 9;
44static constexpr int PID_WIDTH = 5;
45static constexpr int DOMAIN_WIDTH = 5;
46static constexpr int DOMAIN_SHORT_MASK = 0xFFFFF;
47static constexpr int PREFIX_LEN = 42;
48
49static inline int GetColor(uint16_t level)
50{
51    switch (LogLevel(level)) {
52        case LOG_DEBUG: return COLOR_BLUE;
53        case LOG_INFO: return COLOR_GREEN;
54        case LOG_WARN: return COLOR_ORANGE;
55        case LOG_ERROR: return COLOR_RED;
56        case LOG_FATAL: return COLOR_YELLOW;
57        default: return COLOR_DEFAULT;
58    }
59}
60
61static inline const char* GetLogTypePrefix(uint16_t type)
62{
63    switch (LogType(type)) {
64        case LOG_APP: return "A";
65        case LOG_INIT: return "I";
66        case LOG_CORE: return "C";
67        case LOG_KMSG: return "K";
68        case LOG_ONLY_PRERELEASE: return "P";
69        default: return " ";
70    }
71}
72
73static inline uint32_t ShortDomain(uint32_t d)
74{
75    return (d) & DOMAIN_SHORT_MASK;
76}
77
78static void PrintLogPrefix(const LogContent& content, const LogFormat& format, std::ostream& out)
79{
80    // 1. print day & time
81    if (format.timeFormat == FormatTime::TIME) {
82        struct tm tl;
83        time_t time = content.tv_sec;
84#if (defined( __WINDOWS__ ))
85        if (localtime_s(&tl, &time) != 0) {
86            return;
87        }
88#else
89        if (localtime_r(&time, &tl) == nullptr) {
90            return;
91        }
92        if (format.zone) {
93            out << tl.tm_zone << " ";
94        }
95#endif
96        if (format.year) {
97            out << (tl.tm_year + TM_YEAR_BASE) << "-";
98        }
99        out << setfill('0');
100        out << setw(DT_WIDTH) << (tl.tm_mon + 1) << "-" << setw(DT_WIDTH) << tl.tm_mday << " ";
101        out << setw(DT_WIDTH) << tl.tm_hour << ":" << setw(DT_WIDTH) << tl.tm_min << ":";
102        out << setw(DT_WIDTH) << tl.tm_sec;
103    } else if (format.timeFormat == FormatTime::MONOTONIC) {
104        out << setfill(' ');
105        out << setw(MONO_WIDTH) << content.mono_sec;
106    } else if (format.timeFormat == FormatTime::EPOCH) {
107        out << setfill(' ');
108        out << setw(EPOCH_WIDTH) << content.tv_sec;
109    } else {
110        out << "Invalid time format" << endl;
111        return;
112    }
113    // 1.1 print msec/usec/nsec
114    out << ".";
115    out << setfill('0');
116    if (format.timeAccuFormat == FormatTimeAccu::MSEC) {
117        out << setw(MSEC_WIDTH) << (content.tv_nsec / NS2MS);
118    } else if (format.timeAccuFormat == FormatTimeAccu::USEC) {
119        out << setw(USEC_WIDTH) << (content.tv_nsec / NS2US);
120    } else if (format.timeAccuFormat == FormatTimeAccu::NSEC) {
121        out << setw(NSEC_WIDTH) << content.tv_nsec;
122    } else {
123        out << "Invalid time accuracy format" << endl;
124        return;
125    }
126    // The kmsg logs are taken from /proc/kmsg, cannot obtain pid, tid or domain information
127    // The kmsg log printing format: 08-06 16:51:04.945 <6> [4294.967295] hungtask_base whitelist[0]-init-1
128    if (content.type != LOG_KMSG) {
129        out << setfill(' ');
130        // 2. print pid/tid
131        out << " " << setw(PID_WIDTH) << content.pid << " " << setw(PID_WIDTH) << content.tid;
132        // 3. print level
133        out << " " << LogLevel2ShortStr(content.level) << " ";
134        // 4. print log type
135        out << GetLogTypePrefix(content.type);
136        // 5. print domain
137        out << setfill('0');
138        out << hex << setw(DOMAIN_WIDTH) << ShortDomain(content.domain) << dec;
139        // 5. print tag & log
140        out << "/" << content.tag << ": ";
141    } else {
142        out << " " << content.tag << " ";
143    }
144}
145
146static void AdaptWrap(const LogContent& content, const LogFormat& format, std::ostream& out)
147{
148    if (format.wrap) {
149        out << setfill(' ');
150        out << std::setw(PREFIX_LEN + StringToWstring(content.tag).length()) << " ";
151    } else {
152        PrintLogPrefix(content, format, out);
153    }
154}
155
156void LogPrintWithFormat(const LogContent& content, const LogFormat& format, std::ostream& out)
157{
158    // set colorful log
159    if (format.colorful) {
160        out << "\x1B[38;5;" << GetColor(content.level) << "m";
161    }
162
163    const char *pHead = content.log;
164    const char *pScan = content.log;
165    // not print prefix if log is empty string or start with \n
166    if (*pScan != '\0' && *pScan != '\n') {
167        PrintLogPrefix(content, format, out);
168    }
169    // split the log content by '\n', and add log prefix(datetime, pid, tid....) to each new line
170    while (*pScan != '\0') {
171        if (*pScan == '\n') {
172            char tmp[MAX_LOG_LEN];
173            int len = static_cast<int>(pScan - pHead);
174            errno_t ret = memcpy_s(tmp, MAX_LOG_LEN - 1, pHead, len);
175            if (ret != EOK) {
176                break;
177            }
178            tmp[(MAX_LOG_LEN - 1) > len ? len : (MAX_LOG_LEN -1)] = '\0';
179            if (tmp[0] != '\0') {
180                out << tmp << endl;
181            }
182            pHead = pScan + 1;
183            if (pHead[0] != '\0' && pHead[0] != '\n') {
184                AdaptWrap(content, format, out);
185            }
186        }
187        pScan++;
188    }
189    if (pHead[0] != '\0') {
190        out << pHead;
191    }
192
193    // restore color
194    if (format.colorful) {
195        out << "\x1B[0m";
196    }
197    if (pHead[0] != '\0') {
198        out << endl;
199    }
200    return;
201}
202} // namespace HiviewDFX
203} // namespace OHOS