1 /* === This file is part of Calamares - <https://calamares.io> ===
2  *
3  *   SPDX-FileCopyrightText: 2010-2011 Christian Muehlhaeuser <muesli@tomahawk-player.org>
4  *   SPDX-FileCopyrightText: 2014 Teo Mrnjavac <teo@kde.org>
5  *   SPDX-FileCopyrightText: 2017-2019 Adriaan de Groot <groot@kde.org>
6  *   SPDX-License-Identifier: GPL-3.0-or-later
7  *
8  *   Calamares is Free Software: see the License-Identifier above.
9  *
10  *
11  */
12 
13 #ifndef UTILS_LOGGER_H
14 #define UTILS_LOGGER_H
15 
16 #include "DllMacro.h"
17 
18 #include <QDebug>
19 #include <QSharedPointer>
20 
21 #include <memory>
22 
23 namespace Logger
24 {
25 class Once;
26 
27 struct FuncSuppressor
28 {
29     explicit constexpr FuncSuppressor( const char[] );
30     const char* m_s;
31 };
32 
33 struct NoQuote_t
34 {
35 };
36 struct Quote_t
37 {
38 };
39 
40 DLLEXPORT extern const FuncSuppressor Continuation;
41 DLLEXPORT extern const FuncSuppressor SubEntry;
42 DLLEXPORT extern const NoQuote_t NoQuote;
43 DLLEXPORT extern const Quote_t Quote;
44 
45 enum
46 {
47     LOG_DISABLE = 0,
48     LOGERROR = 1,
49     LOGWARNING = 2,
50     LOGDEBUG = 6,
51     LOGVERBOSE = 8
52 };
53 
54 class DLLEXPORT CDebug : public QDebug
55 {
56 public:
57     explicit CDebug( unsigned int debugLevel = LOGDEBUG, const char* func = nullptr );
58     virtual ~CDebug();
59 
60     friend CDebug& operator<<( CDebug&&, const FuncSuppressor& );
61     friend CDebug& operator<<( CDebug&&, const Once& );
62 
level()63     inline unsigned int level() const { return m_debugLevel; }
64 
65 private:
66     QString m_msg;
67     unsigned int m_debugLevel;
68     const char* m_funcinfo = nullptr;
69 };
70 
71 inline CDebug&
72 operator<<( CDebug&& s, const FuncSuppressor& f )
73 {
74     s.m_funcinfo = nullptr;
75     s << f.m_s;
76     return s;
77 }
78 
79 inline QDebug&
80 operator<<( QDebug& s, const FuncSuppressor& f )
81 {
82     return s << f.m_s;
83 }
84 
85 inline QDebug&
86 operator<<( QDebug& s, const NoQuote_t& )
87 {
88     return s.noquote().nospace();
89 }
90 
91 inline QDebug&
92 operator<<( QDebug& s, const Quote_t& )
93 {
94     return s.quote().space();
95 }
96 
97 /**
98  * @brief The full path of the log file.
99  */
100 DLLEXPORT QString logFile();
101 
102 /**
103  * @brief Start logging to the log file.
104  *
105  * Call this (once) to start logging to the log file (usually
106  * ~/.cache/calamares/session.log ). An existing log file is
107  * rolled over if it is too large.
108  */
109 DLLEXPORT void setupLogfile();
110 
111 /**
112  * @brief Set a log level for future logging.
113  *
114  * Pass in a value from the LOG* enum, above. Use 0 to
115  * disable logging. Values greater than LOGVERBOSE are
116  * limited to LOGVERBOSE, which will log everything.
117  *
118  * Practical values are 0, 1, 2, and 6.
119  */
120 DLLEXPORT void setupLogLevel( unsigned int level );
121 
122 /** @brief Return the configured log-level. */
123 DLLEXPORT unsigned int logLevel();
124 
125 /** @brief Would the given @p level really be logged? */
126 DLLEXPORT bool logLevelEnabled( unsigned int level );
127 
128 /**
129  * @brief Row-oriented formatted logging.
130  *
131  * Use DebugRow to produce multiple rows of 2-column output
132  * in a debugging statement. For instance,
133  *      cDebug() << DebugRow<int,int>(1,12)
134  *               << DebugRow<int,int>(2,24)
135  * will produce a single timestamped debug line with continuations.
136  * Each DebugRow produces one line of output, with the two values.
137  */
138 template < typename T, typename U >
139 struct DebugRow
140 {
141 public:
DebugRowDebugRow142     explicit DebugRow( const T& t, const U& u )
143         : first( t )
144         , second( u )
145     {
146     }
147 
148     const T& first;
149     const U& second;
150 };
151 
152 /**
153  * @brief List-oriented formatted logging.
154  *
155  * Use DebugList to produce multiple rows of output in a debugging
156  * statement. For instance,
157  *      cDebug() << DebugList( QStringList() << "foo" << "bar" )
158  * will produce a single timestamped debug line with continuations.
159  * Each element of the list of strings will be logged on a separate line.
160  */
161 /* TODO: Calamares 3.3, bump requirements to C++17, and rename
162  *       this to DebugList, dropping the convenience-definition
163  *       below. In C++17, class template argument deduction is
164  *       added, so `DebugList( whatever )` determines the right
165  *       type already (also for QStringList).
166  */
167 template < typename T >
168 struct DebugListT
169 {
170     using list_t = QList< T >;
171 
DebugListTDebugListT172     explicit DebugListT( const list_t& l )
173         : list( l )
174     {
175     }
176 
177     const list_t& list;
178 };
179 
180 ///@brief Convenience for QStringList, needs no template parameters
181 struct DebugList : public DebugListT< QString >
182 {
DebugListDebugList183     explicit DebugList( const list_t& l )
184         : DebugListT( l )
185     {
186     }
187 };
188 
189 /**
190  * @brief Map-oriented formatted logging.
191  *
192  * Use DebugMap to produce multiple rows of output in a debugging
193  * statement from a map. The output is intentionally a bit-yaml-ish.
194  *      cDebug() << DebugMap( map )
195  * will produce a single timestamped debug line with continuations.
196  * The continued lines will have a key (from the map) and a value
197  * on each line.
198  */
199 struct DebugMap
200 {
201 public:
DebugMapDebugMap202     explicit DebugMap( const QVariantMap& m )
203         : map( m )
204     {
205     }
206 
207     const QVariantMap& map;
208 };
209 
210 /**
211  * @brief Formatted logging of a pointer
212  *
213  * Pointers are printed as void-pointer, so just an address (unlike, say,
214  * QObject pointers which show an address and some metadata) preceded
215  * by an '@'. This avoids C-style (void*) casts in the code.
216  *
217  * Shared pointers are indicated by 'S@' and unique pointers by 'U@'.
218  */
219 struct Pointer
220 {
221 public:
PointerPointer222     explicit Pointer( const void* p )
223         : ptr( p )
224         , kind( 0 )
225     {
226     }
227 
228     template < typename T >
PointerPointer229     explicit Pointer( const std::shared_ptr< T >& p )
230         : ptr( p.get() )
231         , kind( 'S' )
232     {
233     }
234 
235     template < typename T >
PointerPointer236     explicit Pointer( const std::unique_ptr< T >& p )
237         : ptr( p.get() )
238         , kind( 'U' )
239     {
240     }
241 
242     const void* const ptr;
243     const char kind;
244 };
245 
246 /** @brief output operator for DebugRow */
247 template < typename T, typename U >
248 inline QDebug&
249 operator<<( QDebug& s, const DebugRow< T, U >& t )
250 {
251     s << Continuation << t.first << ':' << ' ' << t.second;
252     return s;
253 }
254 
255 /** @brief output operator for DebugList, assuming operator<< for T exists */
256 template < typename T = QString >
257 inline QDebug&
258 operator<<( QDebug& s, const DebugListT< T >& c )
259 {
260     for ( const auto& i : c.list )
261     {
262         s << Continuation << i;
263     }
264     return s;
265 }
266 
267 /** @brief supporting method for outputting a DebugMap */
268 QString toString( const QVariant& v );
269 
270 /** @brief output operator for DebugMap */
271 inline QDebug&
272 operator<<( QDebug& s, const DebugMap& t )
273 {
274     for ( auto it = t.map.constBegin(); it != t.map.constEnd(); ++it )
275     {
276         s << Continuation << it.key().toUtf8().constData() << ':' << ' ' << toString( it.value() ).toUtf8().constData();
277     }
278     return s;
279 }
280 
281 inline QDebug&
282 operator<<( QDebug& s, const Pointer& p )
283 {
284     s << NoQuote;
285     if ( p.kind )
286     {
287         s << p.kind;
288     }
289     s << '@' << p.ptr << Quote;
290     return s;
291 }
292 
293 /** @brief Convenience object for supplying SubEntry to a debug stream
294  *
295  * In a function with convoluted control paths, it may be unclear
296  * when to supply SubEntry to a debug stream -- it is convenient
297  * for the **first** debug statement from a given function to print
298  * the function header, and all subsequent onces to get SubEntry.
299  *
300  * Create an object of type Once and send it (first) to all CDebug
301  * objects; this will print the function header only once within the
302  * lifetime of that Once object.
303  */
304 class Once
305 {
306 public:
Once()307     Once()
308         : m( true )
309     {
310     }
311     friend CDebug& operator<<( CDebug&&, const Once& );
312 
313     /** @brief Restore the object to "fresh" state
314      *
315      * It may be necessary to allow the Once object to stream the
316      * function header again -- for instance, after logging an error,
317      * any following debug log might want to re-introduce the header.
318      */
refresh()319     void refresh() { m = true; }
320 
321 private:
322     mutable bool m = false;
323 };
324 
325 inline CDebug&
326 operator<<( CDebug&& s, const Once& o )
327 {
328     if ( !logLevelEnabled( s.level() ) )
329     {
330         // This won't print, so it's not using the "onceness"
331         return s;
332     }
333 
334     if ( o.m )
335     {
336         o.m = false;
337         return s;
338     }
339     s.m_funcinfo = nullptr;
340     s << SubEntry;
341     return s;
342 }
343 
344 }  // namespace Logger
345 
346 #define cVerbose() Logger::CDebug( Logger::LOGVERBOSE, Q_FUNC_INFO )
347 #define cDebug() Logger::CDebug( Logger::LOGDEBUG, Q_FUNC_INFO )
348 #define cWarning() Logger::CDebug( Logger::LOGWARNING, Q_FUNC_INFO )
349 #define cError() Logger::CDebug( Logger::LOGERROR, Q_FUNC_INFO )
350 
351 #endif
352