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