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