JSBSim Flight Dynamics Model 1.3.3 (16 Oct 2026)
An Open Source Flight Dynamics and Control Software Library in C++
Loading...
Searching...
No Matches
FGLog.cpp
1/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2
3 Module: FGLog.cpp
4 Author: Bertrand Coconnier
5 Date started: 05/03/24
6 Purpose: Manage the logging of messages
7
8 ------------- Copyright (C) 2024 Bertrand Coconnier -------------
9
10 This program is free software; you can redistribute it and/or modify it under
11 the terms of the GNU Lesser General Public License as published by the Free
12 Software Foundation; either version 2 of the License, or (at your option) any
13 later version.
14
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
18 details.
19
20 You should have received a copy of the GNU Lesser General Public License along
21 with this program; if not, write to the Free Software Foundation, Inc., 59
22 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24 Further information about the GNU Lesser General Public License can also be
25 found on the world wide web at http://www.gnu.org.
26
27FUNCTIONAL DESCRIPTION
28--------------------------------------------------------------------------------
29This is the place where the logging of messages is managed. The messages can be
30sent to the console, to a file, etc.
31
32HISTORY
33--------------------------------------------------------------------------------
3405/03/24 BC Created
35
36%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
37INCLUDES
38%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
39
40#include <iostream>
41#include <string_view>
42#include <cstring>
43
44#include "FGLog.h"
45#include "input_output/FGXMLElement.h"
46
47namespace JSBSim {
48
49thread_local FGLogger_ptr GlobalLogger = std::make_shared<FGLogConsole>();
50
51void SetLogger(FGLogger_ptr logger) { GlobalLogger = logger; }
52FGLogger_ptr GetLogger(void) { return GlobalLogger; }
53
54const char highint[5] = {27, '[', '1', 'm', '\0' };
55const char halfint[5] = {27, '[', '2', 'm', '\0' };
56const char normint[6] = {27, '[', '2', '2', 'm', '\0' };
57const char reset[5] = {27, '[', '0', 'm', '\0' };
58const char underon[5] = {27, '[', '4', 'm', '\0' };
59const char underoff[6] = {27, '[', '2', '4', 'm', '\0' };
60const char fgblue[6] = {27, '[', '3', '4', 'm', '\0' };
61const char fgcyan[6] = {27, '[', '3', '6', 'm', '\0' };
62const char fgred[6] = {27, '[', '3', '1', 'm', '\0' };
63const char fggreen[6] = {27, '[', '3', '2', 'm', '\0' };
64const char fgdef[6] = {27, '[', '3', '9', 'm', '\0' };
65
66/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
67CLASS IMPLEMENTATION
68%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
69
70class BufferLogger : public FGLogger
71{
72public:
73 BufferLogger() { logMessageBuffer[0] = '\0'; }
74 void FileLocation(const std::string& filename, int line) override {
75 this->filename = filename;
76 this->line = line;
77 }
78 void Message(const std::string& message) override;
79 void Format(LogFormat format) override;
80 const char* c_str(void) const noexcept { return logMessageBuffer; }
81 ~BufferLogger() override;
82
83private:
84 struct MessageToken
85 {
86 std::string_view messageItem;
87 LogFormat format = LogFormat::DEFAULT;
88 };
89
90 char logMessageBuffer[1024];
91 size_t bufferUsed = 0;
92 std::vector<MessageToken> tokens;
93 std::string filename;
94 int line = -1;
95};
96
97//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
98
99void BufferLogger::Message(const std::string& message) {
100 if (message.empty()) return;
101
102 size_t available = sizeof(logMessageBuffer) - bufferUsed - 1; // -1 for null terminator
103 size_t toCopy = std::min(message.size(), available); // Prevent buffer overflow
104
105 if (toCopy > 0) {
106 // Copy the message to the buffer
107 char* currentPos = logMessageBuffer + bufferUsed;
108 memcpy(currentPos, message.c_str(), toCopy);
109 bufferUsed += toCopy;
110 logMessageBuffer[bufferUsed] = '\0';
111 // Store the message in the tokens vector
112 auto& new_token = tokens.emplace_back();
113 new_token.messageItem = std::string_view(currentPos, toCopy);
114 }
115}
116
117//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
118
119void BufferLogger::Format(LogFormat format)
120{
121 auto& new_token = tokens.emplace_back();
122 new_token.format = format;
123}
124
125//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
126
127BufferLogger::~BufferLogger()
128{
129 if (tokens.empty()) return;
130
131 GlobalLogger->SetLevel(log_level);
132
133 if (line > 0) GlobalLogger->FileLocation(filename, line);
134
135 for (const auto& token : tokens) {
136 if (token.messageItem.empty()) {
137 GlobalLogger->Format(token.format);
138 continue;
139 }
140 GlobalLogger->Message(std::string(token.messageItem));
141 }
142 GlobalLogger->Flush();
143}
144
145//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
146
147FGLogging::FGLogging(LogLevel level)
148 : logger(GlobalLogger)
149{
150 logger->SetLevel(level);
151}
152
153//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
154
155void FGLogging::Flush(void)
156{
157 std::string message = buffer.str();
158
159 if (!message.empty()) {
160 logger->Message(message);
161 buffer.str("");
162 }
163
164 logger->Flush();
165}
166
167//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
168
169FGLogging& FGLogging::operator<<(LogFormat format)
170{
171 std::string message = buffer.str();
172
173 if (!message.empty()) {
174 logger->Message(message);
175 buffer.str("");
176 }
177
178 logger->Format(format);
179 return *this;
180}
181
182//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
183
184FGXMLLogging::FGXMLLogging(Element* el, LogLevel level)
185 : FGLogging(level)
186{
187 logger->FileLocation(el->GetFileName(), el->GetLineNumber());
188}
189
190//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
191
193 if (log_level >= min_level || log_level == LogLevel::STDOUT) {
194 switch (log_level)
195 {
196 case LogLevel::BULK:
197 case LogLevel::DEBUG:
198 case LogLevel::INFO:
199 case LogLevel::STDOUT:
200 std::cout << buffer;
201 std::cout.flush(); // Force the message to be immediately displayed in the console
202 break;
203 default:
204 std::cerr << buffer;
205 std::cerr.flush(); // Force the message to be immediately displayed in the console
206 break;
207 }
208 }
209
210 buffer.clear();
211}
212
213//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
214
215void FGLogConsole::Format(LogFormat format) {
216 if (!highlighting) return;
217
218 switch (format)
219 {
220 case LogFormat::RED:
221 buffer.append(fgred);
222 break;
223 case LogFormat::BLUE:
224 buffer.append(fgblue);
225 break;
226 case LogFormat::GREEN:
227 buffer.append(fggreen);
228 break;
229 case LogFormat::CYAN:
230 buffer.append(fgcyan);
231 break;
232 case LogFormat::BOLD:
233 buffer.append(highint);
234 break;
235 case LogFormat::NORMAL:
236 buffer.append(normint);
237 break;
238 case LogFormat::UNDERLINE_ON:
239 buffer.append(underon);
240 break;
241 case LogFormat::UNDERLINE_OFF:
242 buffer.append(underoff);
243 break;
244 case LogFormat::DEFAULT:
245 buffer.append(fgdef);
246 break;
247 case LogFormat::RESET:
248 default:
249 buffer.append(reset);
250 break;
251 }
252}
253
254//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
255
256LogException::LogException()
257: BaseException(""), FGLogging(std::make_shared<BufferLogger>())
258{
259 logger->SetLevel(LogLevel::FATAL);
260}
261
262//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
263
264LogException::LogException(LogException& other)
265: BaseException(""), FGLogging(other.logger)
266{
267 other.Flush(); // Make the data buffered in `other` accessible to all copies.
268}
269
270//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
271
272const char* LogException::what() const noexcept
273{
274 // Although using const_cast is generally discouraged, it is justified here because:
275 // 1. The what() method must be const to comply with std::exception interface
276 // 2. We need to ensure all buffered messages are flushed before returning the error message
277 // 3. Conceptually, getting the complete error message is a "logically const" operation
278 // i.e. from the user's perspective, it doesn't modify the state of the object.
279 const_cast<LogException*>(this)->Flush();
280 return static_cast<BufferLogger*>(logger.get())->c_str();
281}
282
283//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
284
285XMLLogException::XMLLogException(Element* el)
286 : LogException()
287{
288 logger->FileLocation(el->GetFileName(), el->GetLineNumber());
289}
290
291//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
292
293XMLLogException::XMLLogException(LogException& exception, Element* el)
294 : LogException(exception)
295{
296 logger->FileLocation(el->GetFileName(), el->GetLineNumber());
297}
298};
void Message(const std::string &message) override
Appends message text. May be called multiple times per log record.
Definition FGLog.cpp:99
void FileLocation(const std::string &filename, int line) override
Optionally provides source filename and line for contextual diagnostics.
Definition FGLog.cpp:74
void Format(LogFormat format) override
Applies a formatting hint to subsequent output.
Definition FGLog.cpp:119
int GetLineNumber(void) const
Returns the line number at which the element has been defined.
const std::string & GetFileName(void) const
Returns the name of the file in which the element has been read.
void Flush(void) override
Ends the current log record and commits any buffered output.
Definition FGLog.cpp:192
void Format(LogFormat format) override
Applies a formatting hint to subsequent output.
Definition FGLog.cpp:215
Logging backend interface.
Definition FGLog.h:125
Main namespace for the JSBSim Flight Dynamics Model.
Definition FGFDMExec.cpp:73
FGLogger_ptr GetLogger(void)
Returns the active logger for the current thread.
Definition FGLog.cpp:52
void SetLogger(FGLogger_ptr logger)
Sets the active logger for the current thread.
Definition FGLog.cpp:51