Fraxinus  16.5.0-fx-rc1
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 
33 #include "cxLogFile.h"
34 
35 #include <iostream>
36 #include <QTextStream>
37 #include <QFileInfo>
38 #include "cxTime.h"
39 #include "cxEnumConverter.h"
40 #include "cxDefinitionStrings.h"
41 
42 
43 namespace cx
44 {
45 
47  mFilePosition(0)
48 {
49 
50 }
51 
52 LogFile LogFile::fromChannel(QString path, QString channel)
53 {
54  LogFile retval;
55  retval.mPath = path;
56  retval.mChannel = channel;
57  return retval;
58 }
59 
60 QString LogFile::getFilename() const
61 {
62  return QString("%1/org.custusx.log.%2.txt").arg(mPath).arg(mChannel);
63 }
64 
65 LogFile LogFile::fromFilename(QString filename)
66 {
67  LogFile retval;
68  retval.mPath = QFileInfo(filename).path();
69  retval.mChannel = QFileInfo(filename).completeBaseName().split(".").back();
70  return retval;
71 }
72 
74 {
75  QString timestamp = QDateTime::currentDateTime().toString(timestampMilliSecondsFormatNice());
76  QString formatInfo = "[timestamp][source info][severity][thread] <text> ";
77  QString text = QString("-------> Logging initialized [%1], format: %2\n").arg(timestamp).arg(formatInfo);
78  bool success = this->appendToLogfile(this->getFilename(), text);
79 // return success;
80 }
81 
82 void LogFile::write(Message message)
83 {
84  QString text = this->formatMessage(message) + "\n";
85  this->appendToLogfile(this->getFilename(), text);
86 }
87 
88 bool LogFile::isWritable() const
89 {
90  QString filename = this->getFilename();
91  if (filename.isEmpty())
92  return false;
93 
94  QFile file(filename);
95  return file.open(QFile::WriteOnly | QFile::Append);
96 }
97 
98 
99 QString LogFile::timestampFormat() const
100 {
101  return "hh:mm:ss.zzz";
102 }
103 
104 QString LogFile::formatMessage(Message msg)
105 {
106  QString retval;
107 
108  // timestamp in front
109  retval += QString("[%1]").arg(msg.getTimeStamp().toString(this->timestampFormat()));
110 
111  retval += "\t";
112  if (!msg.mThread.isEmpty())
113  retval += QString("[%1]").arg(msg.mThread);
114 
115  retval += "\t";
116  if (!msg.mSourceFile.isEmpty())
117  retval += QString("[%1:%2]").arg(msg.mSourceFile).arg(msg.mSourceLine);
118 
119  retval += "\t";
120  if (!msg.mSourceFunction.isEmpty())
121  retval += QString("[%1]").arg(msg.mSourceFunction);
122 
123  retval += "\t" + QString("[%1]").arg(qstring_cast(msg.getMessageLevel()));
124 
125  // add message text at end.
126  retval += " " + msg.getText();
127 
128  return retval;
129 }
130 
133 bool LogFile::appendToLogfile(QString filename, QString text)
134 {
135  if (filename.isEmpty())
136  return false;
137 
138  QFile file(filename);
139  QTextStream stream;
140 
141  if (!file.open(QFile::WriteOnly | QFile::Append))
142  {
143  return false;
144  }
145 
146  stream.setDevice(&file);
147  stream << text;
148  stream << flush;
149 
150  return true;
151 }
152 
153 QRegExp LogFile::getRX_Timestamp() const
154 {
155  return QRegExp("\\[(\\d\\d:\\d\\d:\\d\\d\\.\\d\\d\\d)\\]");
156 }
157 
158 std::vector<Message> LogFile::readMessages()
159 {
160  QString text = this->readFileTail();
161 
162 // text = this->removeEarlierSessionsAndSetStartTime(text);
163  if (text.endsWith("\n"))
164  text.chop(1); // remove endline at end of text, in order to get false linebreaks after each file
165 
166  QStringList lines = text.split("\n");
167 
168  QRegExp rx_ts = this->getRX_Timestamp();
169 
170  std::vector<Message> retval;
171 
172  for (int i=0; i<lines.size(); ++i)
173  {
174  QString line = lines[i];
175 
176  QDateTime timestamp = this->readTimestampFromSessionStartLine(lines[i]);
177  if (timestamp.isValid())
178  {
179  mInitTimestamp = timestamp;
180  Message msg(QString("Session initialized: %1").arg(mChannel), mlSUCCESS);
181  msg.mChannel = mChannel;
182  msg.mTimeStamp = timestamp;
183  msg.mThread = "";
184  retval.push_back(msg);
185  continue;
186  }
187 
188  if (line.count(rx_ts))
189  {
190  Message msg = this->readMessageFirstLine(lines[i]);
191  msg.mChannel = mChannel;
192  retval.push_back(msg);
193  }
194  else
195  {
196  if (!retval.empty())
197  retval.back().mText += "\n"+line;
198  }
199  }
200 
201  return retval;
202 }
203 
204 Message LogFile::readMessageFirstLine(QString line)
205 {
206  MESSAGE_LEVEL level = this->readMessageLevel(line);
207  if (level==mlCOUNT)
208  return Message(line, mlINFO);
209  QString levelSymbol = QString("[%1]").arg(enum2string<MESSAGE_LEVEL>(level));
210  QStringList parts = line.split(levelSymbol);
211 
212  if (parts.size()<2)
213  return Message(line, level);
214 
215  QString description = parts[1];
216  QStringList fields = parts[0].split("\t");
217 
218  Message retval(description, level);
219 
220  this->parseTimestamp(this->getIndex(fields, 0), &retval);
221  this->parseThread(this->getIndex(fields, 1), &retval);
222  this->parseSourceFileLine(this->getIndex(fields, 2), &retval);
223  this->parseSourceFunction(this->getIndex(fields, 3), &retval);
224 
225  return retval;
226 }
227 
228 QString LogFile::getIndex(const QStringList& list, int index)
229 {
230  if (0>index || index >= list.size())
231  return "";
232  QString field = list[index];
233  if (field.startsWith("["))
234  field.remove(0, 1);
235  if (field.endsWith("]"))
236  field.chop(1);
237  return field;
238 }
239 
240 void LogFile::parseTimestamp(QString text, Message* retval)
241 {
242  if (text.isEmpty())
243  return;
244 
245  retval->mTimeStamp = mInitTimestamp; // reuse date from init, as this is not part of each line
246  QTime time = QTime::fromString(text, this->timestampFormat());
247  retval->mTimeStamp.setTime(time);
248 }
249 
250 void LogFile::parseThread(QString text, Message* retval)
251 {
252  retval->mThread = text;
253 }
254 
255 void LogFile::parseSourceFileLine(QString text, Message* retval)
256 {
257  if (!text.count(":"))
258  return;
259 
260  QStringList parts = text.split(":");
261  bool ok = 0;
262  retval->mSourceLine = parts.back().toInt(&ok);
263  if (!ok)
264  return;
265  parts.removeLast();
266  retval->mSourceFile = parts.join(":");
267 }
268 
269 void LogFile::parseSourceFunction(QString text, Message* retval)
270 {
271  if (!text.count("("))
272  return;
273  retval->mSourceFunction = text;
274 }
275 
276 MESSAGE_LEVEL LogFile::readMessageLevel(QString line)
277 {
278  QStringList levels;
279  for (int i=0; i<mlCOUNT; ++i)
280  levels << enum2string<MESSAGE_LEVEL>((MESSAGE_LEVEL)(i));
281  QRegExp rx_level(QString("\\[(%1)\\]").arg(levels.join("|")));
282 
283  int pos = rx_level.indexIn(line);
284  QStringList hits = rx_level.capturedTexts();
285 
286  if (hits.size()<2)
287  return mlCOUNT;
288 
289  return string2enum<MESSAGE_LEVEL>(hits[1]);
290 }
291 
292 QDateTime LogFile::readTimestampFromSessionStartLine(QString text)
293 {
294  QString sessionStartSymbol("------->");
295  if (!text.startsWith(sessionStartSymbol))
296  return QDateTime();
297 
298  QRegExp rx_ts_start("\\[([^\\]]*)");
299  if (text.indexOf(rx_ts_start) < 0)
300  return QDateTime();
301 
302  QString rawTime = rx_ts_start.cap(1);
303  QString format = timestampMilliSecondsFormatNice();
304  QDateTime ts = QDateTime::fromString(rawTime, format);
305 
306  return ts;
307 }
308 
309 QString LogFile::readFileTail()
310 {
311  QFile file(this->getFilename());
312  file.open(QIODevice::ReadOnly);
313 
314  file.seek(mFilePosition);
315  QString text = file.readAll();
316  mFilePosition = file.pos();
317 
318  return text;
319 }
320 
321 
322 } //End namespace cx
QString qstring_cast(const T &val)
mlSUCCESS
Definition: cxDefinitions.h:82
std::vector< Message > readMessages()
Definition: cxLogFile.cpp:158
QString mSourceFile
Definition: cxLogMessage.h:98
QString getText() const
The raw message.
void write(Message message)
Definition: cxLogFile.cpp:82
QString mThread
Definition: cxLogMessage.h:96
void writeHeader()
Definition: cxLogFile.cpp:73
QDateTime getTimeStamp() const
The time at which the message was created.
QString mChannel
Definition: cxLogMessage.h:95
static LogFile fromFilename(QString filename)
Definition: cxLogFile.cpp:65
static LogFile fromChannel(QString path, QString channel)
Definition: cxLogFile.cpp:52
cx::Message Message
Definition: cxLogMessage.h:107
QDateTime mTimeStamp
Definition: cxLogMessage.h:92
QString getFilename() const
Definition: cxLogFile.cpp:60
mlINFO
Definition: cxDefinitions.h:82
QString mSourceFunction
Definition: cxLogMessage.h:99
bool isWritable() const
Definition: cxLogFile.cpp:88
QString timestampMilliSecondsFormatNice()
Definition: cxTime.cpp:51
MESSAGE_LEVEL getMessageLevel() const
The category of the message.