1 /*
2 * Copyright (c) 2020-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
16 #include "console_log_impl.h"
17 #if IS_ENABLED(CONSOLE_LOG_OUTPUT)
18 #include "js_app_environment.h"
19 #if (FEATURE_USER_MC_LOG_PRINTF == 1)
20 #include "product_adapter.h"
21 #endif // FEATURE_USER_MC_LOG_PRINTF
22 #ifdef FEATURE_ACELITE_HI_LOG_PRINTF
23 #undef LOG_DOMAIN
24 #undef LOG_TAG
25 #define LOG_DOMAIN 0xD003B00
26 #define LOG_TAG "JS-3RD-APP"
27 #include "hilog/log.h"
28 #endif // FEATURE_ACELITE_HI_LOG_PRINTF
29 #include <stdio.h>
30 #include <string.h>
31
32 namespace OHOS {
33 namespace ACELite {
34 #ifdef CONSOLE_LOG_LINE_MAX_LENGTH
35 const int16_t LOG_BUFFER_SIZE = CONSOLE_LOG_LINE_MAX_LENGTH;
36 #else
37 const int16_t LOG_BUFFER_SIZE = 256; // use 256 as default if it's not config
38 #endif // CONSOLE_LOG_LINE_MAX_LENGTH
LogNative(const LogLevel logLevel,const jerry_value_t func,const jerry_value_t context,const jerry_value_t * args,const jerry_length_t argc)39 jerry_value_t LogNative(const LogLevel logLevel,
40 const jerry_value_t func,
41 const jerry_value_t context,
42 const jerry_value_t *args,
43 const jerry_length_t argc)
44 {
45 (void)func; /* unused */
46 (void)context; /* unused */
47
48 // print out log level if needed
49 LogOutLevel(logLevel);
50
51 const char * const nullStr = "\\u0000";
52 jerry_value_t retVal = jerry_create_undefined();
53
54 for (jerry_length_t argIndex = 0; argIndex < argc; argIndex++) {
55 jerry_value_t strVal;
56
57 if (jerry_value_is_symbol(args[argIndex])) {
58 strVal = jerry_get_symbol_descriptive_string(args[argIndex]);
59 } else {
60 strVal = jerry_value_to_string(args[argIndex]);
61 }
62
63 if (jerry_value_is_error(strVal)) {
64 /* There is no need to free the undefined value. */
65 retVal = strVal;
66 break;
67 }
68
69 jerry_length_t length = jerry_get_utf8_string_length(strVal);
70 jerry_length_t substrPos = 0;
71 const uint16_t bufLength = LOG_BUFFER_SIZE;
72 jerry_char_t substrBuf[bufLength] = {0};
73
74 do {
75 jerry_size_t substrSize =
76 jerry_substring_to_utf8_char_buffer(strVal, substrPos, length, substrBuf, bufLength - 1);
77
78 jerry_char_t *bufEndPos = substrBuf + substrSize;
79
80 /* Update start position by the number of utf-8 characters. */
81 for (jerry_char_t *bufPos = substrBuf; bufPos < bufEndPos; bufPos++) {
82 /* Skip intermediate utf-8 octets. */
83 if ((*bufPos & 0xc0) != 0x80) {
84 substrPos++;
85 }
86 }
87
88 for (jerry_char_t *bufPos = substrBuf; bufPos < bufEndPos; bufPos++) {
89 char chr = static_cast<char>(*bufPos);
90
91 if (chr != '\0') {
92 LogChar(chr, logLevel);
93 continue;
94 }
95
96 for (jerry_size_t null_index = 0; nullStr[null_index] != '\0'; null_index++) {
97 LogChar(nullStr[null_index], logLevel);
98 }
99 }
100 } while (substrPos < length);
101
102 jerry_release_value(strVal);
103 }
104 // output end
105 LogChar('\n', logLevel, true);
106 FlushOutput();
107 return retVal;
108 }
109
LogOutLevel(const LogLevel logLevel)110 void LogOutLevel(const LogLevel logLevel)
111 {
112 switch (logLevel) {
113 case LOG_LEVEL_ERR:
114 LogString(logLevel, "[Console Error] "); // console error
115 break;
116 case LOG_LEVEL_WARN:
117 LogString(logLevel, "[Console Warn] "); // console warn
118 break;
119 case LOG_LEVEL_INFO:
120 LogString(logLevel, "[Console Info] "); // console info
121 break;
122 case LOG_LEVEL_DEBUG:
123 LogString(logLevel, "[Console Debug] "); // console debug
124 break;
125 case LOG_LEVEL_TRACE:
126 LogString(logLevel, "[Console Trace] "); // console trace, this is not supported yet
127 break;
128 case LOG_LEVEL_NONE:
129 LogString(logLevel, "[Console Debug] "); // console.log(), default apply the DEBUG level
130 break;
131 default: // just return for not supported log level
132 break;
133 }
134 }
135
136 /**
137 * @brief: the str to print out.
138 *
139 * @param str the string to print out
140 */
LogString(const LogLevel logLevel,const char * const str)141 void LogString(const LogLevel logLevel, const char * const str)
142 {
143 if (str == nullptr) {
144 return;
145 }
146 #if ((FEATURE_ACELITE_HI_LOG_PRINTF == 1) || (FEATURE_USER_MC_LOG_PRINTF == 1))
147 size_t strLength = strlen(str);
148 for (size_t i = 0; i < strLength; i++) {
149 LogChar(str[i], logLevel, false);
150 }
151 #else
152 Output(logLevel, str, strlen(str));
153 #endif
154 }
155
156 static char logBuffer[LOG_BUFFER_SIZE] = {0};
157 static uint16_t logBufferIndex = 0;
158
LogChar(char c,const LogLevel logLevel,bool endFlag)159 void LogChar(char c, const LogLevel logLevel, bool endFlag)
160 {
161 logBuffer[logBufferIndex++] = c;
162 if ((logBufferIndex == (LOG_BUFFER_SIZE - 1)) || (c == '\n')) {
163 if ((c == '\n') && (logBufferIndex > 0)) {
164 logBufferIndex--; // will trace out line separator after print the content out
165 }
166 logBuffer[logBufferIndex] = '\0';
167 Output(logLevel, logBuffer, logBufferIndex);
168 logBufferIndex = 0;
169 if (c == '\n' || !endFlag) {
170 // this is the newline during the console log, need to append the loglevel prefix,
171 // example: console.log("aa\nbb");
172 #if ((FEATURE_ACELITE_HI_LOG_PRINTF != 1) && (FEATURE_USER_MC_LOG_PRINTF != 1))
173 Output(logLevel, "\n", 1); // hilog will trace our the line separator directly
174 #endif
175 if (!endFlag) {
176 LogOutLevel(logLevel);
177 }
178 }
179 }
180 }
181
182 #ifdef FEATURE_ACELITE_HI_LOG_PRINTF
OutputToHiLog(const LogLevel logLevel,const char * const str)183 static void OutputToHiLog(const LogLevel logLevel, const char * const str)
184 {
185 switch (logLevel) {
186 case LOG_LEVEL_ERR:
187 HILOG_ERROR(HILOG_MODULE_APP, "%{public}s", str);
188 break;
189 case LOG_LEVEL_WARN:
190 HILOG_WARN(HILOG_MODULE_APP, "%{public}s", str);
191 break;
192 case LOG_LEVEL_INFO:
193 HILOG_INFO(HILOG_MODULE_APP, "%{public}s", str);
194 break;
195 case LOG_LEVEL_DEBUG:
196 HILOG_DEBUG(HILOG_MODULE_APP, "%{public}s", str);
197 break;
198 case LOG_LEVEL_TRACE:
199 HILOG_INFO(HILOG_MODULE_APP, "%{public}s", str);
200 break;
201 case LOG_LEVEL_NONE:
202 HILOG_DEBUG(HILOG_MODULE_APP, "%{public}s", str);
203 break;
204 default:
205 break;
206 }
207 }
208 #elif (FEATURE_USER_MC_LOG_PRINTF == 1)
OutputToHiLog(const LogLevel logLevel,const char * const str)209 static void OutputToHiLog(const LogLevel logLevel, const char * const str)
210 {
211 switch (logLevel) {
212 case LOG_LEVEL_ERR:
213 ProductAdapter::OutputJSConsoleLog((uint8_t)(LOG_LEVEL_ERR), str);
214 break;
215 case LOG_LEVEL_WARN:
216 ProductAdapter::OutputJSConsoleLog((uint8_t)(LOG_LEVEL_WARN), str);
217 break;
218 case LOG_LEVEL_INFO:
219 ProductAdapter::OutputJSConsoleLog((uint8_t)(LOG_LEVEL_INFO), str);
220 break;
221 case LOG_LEVEL_DEBUG:
222 // fall through
223 case LOG_LEVEL_TRACE:
224 // fall through
225 case LOG_LEVEL_NONE:
226 ProductAdapter::OutputJSConsoleLog((uint8_t)(LOG_LEVEL_DEBUG), str);
227 break;
228 default:
229 break;
230 }
231 }
232 #endif
233
234 #ifdef TDD_ASSERTIONS
235 static JSLogOutputExtraHandler g_logOutputExtraHandler = nullptr;
236 // add extra hanlder for TDD test cases
RegisterJSLogOutputHandler(JSLogOutputExtraHandler extraHandler)237 void RegisterJSLogOutputHandler(JSLogOutputExtraHandler extraHandler)
238 {
239 g_logOutputExtraHandler = extraHandler;
240 }
241 #endif // TDD_ASSERTIONS
242
Output(const LogLevel logLevel,const char * const str,const uint8_t length)243 void Output(const LogLevel logLevel, const char * const str, const uint8_t length)
244 {
245 if (str == nullptr) {
246 return;
247 }
248 (void)length;
249 Debugger::GetInstance().Output(str);
250 #if ((FEATURE_ACELITE_HI_LOG_PRINTF == 1) || (FEATURE_USER_MC_LOG_PRINTF == 1))
251 OutputToHiLog(logLevel, str);
252 #endif
253 #ifdef TDD_ASSERTIONS
254 // output to extra handler if it was set by test cases
255 if (g_logOutputExtraHandler != nullptr) {
256 g_logOutputExtraHandler(logLevel, str, length);
257 }
258 #endif // TDD_ASSERTIONS
259 }
260
FlushOutput()261 void FlushOutput()
262 {
263 Debugger::GetInstance().FlushOutput();
264 }
265 } // namespace ACELite
266 } // namespace OHOS
267
268 #endif // ENABLED(CONSOLE_LOG_OUTPUT)
269