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