Logger.cxx 7.69 KB
Newer Older
1 2 3 4 5 6 7 8 9
//=========================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
//
//  This software is distributed WITHOUT ANY WARRANTY; without even
//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//  PURPOSE.  See the above copyright notice for more information.
//=========================================================================
10

11 12
#include "smtk/io/Logger.h"

13 14
#include <fstream>

15 16 17 18
namespace smtk
{
namespace io
{
19 20 21 22 23 24
Logger Logger::m_instance;

Logger& Logger::instance()
{
  return Logger::m_instance;
}
25

26 27
Logger::~Logger()
{
28
  this->setFlushToStream(NULL, false, false);
29
  if (m_callback)
30
  {
31
    m_callback();
32
  }
33 34
}

35 36 37 38 39 40 41 42
Logger& Logger::operator=(const Logger& logger)
{
  std::lock_guard<std::mutex> lock(m_mutex);
  m_records = logger.m_records;
  m_hasErrors = logger.m_hasErrors;
  return *this;
}

43 44
void Logger::addRecord(
  Severity s, const std::string& m, const std::string& fname, unsigned int line)
45
{
46
  std::lock_guard<std::mutex> lock(m_mutex);
47
  if ((s == Logger::ERROR) || (s == Logger::FATAL))
48
  {
49
    m_hasErrors = true;
50
  }
51
  m_records.push_back(Record(s, m, fname, line));
52 53
  std::size_t nr = this->numberOfRecords();
  this->flushRecordsToStream(nr - 1, nr);
54
}
55

56
void Logger::append(const Logger& l)
57
{
58 59 60 61 62 63
  // do not append a logger to itself
  if (&l == this)
  {
    return;
  }

64
  std::lock_guard<std::mutex> lock(m_mutex);
65
  m_records.insert(m_records.end(), l.m_records.begin(), l.m_records.end());
66
  if (l.m_hasErrors)
67
  {
68
    m_hasErrors = true;
69
  }
70 71
  std::size_t nr = this->numberOfRecords();
  this->flushRecordsToStream(nr - l.numberOfRecords(), nr);
72
}
73

74 75
void Logger::reset()
{
76
  std::lock_guard<std::mutex> lock(m_mutex);
77 78
  m_hasErrors = false;
  m_records.clear();
79
}
80

81 82 83
std::string Logger::severityAsString(Severity s)
{
  switch (s)
84
  {
85 86 87 88 89 90 91 92 93 94 95 96
    case DEBUG:
      return "DEBUG";
    case INFO:
      return "INFO";
    case WARNING:
      return "WARNING";
    case ERROR:
      return "ERROR";
    case FATAL:
      return "FATAL";
    default:
      return "UNKNOWN";
97
  }
98 99
  return "UNKNOWN";
}
100 101 102 103

/**\brief Convert the given log entry to a string.
  *
  */
104
std::string Logger::toString(const Logger::Record& record, bool includeSourceLoc)
105 106
{
  std::stringstream ss;
107 108
  ss << severityAsString(record.severity) << ": ";
  if (includeSourceLoc && record.fileName != "")
109
  {
110
    ss << "In " << record.fileName << ", line " << record.lineNumber << ": ";
111
  }
112
  ss << record.message << std::endl;
113 114 115
  return ss.str();
}

116 117 118 119 120
/**\brief Convert the given log entry to a string.
  *
  */
std::string Logger::toString(std::size_t i, bool includeSourceLoc) const
{
121
  std::lock_guard<std::mutex> lock(m_mutex);
122 123 124
  return this->toString(m_records[i], includeSourceLoc);
}

125 126 127
/**\brief Convert the given log entry range to a string.
  *
  */
128
std::string Logger::toString(std::size_t i, std::size_t j, bool includeSourceLoc) const
129 130 131 132 133 134
{
  std::lock_guard<std::mutex> lock(m_mutex);
  return this->toStringInternal(i, j, includeSourceLoc);
}

std::string Logger::toStringInternal(std::size_t i, std::size_t j, bool includeSourceLoc) const
135 136
{
  std::stringstream ss;
137
  for (; i < j; i++)
138
  {
139 140
    ss << severityAsString(m_records[i].severity) << ": ";
    if (includeSourceLoc && m_records[i].fileName != "")
141
    {
142
      ss << "In " << m_records[i].fileName << ", line " << m_records[i].lineNumber << ": ";
143
    }
144
    ss << m_records[i].message << std::endl;
145
  }
146 147
  return ss.str();
}
148

149 150
std::string Logger::toHTML(std::size_t i, std::size_t j, bool includeSourceLoc) const
{
151
  std::lock_guard<std::mutex> lock(m_mutex);
152 153 154
  std::stringstream ss;
  ss << "<table>";
  for (; i < j; i++)
155
  {
156
    ss << "<tr class=\"" << severityAsString(m_records[i].severity) << "\">";
157
    if (includeSourceLoc)
158
    {
159
      ss << "<td>";
160
      if (m_records[i].fileName != "")
161
      {
162
        ss << m_records[i].fileName << ", line " << m_records[i].lineNumber;
163
      }
164
      else
165
      {
166 167
        ss << "&nbsp;";
      }
168
      ss << "</td>";
169
    }
170
    ss << "<td>" << m_records[i].message << "</td></tr>\n";
171
  }
172 173 174 175 176 177
  ss << "</table>";
  return ss.str();
}

std::string Logger::convertToString(bool includeSourceLoc) const
{
178
  return this->toString(0, m_records.size(), includeSourceLoc);
179 180 181
}

std::string Logger::convertToHTML(bool includeSourceLog) const
182
{
183
  return this->toHTML(0, m_records.size(), includeSourceLog);
184 185
}

186
/**\brief Request all records be flushed to \a output as they are logged.
187 188 189 190 191
  *
  * If \a ownFile is true, then the Logger takes ownership of \a output
  * and will delete it when the Logger is destructed.
  * If \a output is NULL, then this stops future log records from
  * being appended to any file.
192 193 194 195 196 197 198
  * If \a includePast is true, then all pre-existing records are
  * written to the stream before this method returns (and future
  * records are written as they are added/appended).
  *
  * Note that only a single stream will be written at a time;
  * calling this or other "setFlushTo" methods multiple times
  * will only change where new records are written.
199
  */
200
void Logger::setFlushToStream(std::ostream* output, bool ownFile, bool includePast)
201
{
202
  std::lock_guard<std::mutex> lock(m_mutex);
203 204 205 206
  if (m_ownStream)
    delete m_stream;
  m_stream = output;
  m_ownStream = output ? ownFile : false;
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
  if (includePast)
    this->flushRecordsToStream(0, this->numberOfRecords());
}

/**\brief Request all records be flushed to a file with the given \a filename.
  *
  * If \a includePast is true, then all pre-existing records will
  * be immediately written to the file (and future records as they
  * are added/appended).
  *
  * Returns true if the file was successfully created.
  *
  * Note that only a single stream will be written at a time;
  * calling this or other "setFlushTo" methods multiple times
  * will only change where new records are written.
  */
223
bool Logger::setFlushToFile(std::string filename, bool includePast)
224
{
225
  std::ofstream* file = new std::ofstream(filename.c_str(), std::ios::app);
226
  if (file->good())
227
  {
228
    this->setFlushToStream(file, true, includePast);
229
  }
230
  else
231
  {
232 233
    delete file;
    return false;
234
  }
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
  return true;
}

/**\brief Request all records be flushed to the process's standard output.
  *
  * If \a includePast is true, then all pre-existing records will
  * be immediately written to the file (and future records as they
  * are added/appended).
  *
  * This method is a convenience for Python users.
  *
  * Note that only a single stream will be written at a time;
  * calling this or other "setFlushTo" methods multiple times
  * will only change where new records are written.
  */
void Logger::setFlushToStdout(bool includePast)
{
  this->setFlushToStream(&std::cout, false, includePast);
}

/**\brief Request all records be flushed to the process's standard error output.
  *
  * If \a includePast is true, then all pre-existing records will
  * be immediately written to the file (and future records as they
  * are added/appended).
  *
  * This method is a convenience for Python users.
  *
  * Note that only a single stream will be written at a time;
  * calling this or other "setFlushTo" methods multiple times
  * will only change where new records are written.
  */
void Logger::setFlushToStderr(bool includePast)
{
  this->setFlushToStream(&std::cerr, false, includePast);
}

272 273 274
/// Set a function to be called upon the destruction of the logger.
void Logger::setCallback(std::function<void()> fn)
{
275
  m_callback = fn;
276 277
}

278 279 280
/// This is a helper routine to write records to the stream (if one has been set).
void Logger::flushRecordsToStream(std::size_t beginRec, std::size_t endRec)
{
281
  if (m_stream && beginRec < endRec && beginRec < numberOfRecords() && endRec <= numberOfRecords())
282
  {
283
    (*m_stream) << this->toStringInternal(beginRec, endRec);
284
    m_stream->flush();
285
  }
286 287
}

288
} // namespace io
289
} // namespace smtk