comparison Framework/Orthanc/Core/Logging.cpp @ 1:2dbe613f6c93

add orthanc core
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 14 Oct 2016 15:39:01 +0200
parents
children 9220cf4a63d5
comparison
equal deleted inserted replaced
0:351ab0da0150 1:2dbe613f6c93
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 *
6 * This program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation, either version 3 of the
9 * License, or (at your option) any later version.
10 *
11 * In addition, as a special exception, the copyright holders of this
12 * program give permission to link the code of its release with the
13 * OpenSSL project's "OpenSSL" library (or with modified versions of it
14 * that use the same license as the "OpenSSL" library), and distribute
15 * the linked executables. You must obey the GNU General Public License
16 * in all respects for all of the code used other than "OpenSSL". If you
17 * modify file(s) with this exception, you may extend this exception to
18 * your version of the file(s), but you are not obligated to do so. If
19 * you do not wish to do so, delete this exception statement from your
20 * version. If you delete this exception statement from all source files
21 * in the program, then also delete it here.
22 *
23 * This program is distributed in the hope that it will be useful, but
24 * WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26 * General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30 **/
31
32
33 #include "PrecompiledHeaders.h"
34 #include "Logging.h"
35
36 #if ORTHANC_ENABLE_LOGGING != 1
37
38 namespace Orthanc
39 {
40 namespace Logging
41 {
42 void Initialize()
43 {
44 }
45
46 void Finalize()
47 {
48 }
49
50 void Reset()
51 {
52 }
53
54 void Flush()
55 {
56 }
57
58 void EnableInfoLevel(bool enabled)
59 {
60 }
61
62 void EnableTraceLevel(bool enabled)
63 {
64 }
65
66 void SetTargetFile(const std::string& path)
67 {
68 }
69
70 void SetTargetFolder(const std::string& path)
71 {
72 }
73 }
74 }
75
76 #else
77
78 /*********************************************************
79 * Internal logger of Orthanc, that mimics some
80 * behavior from Google Log.
81 *********************************************************/
82
83 #include "OrthancException.h"
84 #include "Enumerations.h"
85 #include "Toolbox.h"
86
87 #include <fstream>
88 #include <boost/filesystem.hpp>
89 #include <boost/thread.hpp>
90
91 #if BOOST_HAS_DATE_TIME == 1
92 # include <boost/date_time/posix_time/posix_time.hpp>
93 #else
94 # error Boost::date_time is required
95 #endif
96
97
98 namespace
99 {
100 struct LoggingContext
101 {
102 bool infoEnabled_;
103 bool traceEnabled_;
104 std::string targetFile_;
105 std::string targetFolder_;
106
107 std::ostream* error_;
108 std::ostream* warning_;
109 std::ostream* info_;
110
111 std::auto_ptr<std::ofstream> file_;
112
113 LoggingContext() :
114 infoEnabled_(false),
115 traceEnabled_(false),
116 error_(&std::cerr),
117 warning_(&std::cerr),
118 info_(&std::cerr)
119 {
120 }
121 };
122 }
123
124
125
126 static std::auto_ptr<LoggingContext> loggingContext_;
127 static boost::mutex loggingMutex_;
128
129
130
131 namespace Orthanc
132 {
133 namespace Logging
134 {
135 static void GetLogPath(boost::filesystem::path& log,
136 boost::filesystem::path& link,
137 const std::string& suffix,
138 const std::string& directory)
139 {
140 /**
141 From Google Log documentation:
142
143 Unless otherwise specified, logs will be written to the filename
144 "<program name>.<hostname>.<user name>.log<suffix>.",
145 followed by the date, time, and pid (you can't prevent the date,
146 time, and pid from being in the filename).
147
148 In this implementation : "hostname" and "username" are not used
149 **/
150
151 boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
152 boost::filesystem::path root(directory);
153 boost::filesystem::path exe(Toolbox::GetPathToExecutable());
154
155 if (!boost::filesystem::exists(root) ||
156 !boost::filesystem::is_directory(root))
157 {
158 throw OrthancException(ErrorCode_CannotWriteFile);
159 }
160
161 char date[64];
162 sprintf(date, "%04d%02d%02d-%02d%02d%02d.%d",
163 static_cast<int>(now.date().year()),
164 now.date().month().as_number(),
165 now.date().day().as_number(),
166 now.time_of_day().hours(),
167 now.time_of_day().minutes(),
168 now.time_of_day().seconds(),
169 Toolbox::GetProcessId());
170
171 std::string programName = exe.filename().replace_extension("").string();
172
173 log = (root / (programName + ".log" + suffix + "." + std::string(date)));
174 link = (root / (programName + ".log" + suffix));
175 }
176
177
178 static void PrepareLogFolder(std::auto_ptr<std::ofstream>& file,
179 const std::string& suffix,
180 const std::string& directory)
181 {
182 boost::filesystem::path log, link;
183 GetLogPath(log, link, suffix, directory);
184
185 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)))
186 boost::filesystem::remove(link);
187 boost::filesystem::create_symlink(log.filename(), link);
188 #endif
189
190 file.reset(new std::ofstream(log.string().c_str()));
191 }
192
193
194 void Initialize()
195 {
196 boost::mutex::scoped_lock lock(loggingMutex_);
197 loggingContext_.reset(new LoggingContext);
198 }
199
200 void Finalize()
201 {
202 boost::mutex::scoped_lock lock(loggingMutex_);
203 loggingContext_.reset(NULL);
204 }
205
206 void Reset()
207 {
208 // Recover the old logging context
209 std::auto_ptr<LoggingContext> old;
210
211 {
212 boost::mutex::scoped_lock lock(loggingMutex_);
213 if (loggingContext_.get() == NULL)
214 {
215 return;
216 }
217 else
218 {
219 old = loggingContext_;
220
221 // Create a new logging context,
222 loggingContext_.reset(new LoggingContext);
223 }
224 }
225
226 EnableInfoLevel(old->infoEnabled_);
227 EnableTraceLevel(old->traceEnabled_);
228
229 if (!old->targetFolder_.empty())
230 {
231 SetTargetFolder(old->targetFolder_);
232 }
233 else if (!old->targetFile_.empty())
234 {
235 SetTargetFile(old->targetFile_);
236 }
237 }
238
239 void EnableInfoLevel(bool enabled)
240 {
241 boost::mutex::scoped_lock lock(loggingMutex_);
242 assert(loggingContext_.get() != NULL);
243
244 loggingContext_->infoEnabled_ = enabled;
245
246 if (!enabled)
247 {
248 // Also disable the "TRACE" level when info-level debugging is disabled
249 loggingContext_->traceEnabled_ = false;
250 }
251 }
252
253 void EnableTraceLevel(bool enabled)
254 {
255 boost::mutex::scoped_lock lock(loggingMutex_);
256 assert(loggingContext_.get() != NULL);
257
258 loggingContext_->traceEnabled_ = enabled;
259
260 if (enabled)
261 {
262 // Also enable the "INFO" level when trace-level debugging is enabled
263 loggingContext_->infoEnabled_ = true;
264 }
265 }
266
267
268 static void CheckFile(std::auto_ptr<std::ofstream>& f)
269 {
270 if (loggingContext_->file_.get() == NULL ||
271 !loggingContext_->file_->is_open())
272 {
273 throw OrthancException(ErrorCode_CannotWriteFile);
274 }
275 }
276
277 void SetTargetFolder(const std::string& path)
278 {
279 boost::mutex::scoped_lock lock(loggingMutex_);
280 assert(loggingContext_.get() != NULL);
281
282 PrepareLogFolder(loggingContext_->file_, "" /* no suffix */, path);
283 CheckFile(loggingContext_->file_);
284
285 loggingContext_->targetFile_.clear();
286 loggingContext_->targetFolder_ = path;
287 loggingContext_->warning_ = loggingContext_->file_.get();
288 loggingContext_->error_ = loggingContext_->file_.get();
289 loggingContext_->info_ = loggingContext_->file_.get();
290 }
291
292
293 void SetTargetFile(const std::string& path)
294 {
295 boost::mutex::scoped_lock lock(loggingMutex_);
296 assert(loggingContext_.get() != NULL);
297
298 loggingContext_->file_.reset(new std::ofstream(path.c_str(), std::fstream::app));
299 CheckFile(loggingContext_->file_);
300
301 loggingContext_->targetFile_ = path;
302 loggingContext_->targetFolder_.clear();
303 loggingContext_->warning_ = loggingContext_->file_.get();
304 loggingContext_->error_ = loggingContext_->file_.get();
305 loggingContext_->info_ = loggingContext_->file_.get();
306 }
307
308
309 InternalLogger::InternalLogger(const char* level,
310 const char* file,
311 int line) :
312 lock_(loggingMutex_),
313 stream_(&null_) // By default, logging to "/dev/null" is simulated
314 {
315 if (loggingContext_.get() == NULL)
316 {
317 fprintf(stderr, "ERROR: Trying to log a message after the finalization of the logging engine\n");
318 return;
319 }
320
321 LogLevel l = StringToLogLevel(level);
322
323 if ((l == LogLevel_Info && !loggingContext_->infoEnabled_) ||
324 (l == LogLevel_Trace && !loggingContext_->traceEnabled_))
325 {
326 // This logging level is disabled, directly exit and unlock
327 // the mutex to speed-up things. The stream is set to "/dev/null"
328 lock_.unlock();
329 return;
330 }
331
332 // Compute the header of the line, temporary release the lock as
333 // this is a time-consuming operation
334 lock_.unlock();
335 std::string header;
336
337 {
338 boost::filesystem::path path(file);
339 boost::posix_time::ptime now = boost::posix_time::microsec_clock::local_time();
340 boost::posix_time::time_duration duration = now.time_of_day();
341
342 /**
343 From Google Log documentation:
344
345 "Log lines have this form:
346
347 Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg...
348
349 where the fields are defined as follows:
350
351 L A single character, representing the log level (eg 'I' for INFO)
352 mm The month (zero padded; ie May is '05')
353 dd The day (zero padded)
354 hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds
355 threadid The space-padded thread ID as returned by GetTID() (this matches the PID on Linux)
356 file The file name
357 line The line number
358 msg The user-supplied message"
359
360 In this implementation, "threadid" is not printed.
361 **/
362
363 char date[32];
364 sprintf(date, "%c%02d%02d %02d:%02d:%02d.%06d ",
365 level[0],
366 now.date().month().as_number(),
367 now.date().day().as_number(),
368 duration.hours(),
369 duration.minutes(),
370 duration.seconds(),
371 static_cast<int>(duration.fractional_seconds()));
372
373 header = std::string(date) + path.filename().string() + ":" + boost::lexical_cast<std::string>(line) + "] ";
374 }
375
376
377 // The header is computed, we now re-lock the mutex to access
378 // the stream objects. Pay attention that "loggingContext_",
379 // "infoEnabled_" or "traceEnabled_" might have changed while
380 // the mutex was unlocked.
381 lock_.lock();
382
383 if (loggingContext_.get() == NULL)
384 {
385 fprintf(stderr, "ERROR: Trying to log a message after the finalization of the logging engine\n");
386 return;
387 }
388
389 switch (l)
390 {
391 case LogLevel_Error:
392 stream_ = loggingContext_->error_;
393 break;
394
395 case LogLevel_Warning:
396 stream_ = loggingContext_->warning_;
397 break;
398
399 case LogLevel_Info:
400 if (loggingContext_->infoEnabled_)
401 {
402 stream_ = loggingContext_->info_;
403 }
404
405 break;
406
407 case LogLevel_Trace:
408 if (loggingContext_->traceEnabled_)
409 {
410 stream_ = loggingContext_->info_;
411 }
412
413 break;
414
415 default:
416 throw OrthancException(ErrorCode_InternalError);
417 }
418
419 if (stream_ == &null_)
420 {
421 // The logging is disabled for this level. The stream is the
422 // "null_" member of this object, so we can release the global
423 // mutex.
424 lock_.unlock();
425 }
426
427 (*stream_) << header;
428 }
429
430
431 InternalLogger::~InternalLogger()
432 {
433 if (stream_ != &null_)
434 {
435 #if defined(_WIN32)
436 *stream_ << "\r\n";
437 #else
438 *stream_ << "\n";
439 #endif
440
441 stream_->flush();
442 }
443 }
444
445
446 void Flush()
447 {
448 boost::mutex::scoped_lock lock(loggingMutex_);
449
450 if (loggingContext_.get() != NULL &&
451 loggingContext_->file_.get() != NULL)
452 {
453 loggingContext_->file_->flush();
454 }
455 }
456 }
457 }
458
459 #endif // ORTHANC_ENABLE_LOGGING