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  * Copyright (C) 2017-2021 Osimis S.A., Belgium
6  *
7  * This program is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * as published by the Free Software Foundation, either version 3 of
10  * the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this program. If not, see
19  * <http://www.gnu.org/licenses/>.
20  **/
21 
22 
23 #pragma once
24 
25 // To have ORTHANC_ENABLE_LOGGING defined if using the shared library
26 #include "OrthancFramework.h"
27 #include "Compatibility.h"
28 
29 #include <iostream>
30 
31 #if !defined(ORTHANC_ENABLE_LOGGING)
32 #  error The macro ORTHANC_ENABLE_LOGGING must be defined
33 #endif
34 
35 #if !defined(ORTHANC_ENABLE_LOGGING_STDIO)
36 #  if ORTHANC_ENABLE_LOGGING == 1
37 #    error The macro ORTHANC_ENABLE_LOGGING_STDIO must be defined
38 #  else
39 #    define ORTHANC_ENABLE_LOGGING_STDIO 0
40 #  endif
41 #endif
42 
43 
44 namespace Orthanc
45 {
46   namespace Logging
47   {
48     enum LogLevel
49     {
50       LogLevel_ERROR,
51       LogLevel_WARNING,
52       LogLevel_INFO,
53       LogLevel_TRACE
54     };
55 
56     /**
57      * NB: The log level for each category is encoded as a bit
58      * mask. As a consequence, there can be up to 31 log categories
59      * (not 32, as the value GENERIC is reserved for the log commands
60      * that don't fall in a specific category).
61      **/
62     enum LogCategory
63     {
64       LogCategory_GENERIC = (1 << 0),
65       LogCategory_PLUGINS = (1 << 1),
66       LogCategory_HTTP    = (1 << 2),
67       LogCategory_SQLITE  = (1 << 3),
68       LogCategory_DICOM   = (1 << 4),
69       LogCategory_JOBS    = (1 << 5),
70       LogCategory_LUA     = (1 << 6),
71     };
72 
73     ORTHANC_PUBLIC const char* EnumerationToString(LogLevel level);
74 
75     ORTHANC_PUBLIC LogLevel StringToLogLevel(const char* level);
76 
77     // "pluginContext" must be of type "OrthancPluginContext"
78     ORTHANC_PUBLIC void InitializePluginContext(void* pluginContext);
79 
80     ORTHANC_PUBLIC void Initialize();
81 
82     ORTHANC_PUBLIC void Finalize();
83 
84     ORTHANC_PUBLIC void Reset();
85 
86     ORTHANC_PUBLIC void Flush();
87 
88     ORTHANC_PUBLIC void EnableInfoLevel(bool enabled);
89 
90     ORTHANC_PUBLIC void EnableTraceLevel(bool enabled);
91 
92     ORTHANC_PUBLIC bool IsTraceLevelEnabled();
93 
94     ORTHANC_PUBLIC bool IsInfoLevelEnabled();
95 
96     ORTHANC_PUBLIC void SetCategoryEnabled(LogLevel level,
97                                            LogCategory category,
98                                            bool enabled);
99 
100     ORTHANC_PUBLIC bool IsCategoryEnabled(LogLevel level,
101                                           LogCategory category);
102 
103     ORTHANC_PUBLIC bool LookupCategory(LogCategory& target,
104                                        const std::string& category);
105 
106     ORTHANC_PUBLIC unsigned int GetCategoriesCount();
107 
108     ORTHANC_PUBLIC const char* GetCategoryName(unsigned int i);
109 
110     ORTHANC_PUBLIC const char* GetCategoryName(LogCategory category);
111 
112     ORTHANC_PUBLIC void SetTargetFile(const std::string& path);
113 
114     ORTHANC_PUBLIC void SetTargetFolder(const std::string& path);
115 
116     struct ORTHANC_LOCAL NullStream : public std::ostream
117     {
NullStreamNullStream118       NullStream() :
119         std::ios(0),
120         std::ostream(0)
121       {
122       }
123 
124       template <typename T>
125       std::ostream& operator<< (const T& message)
126       {
127         return *this;
128       }
129     };
130   }
131 }
132 
133 
134 
135 /**
136  * NB:
137  * - The "VLOG(unused)" macro is for backward compatibility with
138  *   Orthanc <= 1.8.0.
139  * - The "CLOG()" macro stands for "category log" (new in Orthanc 1.8.1)
140  **/
141 
142 #if defined(LOG)
143 #  error The macro LOG cannot be defined beforehand
144 #endif
145 
146 #if defined(VLOG)
147 #  error The macro VLOG cannot be defined beforehand
148 #endif
149 
150 #if defined(CLOG)
151 #  error The macro CLOG cannot be defined beforehand
152 #endif
153 
154 #if ORTHANC_ENABLE_LOGGING != 1
155 #  define LOG(level)            ::Orthanc::Logging::NullStream()
156 #  define VLOG(unused)          ::Orthanc::Logging::NullStream()
157 #  define CLOG(level, category) ::Orthanc::Logging::NullStream()
158 #else /* ORTHANC_ENABLE_LOGGING == 1 */
159 #  define LOG(level)     ::Orthanc::Logging::InternalLogger     \
160   (::Orthanc::Logging::LogLevel_ ## level,                      \
161    ::Orthanc::Logging::LogCategory_GENERIC, __FILE__, __LINE__)
162 #  define VLOG(unused)   ::Orthanc::Logging::InternalLogger     \
163   (::Orthanc::Logging::LogLevel_TRACE,                          \
164    ::Orthanc::Logging::LogCategory_GENERIC, __FILE__, __LINE__)
165 #  define CLOG(level, category) ::Orthanc::Logging::InternalLogger      \
166   (::Orthanc::Logging::LogLevel_ ## level,                              \
167    ::Orthanc::Logging::LogCategory_ ## category, __FILE__, __LINE__)
168 #endif
169 
170 
171 
172 #if (ORTHANC_ENABLE_LOGGING == 1 &&             \
173      ORTHANC_ENABLE_LOGGING_STDIO == 1)
174 // This is notably for WebAssembly
175 
176 #include <boost/lexical_cast.hpp>
177 #include <boost/noncopyable.hpp>
178 #include <sstream>
179 
180 namespace Orthanc
181 {
182   namespace Logging
183   {
184     class ORTHANC_PUBLIC InternalLogger : public boost::noncopyable
185     {
186     private:
187       LogLevel           level_;
188       LogCategory        category_;
189       std::stringstream  messageStream_;
190 
191     public:
InternalLogger(LogLevel level,LogCategory category,const char * file,int line)192       InternalLogger(LogLevel level,
193                      LogCategory category,
194                      const char* file  /* ignored */,
195                      int line  /* ignored */) :
196         level_(level),
197         category_(category)
198       {
199       }
200 
201       // For backward binary compatibility with Orthanc Framework <= 1.8.0
InternalLogger(LogLevel level,const char * file,int line)202       InternalLogger(LogLevel level,
203                      const char* file  /* ignored */,
204                      int line  /* ignored */) :
205         level_(level),
206         category_(LogCategory_GENERIC)
207       {
208       }
209 
210       ~InternalLogger();
211 
212       template <typename T>
213         std::ostream& operator<< (const T& message)
214       {
215         return messageStream_ << boost::lexical_cast<std::string>(message);
216       }
217     };
218   }
219 }
220 
221 #endif
222 
223 
224 
225 #if (ORTHANC_ENABLE_LOGGING == 1 &&             \
226      ORTHANC_ENABLE_LOGGING_STDIO == 0)
227 
228 #include <boost/lexical_cast.hpp>
229 #include <boost/noncopyable.hpp>
230 #include <boost/thread/mutex.hpp>
231 #include <sstream>
232 
233 namespace Orthanc
234 {
235   namespace Logging
236   {
237     class ORTHANC_PUBLIC InternalLogger : public boost::noncopyable
238     {
239     private:
240       boost::mutex::scoped_lock           lock_;
241       LogLevel                            level_;
242       std::unique_ptr<std::stringstream>  pluginStream_;
243       std::ostream*                       stream_;
244 
245       void Setup(LogCategory category,
246                  const char* file,
247                  int line);
248 
249     public:
250       InternalLogger(LogLevel level,
251                      LogCategory category,
252                      const char* file,
253                      int line);
254 
255       // For backward binary compatibility with Orthanc Framework <= 1.8.0
256       InternalLogger(LogLevel level,
257                      const char* file,
258                      int line);
259 
260       ~InternalLogger();
261 
262       template <typename T>
263         std::ostream& operator<< (const T& message)
264       {
265         return (*stream_) << boost::lexical_cast<std::string>(message);
266       }
267     };
268 
269     /**
270      * Set custom logging streams for the error, warning and info
271      * logs. This function may not be called if a log file or folder
272      * has been set beforehand. All three references must be valid.
273      *
274      * Please ensure the supplied streams remain alive and valid as
275      * long as logging calls are performed. In order to prevent
276      * dangling pointer usage, it is mandatory to call
277      * Orthanc::Logging::Reset() before the stream objects are
278      * destroyed and the references become invalid.
279      *
280      * This function must only be used by unit tests. It is ignored if
281      * InitializePluginContext() was called.
282      **/
283     ORTHANC_PUBLIC void SetErrorWarnInfoLoggingStreams(std::ostream& errorStream,
284                                                        std::ostream& warningStream,
285                                                        std::ostream& infoStream);
286   }
287 }
288 
289 #endif
290