JSBSim Flight Dynamics Model 1.3.0 (09 Apr 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
54
55/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
56CLASS IMPLEMENTATION
57%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
58
59class BufferLogger : public FGLogger
60{
61public:
62 BufferLogger() { logMessageBuffer[0] = '\0'; }
63 void FileLocation(const std::string& filename, int line) override {
64 this->filename = filename;
65 this->line = line;
66 }
67 void Message(const std::string& message) override;
68 void Format(LogFormat format) override;
69 const char* c_str(void) const noexcept { return logMessageBuffer; }
70 ~BufferLogger() override;
71
72private:
73 struct MessageToken
74 {
75 std::string_view messageItem;
76 LogFormat format = LogFormat::DEFAULT;
77 };
78
79 char logMessageBuffer[1024];
80 size_t bufferUsed = 0;
81 std::vector<MessageToken> tokens;
82 std::string filename;
83 int line = -1;
84};
85
86//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
87
88void BufferLogger::Message(const std::string& message) {
89 if (message.empty()) return;
90
91 size_t available = sizeof(logMessageBuffer) - bufferUsed - 1; // -1 for null terminator
92 size_t toCopy = std::min(message.size(), available); // Prevent buffer overflow
93
94 if (toCopy > 0) {
95 // Copy the message to the buffer
96 char* currentPos = logMessageBuffer + bufferUsed;
97 memcpy(currentPos, message.c_str(), toCopy);
98 bufferUsed += toCopy;
99 logMessageBuffer[bufferUsed] = '\0';
100 // Store the message in the tokens vector
101 auto& new_token = tokens.emplace_back();
102 new_token.messageItem = std::string_view(currentPos, toCopy);
103 }
104}
105
106//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
107
108void BufferLogger::Format(LogFormat format)
109{
110 auto& new_token = tokens.emplace_back();
111 new_token.format = format;
112}
113
114//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
115
116BufferLogger::~BufferLogger()
117{
118 if (tokens.empty()) return;
119
120 GlobalLogger->SetLevel(log_level);
121
122 if (line > 0) GlobalLogger->FileLocation(filename, line);
123
124 for (const auto& token : tokens) {
125 if (token.messageItem.empty()) {
126 GlobalLogger->Format(token.format);
127 continue;
128 }
129 GlobalLogger->Message(std::string(token.messageItem));
130 }
131 GlobalLogger->Flush();
132}
133
134//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
135
136FGLogging::FGLogging(LogLevel level)
137 : logger(GlobalLogger)
138{
139 logger->SetLevel(level);
140}
141
142//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
143
144void FGLogging::Flush(void)
145{
146 std::string message = buffer.str();
147
148 if (!message.empty()) {
149 logger->Message(message);
150 buffer.str("");
151 }
152
153 logger->Flush();
154}
155
156//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
157
158FGLogging& FGLogging::operator<<(LogFormat format)
159{
160 std::string message = buffer.str();
161
162 if (!message.empty()) {
163 logger->Message(message);
164 buffer.str("");
165 }
166
167 logger->Format(format);
168 return *this;
169}
170
171//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
172
173FGXMLLogging::FGXMLLogging(Element* el, LogLevel level)
174 : FGLogging(level)
175{
176 logger->FileLocation(el->GetFileName(), el->GetLineNumber());
177}
178
179//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
180
182 if (log_level >= min_level || log_level == LogLevel::STDOUT) {
183 switch (log_level)
184 {
185 case LogLevel::BULK:
186 case LogLevel::DEBUG:
187 case LogLevel::INFO:
188 case LogLevel::STDOUT:
189 std::cout << buffer;
190 std::cout.flush(); // Force the message to be immediately displayed in the console
191 break;
192 default:
193 std::cerr << buffer;
194 std::cerr.flush(); // Force the message to be immediately displayed in the console
195 break;
196 }
197 }
198
199 buffer.clear();
200}
201
202//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
203
204void FGLogConsole::Format(LogFormat format) {
205 switch (format)
206 {
207 case LogFormat::RED:
208 buffer.append(FGJSBBase::fgred);
209 break;
210 case LogFormat::BLUE:
211 buffer.append(FGJSBBase::fgblue);
212 break;
213 case LogFormat::BOLD:
214 buffer.append(FGJSBBase::highint);
215 break;
216 case LogFormat::NORMAL:
217 buffer.append(FGJSBBase::normint);
218 break;
219 case LogFormat::UNDERLINE_ON:
220 buffer.append(FGJSBBase::underon);
221 break;
222 case LogFormat::UNDERLINE_OFF:
223 buffer.append(FGJSBBase::underoff);
224 break;
225 case LogFormat::DEFAULT:
226 buffer.append(FGJSBBase::fgdef);
227 break;
228 case LogFormat::RESET:
229 default:
230 buffer.append(FGJSBBase::reset);
231 break;
232 }
233}
234
235//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
236
237LogException::LogException()
238: BaseException(""), FGLogging(std::make_shared<BufferLogger>())
239{
240 logger->SetLevel(LogLevel::FATAL);
241}
242
243//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
244
245LogException::LogException(LogException& other)
246: BaseException(""), FGLogging(other.logger)
247{
248 other.Flush(); // Make the data buffered in `other` accessible to all copies.
249}
250
251//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
252
253const char* LogException::what() const noexcept
254{
255 // Although using const_cast is generally discouraged, it is justified here because:
256 // 1. The what() method must be const to comply with std::exception interface
257 // 2. We need to ensure all buffered messages are flushed before returning the error message
258 // 3. Conceptually, getting the complete error message is a "logically const" operation
259 // i.e. from the user's perspective, it doesn't modify the state of the object.
260 const_cast<LogException*>(this)->Flush();
261 return static_cast<BufferLogger*>(logger.get())->c_str();
262}
263
264//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
265
266XMLLogException::XMLLogException(Element* el)
267 : LogException()
268{
269 logger->FileLocation(el->GetFileName(), el->GetLineNumber());
270}
271
272//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
273
274XMLLogException::XMLLogException(LogException& exception, Element* el)
275 : LogException(exception)
276{
277 logger->FileLocation(el->GetFileName(), el->GetLineNumber());
278}
279};
void Message(const std::string &message) override
Appends message text. May be called multiple times per log record.
Definition FGLog.cpp:88
void FileLocation(const std::string &filename, int line) override
Optionally provides source filename and line for contextual diagnostics.
Definition FGLog.cpp:63
void Format(LogFormat format) override
Applies a formatting hint to subsequent output.
Definition FGLog.cpp:108
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.
static char normint[6]
normal intensity text
Definition FGJSBBase.h:155
static char fgred[6]
red text
Definition FGJSBBase.h:167
static char fgblue[6]
blue text
Definition FGJSBBase.h:163
static char underon[5]
underlines text
Definition FGJSBBase.h:159
static char fgdef[6]
default text
Definition FGJSBBase.h:171
static char reset[5]
resets text properties
Definition FGJSBBase.h:157
static char underoff[6]
underline off
Definition FGJSBBase.h:161
static char highint[5]
highlights text
Definition FGJSBBase.h:151
void Flush(void) override
Ends the current log record and commits any buffered output.
Definition FGLog.cpp:181
void Format(LogFormat format) override
Applies a formatting hint to subsequent output.
Definition FGLog.cpp:204
Logging backend interface.
Definition FGLog.h:125
Main namespace for the JSBSim Flight Dynamics Model.
Definition FGFDMExec.cpp:71
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