summaryrefslogtreecommitdiff
path: root/sal/inc/sal/log.hxx
blob: 2e09c29e5aa95d33e20e8aaf6714fe297f9b757d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
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
272
273
274
275
276
277
278
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * Version: MPL 1.1 / GPLv3+ / LGPLv3+
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License or as specified alternatively below. You may obtain a copy of
 * the License at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * Major Contributor(s):
 * [ Copyright (C) 2011 Stephan Bergmann, Red Hat <sbergman@redhat.com> (initial
 *   developer) ]
 *
 * All Rights Reserved.
 *
 * For minor contributions see the git repository.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 3 or later (the "GPLv3+"), or
 * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
 * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
 * instead of those above.
 */

#ifndef INCLUDED_SAL_LOG_HXX
#define INCLUDED_SAL_LOG_HXX

#include "sal/config.h"

#include <cstdlib>
#include <sstream>
#include <string>

#include "sal/detail/log.h"
#include "sal/types.h"

// Avoid the use of other sal code in this header as much as possible, so that
// this code can be called from other sal code without causing endless
// recursion.

/// @internal
extern "C" void SAL_CALL sal_detail_log(
    enum sal_detail_LogLevel level, char const * area, char const * where,
    char const * message);

/// @internal
namespace sal { namespace detail {

inline void SAL_CALL log(
    sal_detail_LogLevel level, char const * area, char const * where,
    std::ostringstream const & stream)
{
    // An alternative would be to have sal_detail_log take a std::ostringstream
    // pointer (via a C void pointer); the advantage would be smaller client
    // code (the ".str().c_str()" part would move into the implementation of
    // sal_detail_log) and potential for proper support of embedded null
    // characters within the message, but the disadvantage would be dependence
    // on the C++ ABI; as a compromise, the ".str().c_str()" part has been moved
    // to this inline function so that it is potentially only emitted once per
    // dynamic library:
    sal_detail_log(level, area, where, stream.str().c_str());
}

// Special handling of the common case where the message consists of just a
// string literal, to produce smaller call-site code:

struct StreamStart {};

struct StreamString {
    StreamString(char const * s): string(s) {}

    char const * string;

    typedef char Result;
};

struct StreamIgnore {
    typedef struct { char a[2]; } Result;
};

inline StreamString operator <<(StreamStart const &, char const * s) {
    return StreamString(s);
}

template< typename T > inline StreamIgnore operator <<(
    StreamStart const &, T const &)
{
    std::abort();
#if defined _MSC_VER
    return StreamIgnore();
#endif
}

template< typename T > inline StreamIgnore operator <<(
    StreamString const &, T const &)
{
    std::abort();
#if defined _MSC_VER
    return StreamIgnore();
#endif
}

template< typename T > inline StreamIgnore operator <<(
    StreamIgnore const &, T const &)
{
    std::abort();
#if defined _MSC_VER
    return StreamIgnore();
#endif
}

template< typename T > typename T::Result getResult(T const &);

inline char const * unwrapStream(StreamString const & s) { return s.string; }

inline char const * unwrapStream(StreamIgnore const &) {
    std::abort();
#if defined _MSC_VER
    return 0;
#endif
}

} }

/// @internal
#define SAL_DETAIL_LOG_STREAM(condition, level, area, where, stream) \
    do { \
        if (condition) { \
            if (sizeof ::sal::detail::getResult( \
                    ::sal::detail::StreamStart() << stream) == 1) \
            { \
                ::sal_detail_log( \
                    (level), (area), (where), \
                    ::sal::detail::unwrapStream( \
                        ::sal::detail::StreamStart() << stream)); \
            } else { \
                ::std::ostringstream sal_detail_stream; \
                sal_detail_stream << stream; \
                ::sal::detail::log( \
                    (level), (area), (where), sal_detail_stream); \
            } \
        } \
    } while (false)

/** A simple macro to create a "file and line number" string.

    Potentially not only useful within the log framework (where it is used
    automatically), but also when creating exception messages.

    @since LibreOffice 3.5
*/
#define SAL_WHERE SAL_DETAIL_WHERE

/** A facility for generating temporary string messages by piping items into a
    C++ std::ostringstream.

    This can be useful for example in a call to SAL_INFO when depending on some
    boolean condition data of incompatible types shall be streamed into the
    message, as in:

      SAL_INFO("foo", "object: " << (hasName ? obj->name : SAL_STREAM(obj)));

    @since LibreOffice 3.5
*/
#define SAL_STREAM(stream) \
    (dynamic_cast< ::std::ostringstream & >(::std::ostringstream() << stream). \
     str())

