1/*
2 * Copyright (c) 2023 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 "texgine/utils/logger.h"
17
18#include <fstream>
19#include <iomanip>
20#include <iostream>
21#include <unistd.h>
22
23#ifdef BUILD_NON_SDK_VER
24#include <sys/syscall.h>
25#define GET_TID() syscall(__NR_gettid)
26#else
27#ifdef _WIN32
28#include <windows.h>
29#define GET_TID GetCurrentThreadId
30#endif
31
32#if defined(BUILD_SDK_MAC) || defined(BUILD_SDK_IOS)
33#include <stdlib.h>
34#include <sys/syscall.h>
35#define GET_TID() syscall(SYS_thread_selfid)
36#elif defined(BUILD_SDK_ANDROID)
37#define GET_TID() gettid()
38#else
39#ifdef __gnu_linux__
40#include <sys/types.h>
41#include <sys/syscall.h>
42#define GET_TID() syscall(SYS_gettid)
43#endif
44#endif
45
46#ifdef __APPLE__
47#define getpid getpid
48#endif
49
50#ifdef ERROR
51#undef ERROR
52#endif
53#endif
54
55#ifdef LOGGER_NO_COLOR
56#define IF_COLOR(x)
57#else
58#define IF_COLOR(x) x
59#endif
60
61namespace OHOS {
62namespace Rosen {
63namespace TextEngine {
64namespace {
65const char *GetLevelStr(enum Logger::LOG_LEVEL level)
66{
67    switch (level) {
68        case Logger::LOG_LEVEL::DEBUG:
69            return IF_COLOR("\033[37m") "D" IF_COLOR("\033[0m");
70        case Logger::LOG_LEVEL::INFO:
71            return IF_COLOR("\033[36m") "I" IF_COLOR("\033[0m");
72        case Logger::LOG_LEVEL::WARN:
73            return IF_COLOR("\033[33m") "W" IF_COLOR("\033[0m");
74        case Logger::LOG_LEVEL::ERROR:
75            return IF_COLOR("\033[31m") "E" IF_COLOR("\033[0m");
76        case Logger::LOG_LEVEL::FATAL:
77            return IF_COLOR("\033[4;31m") "F" IF_COLOR("\033[0m");
78    }
79    return "?";
80}
81} // namespace
82
83void Logger::SetToNoReturn(Logger &logger, enum LOG_PHASE phase)
84{
85    logger.return_ = false;
86}
87
88void Logger::SetToContinue(Logger &logger, enum LOG_PHASE phase)
89{
90    logger.continue_ = true;
91}
92
93void Logger::OutputByStdout(Logger &logger, enum LOG_PHASE phase)
94{
95    if (phase == LOG_PHASE::BEGIN) {
96        return;
97    }
98
99    // LOG_PHASE::END
100    if (logger.continue_ == false) {
101        std::cout << GetLevelStr(logger.GetLevel()) << " ";
102    }
103
104    std::cout << logger.str();
105    if (logger.return_) {
106        std::cout << std::endl;
107    }
108}
109
110void Logger::OutputByStderr(Logger &logger, enum LOG_PHASE phase)
111{
112    if (phase == LOG_PHASE::BEGIN) {
113        return;
114    }
115
116    // LOG_PHASE::END
117    if (logger.continue_ == false) {
118        std::cerr << GetLevelStr(logger.GetLevel()) << " ";
119    }
120
121    std::cerr << logger.str();
122    if (logger.return_) {
123        std::cerr << std::endl;
124    }
125}
126
127void Logger::OutputByFileLog(Logger &logger, enum LOG_PHASE phase)
128{
129    struct FileLogData {
130        const char *filename;
131    };
132    auto data = logger.GetData<struct FileLogData>();
133    if (phase == LOG_PHASE::BEGIN) {
134        auto filename = va_arg(logger.GetVariousArgument(), const char *);
135        data->filename = filename;
136        return;
137    }
138
139    // LOG_PHASE::END
140    std::ofstream ofs(data->filename, std::ofstream::out | std::ofstream::app);
141    if (!ofs) {
142        // open failed, errno
143        return;
144    }
145
146    if (logger.continue_ == false) {
147        ofs << GetLevelStr(logger.GetLevel()) << " ";
148    }
149
150    ofs << logger.str();
151    if (logger.return_) {
152        ofs << std::endl;
153    }
154    ofs.close();
155}
156
157void Logger::AppendFunc(Logger &logger, enum LOG_PHASE phase)
158{
159    if (phase == LOG_PHASE::BEGIN) {
160        logger << IF_COLOR("\033[34m");
161        logger.AlignFunc();
162        logger << logger.GetFunc() << IF_COLOR("\033[0m") " ";
163    }
164}
165
166void Logger::AppendFuncLine(Logger &logger, enum LOG_PHASE phase)
167{
168    if (phase == LOG_PHASE::BEGIN) {
169        logger << IF_COLOR("\033[34m");
170        logger.AlignFunc();
171        logger << logger.GetFunc() << " ";
172        logger.AlignLine();
173        logger << IF_COLOR("\033[35m") "+" << logger.GetLine() << IF_COLOR("\033[0m") " ";
174    }
175}
176
177void Logger::AppendFileLine(Logger &logger, enum LOG_PHASE phase)
178{
179    if (phase == LOG_PHASE::BEGIN) {
180        logger << IF_COLOR("\033[34m") << logger.GetFile() << " ";
181        logger.AlignLine();
182        logger << IF_COLOR("\033[35m") "+" << logger.GetLine() << IF_COLOR("\033[0m") " ";
183    }
184}
185
186void Logger::AppendFileFuncLine(Logger &logger, enum LOG_PHASE phase)
187{
188    if (phase == LOG_PHASE::BEGIN) {
189        logger << IF_COLOR("\033[34m") << logger.GetFile() << " ";
190        logger.AlignLine();
191        logger << IF_COLOR("\033[35m") "+" << logger.GetLine() << " ";
192        logger.AlignFunc();
193        logger << logger.GetFunc() << IF_COLOR("\033[0m") " ";
194    }
195}
196
197void Logger::AppendPidTid(Logger &logger, enum LOG_PHASE phase)
198{
199    if (phase == LOG_PHASE::BEGIN) {
200        logger << getpid() << ":" << GET_TID() << " ";
201    }
202}
203
204void Logger::SetScopeParam(int func, int line)
205{
206    alignFunc = func;
207    alignLine = line;
208}
209
210void Logger::EnterScope()
211{
212    std::lock_guard<std::mutex> lock(scopeMutex_);
213    scope_++;
214}
215
216void Logger::ExitScope()
217{
218    std::lock_guard<std::mutex> lock(scopeMutex_);
219    scope_--;
220}
221
222Logger::Logger(const std::string &file, const std::string &func, int line, enum LOG_LEVEL level, ...)
223{
224    *this << std::boolalpha;
225    file_ = file;
226    func_ = func;
227    line_ = line;
228    level_ = level;
229    va_start(vl_, level);
230
231    while (true) {
232        LoggerWrapperFunc f = va_arg(vl_, LoggerWrapperFunc);
233        if (f == nullptr) {
234            break;
235        }
236
237        f(*this, LOG_PHASE::BEGIN);
238        wrappers_.push_back(f);
239    }
240
241#ifdef LOGGER_ENABLE_SCOPE
242    {
243        std::lock_guard<std::mutex> lock(scopeMutex_);
244        // The number of space if enable scope
245        Align(scope_ * 2);  // 2 means multiple
246    }
247#endif
248}
249
250Logger::Logger(const Logger &logger)
251{
252    file_ = logger.file_;
253    func_ = logger.func_;
254    line_ = logger.line_;
255    level_ = logger.level_;
256    data_ = logger.data_;
257    wrappers_ = logger.wrappers_;
258    *this << logger.str();
259}
260
261Logger::Logger(Logger &&logger)
262{
263    file_ = logger.file_;
264    func_ = logger.func_;
265    line_ = logger.line_;
266    level_ = logger.level_;
267    data_ = logger.data_;
268    wrappers_ = logger.wrappers_;
269    *this << logger.str();
270
271    logger.wrappers_.clear();
272}
273
274Logger::~Logger()
275{
276    for (const auto &wrapper : wrappers_) {
277        wrapper(*this, LOG_PHASE::END);
278    }
279}
280
281const std::string &Logger::GetFile() const
282{
283    return file_;
284}
285
286const std::string &Logger::GetFunc() const
287{
288    return func_;
289}
290
291int Logger::GetLine() const
292{
293    return line_;
294}
295
296enum Logger::LOG_LEVEL Logger::GetLevel() const
297{
298    return level_;
299}
300
301va_list &Logger::GetVariousArgument()
302{
303    return vl_;
304}
305
306void Logger::Align(int num)
307{
308    if (continue_) {
309        return;
310    }
311
312    for (int32_t i = 0; i < num; i++) {
313        *this << " ";
314    }
315}
316
317void Logger::AlignLine()
318{
319    if (alignLine) {
320        auto line = GetLine();
321        auto num = line == 0 ? 1 : 0;
322        while (line) {
323            // 10 is to calculate the number of bits in the row where the function is located
324            line /= 10;
325            num++;
326        }
327        Align(alignLine - num);
328    }
329}
330
331void Logger::AlignFunc()
332{
333    if (alignFunc) {
334        Align(alignFunc - GetFunc().size());
335    }
336}
337
338ScopedLogger::ScopedLogger(NoLogger &&logger)
339{
340}
341
342ScopedLogger::ScopedLogger(NoLogger &&logger, const std::string &name)
343{
344}
345
346ScopedLogger::ScopedLogger(Logger &&logger)
347    : ScopedLogger(std::move(logger), "")
348{
349}
350
351ScopedLogger::ScopedLogger(Logger &&logger, const std::string &name)
352{
353#ifdef LOGGER_ENABLE_SCOPE
354    logger_ = new Logger(logger);
355    *logger_ << "} " << name;
356    logger << "{ ";
357#endif
358    logger << name;
359    Logger::EnterScope();
360}
361
362ScopedLogger::~ScopedLogger()
363{
364    Finish();
365}
366
367void ScopedLogger::Finish()
368{
369    if (logger_) {
370        Logger::ExitScope();
371        delete logger_;
372        logger_ = nullptr;
373    }
374}
375} // namespace TextEngine
376} // namespace Rosen
377} // namespace OHOS
378