// SPDX-License-Identifier: LGPL-2.1-or-later /*************************************************************************** * Copyright (c) 2002 Jürgen Riegel * * * * This file is part of the FreeCAD CAx development system. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License (LGPL) * * as published by the Free Software Foundation; either version 2 of * * the License, or (at your option) any later version. * * for detail see the LICENCE text file. * * * * FreeCAD is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with FreeCAD; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * * USA * * * ***************************************************************************/ #include #ifdef FC_OS_WIN32 # include #endif #include #include #include #include "ConsoleObserver.h" #include "Interpreter.h" #include "Tools.h" using namespace Base; //========================================================================= // some special observers ConsoleObserverFile::ConsoleObserverFile(const char* sFileName) : cFileStream(Base::FileInfo(sFileName)) // can be in UTF8 { if (!cFileStream.is_open()) { Console().warning("Cannot open log file '%s'.\n", sFileName); } // mark the file as a UTF-8 encoded file unsigned char bom[3] = {0xef, 0xbb, 0xbf}; cFileStream.write(reinterpret_cast(bom), 3 * sizeof(char)); } ConsoleObserverFile::~ConsoleObserverFile() { cFileStream.close(); } void ConsoleObserverFile::sendLog( const std::string& notifiername, const std::string& msg, LogStyle level, IntendedRecipient recipient, ContentType content ) { (void)notifiername; // Do not log translated messages, or messages intended only to the user to log file if (recipient == IntendedRecipient::User || content == ContentType::Translated) { return; } std::string prefix; switch (level) { case LogStyle::Warning: prefix = "Wrn: "; break; case LogStyle::Message: prefix = "Msg: "; break; case LogStyle::Error: prefix = "Err: "; break; case LogStyle::Log: prefix = "Log: "; break; case LogStyle::Critical: prefix = "Critical: "; break; default: break; } cFileStream << prefix << msg; cFileStream.flush(); } ConsoleObserverStd::ConsoleObserverStd() : #if defined(FC_OS_WIN32) useColorStderr(true) #elif defined(FC_OS_LINUX) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) useColorStderr(isatty(STDERR_FILENO)) #else useColorStderr(false) #endif { bLog = false; } ConsoleObserverStd::~ConsoleObserverStd() = default; void ConsoleObserverStd::sendLog( const std::string& notifiername, const std::string& msg, LogStyle level, IntendedRecipient recipient, ContentType content ) { (void)notifiername; // Do not log translated messages, or messages intended only to the user to std log if (recipient == IntendedRecipient::User || content == ContentType::Translated) { return; } switch (level) { case LogStyle::Warning: this->Warning(msg.c_str()); break; case LogStyle::Message: this->Message(msg.c_str()); break; case LogStyle::Error: this->Error(msg.c_str()); break; case LogStyle::Log: this->Log(msg.c_str()); break; case LogStyle::Critical: this->Critical(msg.c_str()); break; default: break; } } void ConsoleObserverStd::Message(const char* sMsg) { printf("%s", sMsg); } void ConsoleObserverStd::Warning(const char* sWarn) { if (useColorStderr) { #if defined(FC_OS_WIN32) ::SetConsoleTextAttribute(::GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE); #elif defined(FC_OS_LINUX) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) fprintf(stderr, "\033[1;33m"); #endif } fprintf(stderr, "%s", sWarn); if (useColorStderr) { #if defined(FC_OS_WIN32) ::SetConsoleTextAttribute( ::GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE ); #elif defined(FC_OS_LINUX) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) fprintf(stderr, "\033[0m"); #endif } } void ConsoleObserverStd::Error(const char* sErr) { if (useColorStderr) { #if defined(FC_OS_WIN32) ::SetConsoleTextAttribute( ::GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_RED | FOREGROUND_INTENSITY ); #elif defined(FC_OS_LINUX) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) fprintf(stderr, "\033[1;31m"); #endif } fprintf(stderr, "%s", sErr); if (useColorStderr) { #if defined(FC_OS_WIN32) ::SetConsoleTextAttribute( ::GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE ); #elif defined(FC_OS_LINUX) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) fprintf(stderr, "\033[0m"); #endif } } void ConsoleObserverStd::Log(const char* sLog) { if (useColorStderr) { #if defined(FC_OS_WIN32) ::SetConsoleTextAttribute(::GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN); #elif defined(FC_OS_LINUX) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) fprintf(stderr, "\033[1;36m"); #endif } fprintf(stderr, "%s", sLog); if (useColorStderr) { #if defined(FC_OS_WIN32) ::SetConsoleTextAttribute( ::GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE ); #elif defined(FC_OS_LINUX) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) fprintf(stderr, "\033[0m"); #endif } } void ConsoleObserverStd::Critical(const char* sCritical) { if (useColorStderr) { #if defined(FC_OS_WIN32) ::SetConsoleTextAttribute(::GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE); #elif defined(FC_OS_LINUX) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) fprintf(stderr, "\033[1;33m"); #endif } fprintf(stderr, "%s", sCritical); if (useColorStderr) { #if defined(FC_OS_WIN32) ::SetConsoleTextAttribute( ::GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE ); #elif defined(FC_OS_LINUX) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) fprintf(stderr, "\033[0m"); #endif } } RedirectStdOutput::RedirectStdOutput() { buffer.reserve(80); } int RedirectStdOutput::overflow(int ch) { if (ch != EOF) { buffer.push_back(static_cast(ch)); } return ch; } int RedirectStdOutput::sync() { // Print as log as this might be verbose if (!buffer.empty() && buffer.back() == '\n') { Base::Console().log("%s", buffer.c_str()); buffer.clear(); } return 0; } RedirectStdLog::RedirectStdLog() { buffer.reserve(80); } int RedirectStdLog::overflow(int ch) { if (ch != EOF) { buffer.push_back(static_cast(ch)); } return ch; } int RedirectStdLog::sync() { // Print as log as this might be verbose if (!buffer.empty() && buffer.back() == '\n') { Base::Console().log("%s", buffer.c_str()); buffer.clear(); } return 0; } RedirectStdError::RedirectStdError() { buffer.reserve(80); } int RedirectStdError::overflow(int ch) { if (ch != EOF) { buffer.push_back(static_cast(ch)); } return ch; } int RedirectStdError::sync() { if (!buffer.empty() && buffer.back() == '\n') { Base::Console().error("%s", buffer.c_str()); buffer.clear(); } return 0; } //--------------------------------------------------------- std::stringstream& LogLevel::prefix(std::stringstream& str, const char* src, int line) { static FC_TIME_POINT s_tstart; static bool s_timing = false; if (print_time) { if (!s_timing) { s_timing = true; _FC_TIME_INIT(s_tstart); } auto tnow = std::chrono::FC_TIME_CLOCK::now(); auto dc = std::chrono::duration_cast(tnow - s_tstart); str << dc.count() << ' '; } if (print_tag) { str << '<' << tag << "> "; } if (print_src == 2) { Base::PyGILStateLocker lock; PyFrameObject* frame = PyEval_GetFrame(); if (frame) { line = PyFrame_GetLineNumber(frame); #if PY_VERSION_HEX < 0x030b0000 src = PyUnicode_AsUTF8(frame->f_code->co_filename); #else PyCodeObject* code = PyFrame_GetCode(frame); src = PyUnicode_AsUTF8(code->co_filename); Py_DECREF(code); #endif } } if (print_src && !Base::Tools::isNullOrEmpty(src)) { #ifdef FC_OS_WIN32 const char* _f = std::strrchr(src, '\\'); #else const char* _f = std::strrchr(src, '/'); #endif str << (_f ? _f + 1 : src) << "(" << line << "): "; } return str; }