CustusX  2023.01.05-dev+develop.0da12
An IGT application
cxLogFile.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) SINTEF Department of Medical Technology.
5 All rights reserved.
6 
7 CustusX is released under a BSD 3-Clause license.
8 
9 See Lisence.txt (https://github.com/SINTEFMedtek/CustusX/blob/master/License.txt) for details.
10 =========================================================================*/
11 
12 #include "cxLogFile.h"
13 
14 #include <iostream>
15 #include <QTextStream>
16 #include <QFileInfo>
17 #include "cxTime.h"
18 #include "cxEnumConversion.h"
19 
20 
21 namespace cx
22 {
23 
25  mFilePosition(0)
26 {
27 
28 }
29 
30 LogFile LogFile::fromChannel(QString path, QString channel)
31 {
32  LogFile retval;
33  retval.mPath = path;
34  retval.mChannel = channel;
35  return retval;
36 }
37 
38 QString LogFile::getFilename() const
39 {
40  return QString("%1/org.custusx.log.%2.txt").arg(mPath).arg(mChannel);
41 }
42 
43 LogFile LogFile::fromFilename(QString filename)
44 {
45  LogFile retval;
46  retval.mPath = QFileInfo(filename).path();
47  retval.mChannel = QFileInfo(filename).completeBaseName().split(".").back();
48  return retval;
49 }
50 
52 {
53  QString timestamp = QDateTime::currentDateTime().toString(timestampMilliSecondsFormatNice());
54  QString formatInfo = "[timestamp][source info][severity][thread] <text> ";
55  QString text = QString("-------> Logging initialized [%1], format: %2\n").arg(timestamp).arg(formatInfo);
56  bool success = this->appendToLogfile(this->getFilename(), text);
57 // return success;
58 }
59 
60 void LogFile::write(Message message)
61 {
62  QString text = this->formatMessage(message) + "\n";
63  this->appendToLogfile(this->getFilename(), text);
64 }
65 
66 bool LogFile::isWritable() const
67 {
68  QString filename = this->getFilename();
69  if (filename.isEmpty())
70  return false;
71 
72  QFile file(filename);
73  return file.open(QFile::WriteOnly | QFile::Append);
74 }
75 
76 
77 QString LogFile::timestampFormat() const
78 {
79  return "hh:mm:ss.zzz";
80 }
81 
82 QString LogFile::formatMessage(Message msg)
83 {
84  QString retval;
85 
86  // timestamp in front
87  retval += QString("[%1]").arg(msg.getTimeStamp().toString(this->timestampFormat()));
88 
89  retval += "\t";
90  if (!msg.mThread.isEmpty())
91  retval += QString("[%1]").arg(msg.mThread);
92 
93  retval += "\t";
94  if (!msg.mSourceFile.isEmpty())
95  retval += QString("[%1:%2]").arg(msg.mSourceFile).arg(msg.mSourceLine);
96 
97  retval += "\t";
98  if (!msg.mSourceFunction.isEmpty())
99  retval += QString("[%1]").arg(msg.mSourceFunction);
100 
101  retval += "\t" + QString("[%1]").arg(qstring_cast(msg.getMessageLevel()));
102 
103  // add message text at end.
104  retval += " " + msg.getText();
105 
106  return retval;
107 }
108 
111 bool LogFile::appendToLogfile(QString filename, QString text)
112 {
113  if (filename.isEmpty())
114  return false;
115 
116  QFile file(filename);
117  QTextStream stream;
118 
119  if (!file.open(QFile::WriteOnly | QFile::Append))
120  {
121  return false;
122  }
123 
124  stream.setDevice(&file);
125  stream << text;
126  stream << flush;
127 
128  return true;
129 }
130 
131 QRegExp LogFile::getRX_Timestamp() const
132 {
133  return QRegExp("\\[(\\d\\d:\\d\\d:\\d\\d\\.\\d\\d\\d)\\]");
134 }
135 
136 std::vector<Message> LogFile::readMessages()
137 {
138  QString text = this->readFileTail();
139 
140 // text = this->removeEarlierSessionsAndSetStartTime(text);
141  if (text.endsWith("\n"))
142  text.chop(1); // remove endline at end of text, in order to get false linebreaks after each file
143 
144  QStringList lines = text.split("\n");
145 
146  QRegExp rx_ts = this->getRX_Timestamp();
147 
148  std::vector<Message> retval;
149 
150  for (int i=0; i<lines.size(); ++i)
151  {
152  QString line = lines[i];
153 
154  QDateTime timestamp = this->readTimestampFromSessionStartLine(lines[i]);
155  if (timestamp.isValid())
156  {
157  mInitTimestamp = timestamp;
158  Message msg(QString("Session initialized: %1").arg(mChannel), mlSUCCESS);
159  msg.mChannel = mChannel;
160  msg.mTimeStamp = timestamp;
161  msg.mThread = "";
162  retval.push_back(msg);
163  continue;
164  }
165 
166  if (line.count(rx_ts))
167  {
168  Message msg = this->readMessageFirstLine(lines[i]);
169  msg.mChannel = mChannel;
170  retval.push_back(msg);
171  }
172  else
173  {
174  if (!retval.empty())
175  retval.back().mText += "\n"+line;
176  }
177  }
178 
179  return retval;
180 }
181 
182 Message LogFile::readMessageFirstLine(QString line)
183 {
184  MESSAGE_LEVEL level = this->readMessageLevel(line);
185  if (level==mlCOUNT)
186  return Message(line, mlINFO);
187  QString levelSymbol = QString("[%1]").arg(enum2string<MESSAGE_LEVEL>(level));
188  QStringList parts = line.split(levelSymbol);
189 
190  if (parts.size()<2)
191  return Message(line, level);
192 
193  QString description = parts[1];
194  QStringList fields = parts[0].split("\t");
195 
196  Message retval(description, level);
197 
198  this->parseTimestamp(this->getIndex(fields, 0), &retval);
199  this->parseThread(this->getIndex(fields, 1), &retval);
200  this->parseSourceFileLine(this->getIndex(fields, 2), &retval);
201  this->parseSourceFunction(this->getIndex(fields, 3), &retval);
202 
203  return retval;
204 }
205 
206 QString LogFile::getIndex(const QStringList& list, int index)
207 {
208  if (0>index || index >= list.size())
209  return "";
210  QString field = list[index];
211  if (field.startsWith("["))
212  field.remove(0, 1);
213  if (field.endsWith("]"))
214  field.chop(1);
215  return field;
216 }
217 
218 void LogFile::parseTimestamp(QString text, Message* retval)
219 {
220  if (text.isEmpty())
221  return;
222 
223  retval->mTimeStamp = mInitTimestamp; // reuse date from init, as this is not part of each line
224  QTime time = QTime::fromString(text, this->timestampFormat());
225  retval->mTimeStamp.setTime(time);
226 }
227 
228 void LogFile::parseThread(QString text, Message* retval)
229 {
230  retval->mThread = text;
231 }
232 
233 void LogFile::parseSourceFileLine(QString text, Message* retval)
234 {
235  if (!text.count(":"))
236  return;
237 
238  QStringList parts = text.split(":");
239  bool ok = 0;
240  retval->mSourceLine = parts.back().toInt(&ok);
241  if (!ok)
242  return;
243  parts.removeLast();
244  retval->mSourceFile = parts.join(":");
245 }
246 
247 void LogFile::parseSourceFunction(QString text, Message* retval)
248 {
249  if (!text.count("("))
250  return;
251  retval->mSourceFunction = text;
252 }
253 
254 MESSAGE_LEVEL LogFile::readMessageLevel(QString line)
255 {
256  QStringList levels;
257  for (int i=0; i<mlCOUNT; ++i)
258  levels << enum2string<MESSAGE_LEVEL>((MESSAGE_LEVEL)(i));
259  QRegExp rx_level(QString("\\[(%1)\\]").arg(levels.join("|")));
260 
261  int pos = rx_level.indexIn(line);
262  QStringList hits = rx_level.capturedTexts();
263 
264  if (hits.size()<2)
265  return mlCOUNT;
266 
267  return string2enum<MESSAGE_LEVEL>(hits[1]);
268 }
269 
270 QDateTime LogFile::readTimestampFromSessionStartLine(QString text)
271 {
272  QString sessionStartSymbol("------->");
273  if (!text.startsWith(sessionStartSymbol))
274  return QDateTime();
275 
276  QRegExp rx_ts_start("\\[([^\\]]*)");
277  if (text.indexOf(rx_ts_start) < 0)
278  return QDateTime();
279 
280  QString rawTime = rx_ts_start.cap(1);
281  QString format = timestampMilliSecondsFormatNice();
282  QDateTime ts = QDateTime::fromString(rawTime, format);
283 
284  return ts;
285 }
286 
287 QString LogFile::readFileTail()
288 {
289  QFile file(this->getFilename());
290  file.open(QIODevice::ReadOnly);
291 
292  file.seek(mFilePosition);
293  QString text = file.readAll();
294  mFilePosition = file.pos();
295 
296  return text;
297 }
298 
299 
300 } //End namespace cx
QString qstring_cast(const T &val)
mlSUCCESS
Definition: cxDefinitions.h:67
std::vector< Message > readMessages()
Definition: cxLogFile.cpp:136
QString mSourceFile
Definition: cxLogMessage.h:77
QString getText() const
The raw message.
void write(Message message)
Definition: cxLogFile.cpp:60
QString mThread
Definition: cxLogMessage.h:75
void writeHeader()
Definition: cxLogFile.cpp:51
QDateTime getTimeStamp() const
The time at which the message was created.
QString mChannel
Definition: cxLogMessage.h:74
static LogFile fromFilename(QString filename)
Definition: cxLogFile.cpp:43
static LogFile fromChannel(QString path, QString channel)
Definition: cxLogFile.cpp:30
cx::Message Message
Definition: cxLogMessage.h:86
QDateTime mTimeStamp
Definition: cxLogMessage.h:71
QString getFilename() const
Definition: cxLogFile.cpp:38
mlINFO
Definition: cxDefinitions.h:67
QString mSourceFunction
Definition: cxLogMessage.h:78
bool isWritable() const
Definition: cxLogFile.cpp:66
QString timestampMilliSecondsFormatNice()
Definition: cxTime.cpp:30
MESSAGE_LEVEL getMessageLevel() const
The category of the message.
Namespace for all CustusX production code.