1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | /* |
3 | * This file is part of the LibreOffice project. |
4 | * |
5 | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | */ |
9 | |
10 | #ifndef INCLUDED_SAL_LOG_HXX |
11 | #define INCLUDED_SAL_LOG_HXX |
12 | |
13 | #include <sal/config.h> |
14 | |
15 | #include <cstdlib> |
16 | #include <sstream> |
17 | #include <string> |
18 | |
19 | #include <sal/detail/log.h> |
20 | #include <sal/saldllapi.h> |
21 | #include <sal/types.h> |
22 | |
23 | // Avoid the use of other sal code in this header as much as possible, so that |
24 | // this code can be called from other sal code without causing endless |
25 | // recursion. |
26 | |
27 | /// @cond INTERNAL |
28 | |
29 | extern "C" SAL_DLLPUBLIC void SAL_CALL sal_detail_log( |
30 | enum sal_detail_LogLevel level, char const * area, char const * where, |
31 | char const * message); |
32 | |
33 | namespace sal { namespace detail { |
34 | |
35 | inline void SAL_CALL log( |
36 | sal_detail_LogLevel level, char const * area, char const * where, |
37 | std::ostringstream const & stream) |
38 | { |
39 | // An alternative would be to have sal_detail_log take a std::ostringstream |
40 | // pointer (via a C void pointer); the advantage would be smaller client |
41 | // code (the ".str().c_str()" part would move into the implementation of |
42 | // sal_detail_log) and potential for proper support of embedded null |
43 | // characters within the message, but the disadvantage would be dependence |
44 | // on the C++ ABI; as a compromise, the ".str().c_str()" part has been moved |
45 | // to this inline function so that it is potentially only emitted once per |
46 | // dynamic library: |
47 | sal_detail_log(level, area, where, stream.str().c_str()); |
48 | } |
49 | |
50 | // Special handling of the common case where the message consists of just a |
51 | // string literal, to produce smaller call-site code: |
52 | |
53 | struct StreamStart {}; |
54 | |
55 | struct StreamString { |
56 | StreamString(char const * s): string(s) {} |
57 | |
58 | char const * string; |
59 | |
60 | typedef char Result; |
61 | }; |
62 | |
63 | struct StreamIgnore { |
64 | typedef struct { char a[2]; } Result; |
65 | }; |
66 | |
67 | inline StreamString operator <<( |
68 | SAL_UNUSED_PARAMETER StreamStart const &, char const * s) |
69 | { |
70 | return StreamString(s); |
71 | } |
72 | |
73 | template< typename T > inline StreamIgnore operator <<( |
74 | SAL_UNUSED_PARAMETER StreamStart const &, SAL_UNUSED_PARAMETER T const &) |
75 | { |
76 | std::abort(); |
77 | #if defined _MSC_VER && _MSC_VER < 1700 |
78 | return StreamIgnore(); |
79 | #endif |
80 | } |
81 | |
82 | template< typename T > inline StreamIgnore operator <<( |
83 | SAL_UNUSED_PARAMETER StreamString const &, SAL_UNUSED_PARAMETER T const &) |
84 | { |
85 | std::abort(); |
86 | #if defined _MSC_VER && _MSC_VER < 1700 |
87 | return StreamIgnore(); |
88 | #endif |
89 | } |
90 | |
91 | template< typename T > inline StreamIgnore operator <<( |
92 | SAL_UNUSED_PARAMETER StreamIgnore const &, SAL_UNUSED_PARAMETER T const &) |
93 | { |
94 | std::abort(); |
95 | #if defined _MSC_VER && _MSC_VER < 1700 |
96 | return StreamIgnore(); |
97 | #endif |
98 | } |
99 | |
100 | template< typename T > typename T::Result getResult(T const &); |
101 | |
102 | inline char const * unwrapStream(StreamString const & s) { return s.string; } |
103 | |
104 | inline char const * unwrapStream(SAL_UNUSED_PARAMETER StreamIgnore const &) { |
105 | std::abort(); |
106 | #if defined _MSC_VER && _MSC_VER < 1700 |
107 | return 0; |
108 | #endif |
109 | } |
110 | |
111 | } } |
112 | |
113 | #define SAL_DETAIL_LOG_STREAM(condition, level, area, where, stream) \ |
114 | do { \ |
115 | if (condition) { \ |
116 | if (sizeof ::sal::detail::getResult( \ |
117 | ::sal::detail::StreamStart() << stream) == 1) \ |
118 | { \ |
119 | ::sal_detail_log( \ |
120 | (level), (area), (where), \ |
121 | ::sal::detail::unwrapStream( \ |
122 | ::sal::detail::StreamStart() << stream)); \ |
123 | } else { \ |
124 | ::std::ostringstream sal_detail_stream; \ |
125 | sal_detail_stream << stream; \ |
126 | ::sal::detail::log( \ |
127 | (level), (area), (where), sal_detail_stream); \ |
128 | } \ |
129 | } \ |
130 | } while (false) |
131 | |
132 | /// @endcond |
133 | |
134 | /** A simple macro to create a "file and line number" string. |
135 | |
136 | Potentially not only useful within the log framework (where it is used |
137 | automatically), but also when creating exception messages. |
138 | |
139 | @attention For now, this functionality should only be used internally within |
140 | LibreOffice. It may change again in a future version. |
141 | |
142 | @since LibreOffice 3.5 |
143 | */ |
144 | #define SAL_WHERE SAL_DETAIL_WHERE |
145 | |
146 | /** A facility for generating temporary string messages by piping items into a |
147 | C++ std::ostringstream. |
148 | |
149 | This can be useful for example in a call to SAL_INFO when depending on some |
150 | boolean condition data of incompatible types shall be streamed into the |
151 | message, as in: |
152 | |
153 | SAL_INFO("foo", "object: " << (hasName ? obj->name : SAL_STREAM(obj))); |
154 | |
155 | @attention For now, this functionality should only be used internally within |
156 | LibreOffice. It may change again in a future version. |
157 | |
158 | @since LibreOffice 3.5 |
159 | */ |
160 | #ifdef _LIBCPP_VERSION |
161 | #define SAL_STREAM(stream) \ |
162 | (::std::ostringstream() << stream).str() |
163 | #else |
164 | #define SAL_STREAM(stream) \ |
165 | (dynamic_cast< ::std::ostringstream & >(::std::ostringstream() << stream).str()) |
166 | #endif |
167 | |
168 | /** |
169 | @page sal_log Basic logging functionality. |
170 | |
171 | @short Macros for logging. |
172 | |
173 | SAL_INFO(char const * area, expr), |
174 | SAL_INFO_IF(bool condition, char const * area, expr), |
175 | SAL_WARN(char const * area, expr), |
176 | SAL_WARN_IF(bool condition, char const * area, expr), and SAL_DEBUG(expr) |
177 | produce an info, warning, or debug log entry with a message produced by |
178 | piping items into a C++ std::ostringstream. The given expr must be so that |
179 | the full expression "stream << expr" is valid, where stream is a variable of |
180 | type std::ostringstream. |
181 | |
182 | SAL_INFO("foo", "string " << s << " of length " << n) |
183 | |
184 | would be an example of such a call. |
185 | |
186 | The composed message should be in UTF-8 and it should contain no vertical |
187 | formatting characters and no null characters |
188 | |
189 | For the _IF variants, log output is only generated if the given condition is |
190 | true (in addition to the other conditions that have to be met). |
191 | |
192 | The SAL_DEBUG macro is for temporary debug statements that are used while |
193 | working on code. It is never meant to remain in the code. It will always |
194 | simply output the given expression in debug builds. |
195 | |
196 | For all the other macros, the given area argument must be non-null and must |
197 | match the regular expression |
198 | |
199 | @verbatim |
200 | <area> ::= <segment>("."<segment>)* |
201 | @endverbatim |
202 | |
203 | with |
204 | |
205 | @verbatim |
206 | <segment> ::= [0-9a-z]+ |
207 | @endverbatim |
208 | |
209 | For a list of areas used see @ref sal_log_areas "SAL debug areas". Whenever |
210 | you use a new log area, add it to the file include/sal/log-areas.dox . |
211 | |
212 | Whether these macros generate any log output is controlled in a two-stage |
213 | process. |
214 | |
215 | First, at compile time the macros SAL_LOG_INFO and SAL_LOG_WARN, |
216 | respectively, control whether the INFO and WARN macros, respectively, |
217 | expand to actual code (in case the macro is defined, to any value) or to |
218 | no-ops (in case the macro is not defined). |
219 | |
220 | Second, at runtime the environment variable SAL_LOG further limits which |
221 | macro calls actually generate log output. The environment variable SAL_LOG |
222 | must either be unset or must match the regular expression |
223 | |
224 | @verbatim |
225 | <env> ::= <switch>* |
226 | @endverbatim |
227 | |
228 | with |
229 | |
230 | @verbatim |
231 | <switch> ::= <sense><level>("."<area>)? |
232 | <sense> ::= "+"|"-" |
233 | <level> ::= "INFO"|"WARN" |
234 | @endverbatim |
235 | |
236 | If the environment variable is unset, "+WARN" is used instead (which results |
237 | in all warnings being output but no infos). If the given value does not |
238 | match the regular expression, "+INFO+WARN" is used instead (which in turn |
239 | results in everything being output). |
240 | |
241 | A given macro call's level (INFO or WARN) and area is matched against the |
242 | given switches as follows: Only those switches for which the level matches |
243 | the given level and for which the area is a prefix (including both empty and |
244 | full prefixes) of the given area are considered. Log output is generated if |
245 | and only if among the longest such switches (if any), there is at least one |
246 | that has a sense of "+". (That is, if both +INFO.foo and -INFO.foo are |
247 | present, +INFO.foo wins.) |
248 | |
249 | For example, if SAL_LOG is "+INFO-INFO.foo+INFO.foo.bar", then calls like |
250 | SAL_INFO("foo.bar", ...), SAL_INFO("foo.bar.baz", ...), or |
251 | SAL_INFO("other", ...) generate output, while calls like |
252 | SAL_INFO("foo", ...) or SAL_INFO("foo.barzzz", ...) do not. |
253 | |
254 | The generated log output consists of the given level ("info" or "warn"), the |
255 | given area, the process ID, the thread ID, the source file, and the source |
256 | line number, each followed by a colon, followed by a space, the given |
257 | message, and a newline. The precise format of the log output is subject to |
258 | change. The log output is printed to stderr without further text encoding |
259 | conversion. |
260 | |
261 | @see @ref sal_log_areas |
262 | |
263 | @attention For now, this functionality should only be used internally within |
264 | LibreOffice. It may change again in a future version. |
265 | |
266 | @since LibreOffice 3.5 |
267 | */ |
268 | |
269 | /** |
270 | Produce log entry from stream in the given log area. |
271 | |
272 | See @ref sal_log "basic logging functionality" for details. |
273 | */ |
274 | #define SAL_INFO(area, stream) \ |
275 | SAL_DETAIL_LOG_STREAM( \ |
276 | SAL_DETAIL_ENABLE_LOG_INFO, ::SAL_DETAIL_LOG_LEVEL_INFO, area, \ |
277 | SAL_WHERE, stream) |
278 | |
279 | /** |
280 | Produce log entry from stream in the given log area if condition is true. |
281 | |
282 | See @ref sal_log "basic logging functionality" for details. |
283 | */ |
284 | #define SAL_INFO_IF(condition, area, stream) \ |
285 | SAL_DETAIL_LOG_STREAM( \ |
286 | SAL_DETAIL_ENABLE_LOG_INFO && (condition), \ |
287 | ::SAL_DETAIL_LOG_LEVEL_INFO, area, SAL_WHERE, stream) |
288 | |
289 | /** |
290 | Produce warning entry from stream in the given log area. |
291 | |
292 | See @ref sal_log "basic logging functionality" for details. |
293 | */ |
294 | #define SAL_WARN(area, stream) \ |
295 | SAL_DETAIL_LOG_STREAM( \ |
296 | SAL_DETAIL_ENABLE_LOG_WARN, ::SAL_DETAIL_LOG_LEVEL_WARN, area, \ |
297 | SAL_WHERE, stream) |
298 | |
299 | /** |
300 | Produce warning entry from stream in the given log area if condition is true. |
301 | |
302 | See @ref sal_log "basic logging functionality" for details. |
303 | */ |
304 | #define SAL_WARN_IF(condition, area, stream) \ |
305 | SAL_DETAIL_LOG_STREAM( \ |
306 | SAL_DETAIL_ENABLE_LOG_WARN && (condition), \ |
307 | ::SAL_DETAIL_LOG_LEVEL_WARN, area, SAL_WHERE, stream) |
308 | |
309 | /** |
310 | Produce temporary debugging output from stream. This macro is meant to be |
311 | used only while working on code and should never exist in production code. |
312 | |
313 | See @ref sal_log "basic logging functionality" for details. |
314 | */ |
315 | #define SAL_DEBUG(stream) \ |
316 | SAL_DETAIL_LOG_STREAM( \ |
317 | SAL_LOG_TRUE, ::SAL_DETAIL_LOG_LEVEL_DEBUG, 0, 0, stream) |
318 | |
319 | #endif |
320 | |
321 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |
322 | |