/** Basic logging functionality.

    SAL_INFO(char const * area, expr),
    SAL_INFO_IF(bool condition, char const * area, expr),
    SAL_WARN(char const * area, expr), and
    SAL_WARN_IF(bool condition, char const * area, expr) produce an info resp.
    warning log entry with a message produced by piping items into a C++
    std::ostringstream.  The given expr must be so that the full expression
    "stream << expr" is valid, where stream is a variable of type
    std::ostringstream.

      SAL_INFO("foo", "string " << s << " of length " << n)

    would be an example of such a call; if the given s is of type rtl::OUString,

      #include "rtl/oustringostreaminserter.hxx"

    would make sure that an appropriate operator << is available.

    In either case, the composed message should be in UTF-8 and it should
    contain no vertical formatting characters and no null characters

    For the _IF variants, log output is only generated if the given condition is
    true (in addition to the other conditions that have to be met).

    For all these macros, the given area argument must be non-null and must
    match the regular expression

      <area> ::= <segment>("."<segment>)*

    with

      <segment> ::= [0-9a-z]+

    Whether these macros generate any log output is controlled in a two-stage
    process.

    First, at compile time the macros SAL_LOG_INFO and SAL_LOG_WARN,
    respectively, control whether the INFO and WARN macros, respectively,
    expand to actual code (in case the macro is defined, to any value) or to
    no-ops (in case the macro is not defined).

    Second, at runtime the environment variable SAL_LOG further limits which
    macro calls actually generate log output.  The environment variable SAL_LOG
    must either be unset or must match the regular expression

      <env> ::= <switch>*

    with

      <switch> ::= <sense><level>("."<area>)?
      <sense> ::= "+"|"-"
      <level> ::= "INFO"|"WARN"

    If the environment variable is unset, "+WARN" is used instead (which results
    in all warnings being output but no infos).  If the given value does not
    match the regular expression, "+INFO+WARN" is used instead (which in turn
    results in everything being output).

    A given macro call's level (INFO or WARN) and area is matched against the
    given switches as follows:  Only those switches for which the level matches
    the given level and for which the area is a prefix (including both empty and
    full prefixes) of the given area are considered.  Log output is generated if
    and only if among the longest such switches (if any), there is at least one
    that has a sense of "+".  (That is, if both +INFO.foo and -INFO.foo are
    present, +INFO.foo wins.)

    For example, if SAL_LOG is "+INFO-INFO.foo+INFO.foo.bar", then calls like
    SAL_INFO("foo.bar", ...), SAL_INFO("foo.bar.baz", ...), or
    SAL_INFO("other", ...) generate output, while calls like
    SAL_INFO("foo", ...) or SAL_INFO("foo.barzzz", ...) do not.

    The generated log output consists of the given level ("info" or "warn"), the
    given area, the process ID, the thread ID, the source file, and the source
    line number, each followed by a colon, followed by a space, the given
    message, and a newline.  The precise format of the log output is subject to
    change.  The log output is printed to stderr without further text encoding
    conversion.

    @since LibreOffice 3.5
*/

#define SAL_INFO(area, stream) \
    SAL_DETAIL_LOG_STREAM( \
        SAL_DETAIL_ENABLE_LOG_INFO, ::SAL_DETAIL_LOG_LEVEL_INFO, area, \
        SAL_WHERE, stream)

#define SAL_INFO_IF(condition, area, stream)  \
    SAL_DETAIL_LOG_STREAM( \
        SAL_DETAIL_ENABLE_LOG_INFO && (condition), \
        ::SAL_DETAIL_LOG_LEVEL_INFO, area, SAL_WHERE, stream)

#define SAL_WARN(area, stream) \
    SAL_DETAIL_LOG_STREAM( \
        SAL_DETAIL_ENABLE_LOG_WARN, ::SAL_DETAIL_LOG_LEVEL_WARN, area, \
        SAL_WHERE, stream)

#define SAL_WARN_IF(condition, area, stream)   \
    SAL_DETAIL_LOG_STREAM( \
        SAL_DETAIL_ENABLE_LOG_WARN && (condition), \
        ::SAL_DETAIL_LOG_LEVEL_WARN, area, SAL_WHERE, stream)

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */