Imported Upstream version 17.28.8
[platform/upstream/libzypp.git] / zypp-core / base / LogControl.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/base/LogControl.cc
10  *
11 */
12 #include <iostream>
13 #include <fstream>
14 #include <string>
15 #include <mutex>
16 #include <map>
17
18 #include <zypp-core/base/Logger.h>
19 #include <zypp-core/base/LogControl.h>
20 #include <zypp-core/base/ProfilingFormater.h>
21 #include <zypp-core/base/String.h>
22 #include <zypp-core/Date.h>
23 #include <zypp-core/TriBool.h>
24 #include <zypp-core/AutoDispose.h>
25
26 #include <zypp-core/zyppng/io/Socket>
27 #include <zypp-core/zyppng/io/SockAddr>
28 #include <zypp-core/zyppng/base/EventLoop>
29 #include <zypp-core/zyppng/base/EventDispatcher>
30 #include <zypp-core/zyppng/base/Timer>
31 #include <zypp-core/zyppng/base/private/linuxhelpers_p.h>
32 #include <zypp-core/zyppng/thread/Wakeup>
33 #include <zypp-core/zyppng/base/private/threaddata_p.h>
34 #include <zypp-core/zyppng/base/SocketNotifier>
35
36 #include <thread>
37 #include <variant>
38 #include <atomic>
39 #include <csignal>
40
41 extern "C"
42 {
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <fcntl.h>
46 #include <unistd.h>
47 #include <dirent.h>
48 }
49
50 using std::endl;
51
52 std::once_flag flagReadEnvAutomatically;
53
54 namespace zypp
55 {
56   constexpr std::string_view ZYPP_MAIN_THREAD_NAME( "Zypp-main" );
57
58   template<class> inline constexpr bool always_false_v = false;
59
60   /*!
61    * \internal Provides a very simply lock that is using atomics so it can be
62    * safely used in signal handlers
63    */
64   class SpinLock {
65   public:
66     void lock () {
67       // acquire lock
68       while ( _atomicLock.test_and_set())
69         // reschedule the current thread while we wait, maybe when its our next turn the lock is free again
70         std::this_thread::yield();
71     }
72
73     void unlock() {
74       _atomicLock.clear();
75     }
76
77   private:
78     // we use a lock free atomic flag here so this lock can be safely obtained in a signal handler as well
79     std::atomic_flag _atomicLock = ATOMIC_FLAG_INIT;
80   };
81
82   class LogThread
83   {
84
85   public:
86
87     ~LogThread() {
88       stop();
89     }
90
91     static LogThread &instance () {
92       static LogThread t;
93       return t;
94     }
95
96     void setLineWriter ( boost::shared_ptr<log::LineWriter> writer ) {
97       std::lock_guard lk( _lineWriterLock );
98       _lineWriter = writer;
99     }
100
101     boost::shared_ptr<log::LineWriter> getLineWriter () {
102       std::lock_guard lk( _lineWriterLock );
103       auto lw = _lineWriter;
104       return lw;
105     }
106
107     void stop () {
108       _stopSignal.notify();
109       if ( _thread.get_id() != std::this_thread::get_id() )
110         _thread.join();
111     }
112
113     std::thread::id threadId () {
114       return _thread.get_id();
115     }
116
117     static std::string sockPath () {
118       static std::string path = zypp::str::Format("zypp-logsocket-%1%") % getpid();
119       return path;
120     }
121
122   private:
123
124     LogThread ()
125     {
126       // Name the thread that started the logger assuming it's main thread.
127       zyppng::ThreadData::current().setName(ZYPP_MAIN_THREAD_NAME);
128       _thread = std::thread( [this] () {
129         workerMain();
130       });
131     }
132
133     void workerMain () {
134
135       // force the kernel to pick another thread to handle those signals
136       zyppng::blockSignalsForCurrentThread( { SIGTERM, SIGINT, SIGPIPE, } );
137
138       zyppng::ThreadData::current().setName("Zypp-Log");
139
140       auto ev = zyppng::EventLoop::create();
141       auto server = zyppng::Socket::create( AF_UNIX, SOCK_STREAM, 0 );
142       auto stopNotifyWatch = _stopSignal.makeNotifier( );
143
144       std::vector<zyppng::Socket::Ptr> clients;
145
146       // bind to a abstract unix domain socket address, which means we do not need to care about cleaning it up
147       server->bind( std::make_shared<zyppng::UnixSockAddr>( sockPath(), true ) );
148       server->listen();
149
150       // wait for incoming connections from other threads
151       server->connectFunc( &zyppng::Socket::sigIncomingConnection, [&](){
152
153         auto cl = server->accept();
154         if ( !cl ) return;
155         clients.push_back( cl );
156
157         // wait until data is available, we operate line by line so we only
158         // log a string once we encounter \n
159         cl->connectFunc( &zyppng::Socket::sigReadyRead, [ this, sock = cl.get() ](){
160           auto writer = getLineWriter();
161           if ( !writer ) return;
162           while ( sock->canReadLine() ) {
163             auto br = sock->readLine();
164             writer->writeOut( std::string( br.data(), br.size() - 1 ) );
165           }
166         }, *cl);
167
168         // once a client disconnects we remove it from the std::vector so that the socket is not leaked
169         cl->connectFunc( &zyppng::Socket::sigDisconnected, [&clients, sock = std::weak_ptr(cl)](){
170           auto lock = sock.lock();
171           if ( !lock  )
172             return;
173
174           auto idx = std::find_if( clients.begin(), clients.end(), [lock]( const auto &s ){ return lock.get() == s.get(); } );
175           clients.erase( idx );
176         });
177
178       });
179
180       stopNotifyWatch->connectFunc( &zyppng::SocketNotifier::sigActivated, [&ev]( const auto &, auto ) {
181         ev->quit();
182       });
183
184       ev->run();
185
186       // make sure we have written everything
187       auto writer = getLineWriter();
188       if ( writer ) {
189         for ( auto &sock : clients ){
190           while ( sock->canReadLine() ) {
191             auto br = sock->readLine();
192             writer->writeOut( std::string( br.data(), br.size() - 1 ) );
193           }
194         }
195       }
196     }
197
198   private:
199     std::thread _thread;
200     zyppng::Wakeup _stopSignal;
201
202     // since the public API uses boost::shared_ptr we can not use the atomic
203     // functionalities provided in std.
204     // this lock type can be used safely in signals
205     SpinLock _lineWriterLock;
206     // boost shared_ptr has a lock free implementation of reference counting so it can be used from signal handlers as well
207     boost::shared_ptr<log::LineWriter> _lineWriter{ nullptr };
208   };
209
210   class LogClient
211   {
212    public:
213     LogClient(){
214       // make sure the thread is running
215       LogThread::instance();
216     }
217
218     ~LogClient() {
219       ::close( _sockFD );
220     }
221
222     /*!
223      * Tries to connect to the log threads socket, returns true on success or
224      * if the socket is already connected
225      */
226     bool ensureConnection () {
227       if ( _sockFD >= 0 )
228         return true;
229
230       _sockFD = ::socket( AF_UNIX, SOCK_STREAM, 0 );
231       if ( _sockFD == -1 )
232         return false;
233
234       zyppng::UnixSockAddr addr( LogThread::sockPath(), true );
235       return zyppng::trySocketConnection( _sockFD, addr, 100 );
236     }
237
238     /*!
239      * Sends a message to the log thread.
240      */
241     void pushMessage ( std::string &&msg ) {
242       if ( inPushMessage ) {
243         return;
244       }
245
246       // make sure we do not end up in a busy loop
247       zypp::AutoDispose<bool *> res( &inPushMessage, [](auto val){
248         *val = false;
249       });
250       inPushMessage = true;
251
252       // if we are in the same thread as the Log worker we can directly push our messages out, no need to use the socket
253       if ( std::this_thread::get_id() == LogThread::instance().threadId() ) {
254         auto writer = LogThread::instance().getLineWriter();
255         if ( writer )
256           writer->writeOut( msg );
257         return;
258       }
259
260       if(!ensureConnection())
261         return;
262
263       if ( msg.back() != '\n' )
264         msg.push_back('\n');
265
266       size_t written = 0;
267       while ( written < msg.size() ) {
268         const auto res = zyppng::eintrSafeCall( ::send, _sockFD, msg.data() + written, msg.size() - written, MSG_NOSIGNAL );
269         if ( res == -1 ) {
270           //assume broken socket
271           ::close( _sockFD );
272           _sockFD = -1;
273           return;
274         }
275         written += res;
276       }
277     }
278
279     private:
280       int _sockFD = -1;
281       bool inPushMessage = false;
282   };
283
284 #ifndef ZYPP_NDEBUG
285   namespace debug
286   {
287     // Fg::Black:   30  Bg: 40 Attr::Normal:  22;27
288     // Fg::Red:     31  ...    Attr::Bright:  1
289     // Fg::Green:   32         Attr::Reverse: 7
290     // Fg::Yellow:  33
291     // Fg::Blue:    34
292     // Fg::Magenta: 35
293     // Fg::Cyan:    36
294     // Fg::White:   37
295     // Fg::Default: 39
296     static constexpr std::string_view OO { "\033[0m" };
297     static constexpr std::string_view WH { "\033[37;40m" };
298     static constexpr std::string_view CY { "\033[36;40m" };
299     static constexpr std::string_view YE { "\033[33;1;40m" };
300     static constexpr std::string_view GR { "\033[32;40m" };
301     static constexpr std::string_view RE { "\033[31;1;40m" };
302     static constexpr std::string_view MA { "\033[35;40m" };
303
304     unsigned TraceLeave::_depth = 1;
305
306     std::string tracestr( char tag_r, unsigned depth_r, const char * file_r, const char * fnc_r, int line_r )
307     {
308       static str::Format fmt { "*** %s %s(%s):%d" };
309       fmt % std::string(depth_r,tag_r) % file_r % fnc_r % line_r;
310       return fmt;
311     }
312
313     TraceLeave::TraceLeave( const char * file_r, const char * fnc_r, int line_r )
314     : _file( std::move(file_r) )
315     , _fnc( std::move(fnc_r) )
316     , _line( line_r )
317     {
318       const std::string & m { tracestr( '>',_depth++, _file,_fnc,_line ) };
319       USR << m << endl;
320       Osd(L_USR("TRACE"),1) << m << endl;
321     }
322
323     TraceLeave::~TraceLeave()
324     {
325       const std::string & m { tracestr( '<',--_depth, _file,_fnc,_line ) };
326       USR << m << endl;
327       Osd(L_USR("TRACE"),1) << m << endl;
328     }
329
330     Osd::Osd( std::ostream & str, int i )
331     : _strout { std::cerr }
332     , _strlog { str }
333     { _strout << (i?WH:YE); }
334
335     Osd::~Osd()
336     { _strout << OO; }
337
338     Osd & Osd::operator<<( std::ostream& (*iomanip)( std::ostream& ) )
339     {
340       _strout << iomanip;
341       _strlog << iomanip;
342       return *this;
343     }
344 }
345 #endif // ZYPP_NDEBUG
346
347   ///////////////////////////////////////////////////////////////////
348   namespace log
349   { /////////////////////////////////////////////////////////////////
350
351     StdoutLineWriter::StdoutLineWriter()
352       : StreamLineWriter( std::cout )
353     {}
354
355     StderrLineWriter::StderrLineWriter()
356       : StreamLineWriter( std::cerr )
357     {}
358
359     FileLineWriter::FileLineWriter( const Pathname & file_r, mode_t mode_r )
360     {
361       if ( file_r == Pathname("-") )
362       {
363         _str = &std::cerr;
364       }
365       else
366       {
367         if ( mode_r )
368         {
369           // not filesystem::assert_file as filesystem:: functions log,
370           // and this FileWriter is not yet in place.
371           int fd = ::open( file_r.c_str(), O_CREAT|O_EXCL, mode_r );
372           if ( fd != -1 )
373             ::close( fd );
374         }
375         // set unbuffered write
376         std::ofstream * fstr = 0;
377         _outs.reset( (fstr = new std::ofstream( file_r.asString().c_str(), std::ios_base::app )) );
378         fstr->rdbuf()->pubsetbuf(0,0);
379         _str = &(*fstr);
380       }
381     }
382
383     /////////////////////////////////////////////////////////////////
384   } // namespace log
385   ///////////////////////////////////////////////////////////////////
386
387   ///////////////////////////////////////////////////////////////////
388   namespace base
389   { /////////////////////////////////////////////////////////////////
390     ///////////////////////////////////////////////////////////////////
391     namespace logger
392     { /////////////////////////////////////////////////////////////////
393
394       inline void putStream( const std::string & group_r, LogLevel level_r,
395                              const char * file_r, const char * func_r, int line_r,
396                              const std::string & buffer_r );
397
398       ///////////////////////////////////////////////////////////////////
399       //
400       //        CLASS NAME : Loglinebuf
401       //
402       class Loglinebuf : public std::streambuf {
403
404       public:
405         /** */
406         Loglinebuf( const std::string & group_r, LogLevel level_r )
407         : _group( group_r )
408         , _level( level_r )
409         , _file( "" )
410         , _func( "" )
411         , _line( -1 )
412         {}
413         /** */
414         ~Loglinebuf()
415         {
416           if ( !_buffer.empty() )
417             writeout( "\n", 1 );
418         }
419
420         /** */
421         void tagSet( const char * fil_r, const char * fnc_r, int lne_r )
422         {
423           _file = fil_r;
424           _func = fnc_r;
425           _line = lne_r;
426         }
427
428       private:
429         /** */
430         virtual std::streamsize xsputn( const char * s, std::streamsize n )
431         { return writeout( s, n ); }
432         /** */
433         virtual int overflow( int ch = EOF )
434         {
435           if ( ch != EOF )
436             {
437               char tmp = ch;
438               writeout( &tmp, 1 );
439             }
440           return 0;
441         }
442         /** */
443         virtual int writeout( const char* s, std::streamsize n )
444         {
445           //logger::putStream( _group, _level, _file, _func, _line, _buffer );
446           //return n;
447           if ( s && n )
448             {
449               const char * c = s;
450               for ( int i = 0; i < n; ++i, ++c )
451                 {
452                   if ( *c == '\n' ) {
453                     _buffer += std::string( s, c-s );
454                     logger::putStream( _group, _level, _file, _func, _line, _buffer );
455                     _buffer = std::string();
456                     s = c+1;
457                   }
458                 }
459               if ( s < c )
460                 {
461                   _buffer += std::string( s, c-s );
462                 }
463             }
464           return n;
465         }
466
467       private:
468         std::string  _group;
469         LogLevel     _level;
470         const char * _file;
471         const char * _func;
472         int          _line;
473         std::string  _buffer;
474       };
475
476       ///////////////////////////////////////////////////////////////////
477
478       ///////////////////////////////////////////////////////////////////
479       //
480       //        CLASS NAME : Loglinestream
481       //
482       class Loglinestream {
483
484       public:
485         /** */
486         Loglinestream( const std::string & group_r, LogLevel level_r )
487         : _mybuf( group_r, level_r )
488         , _mystream( &_mybuf )
489         {}
490         /** */
491         ~Loglinestream()
492         { _mystream.flush(); }
493
494       public:
495         /** */
496         std::ostream & getStream( const char * fil_r, const char * fnc_r, int lne_r )
497         {
498           _mybuf.tagSet( fil_r, fnc_r, lne_r );
499           return _mystream;
500         }
501
502       private:
503         Loglinebuf   _mybuf;
504         std::ostream _mystream;
505       };
506       ///////////////////////////////////////////////////////////////////
507
508       struct LogControlImpl;
509
510       /*
511        * Ugly hack to prevent the use of LogControlImpl when libzypp is shutting down.
512        * Due to the c++ std thread_local static instances are cleaned up before the first global static
513        * destructor is called. So all classes that use logging after that point in time would crash the
514        * application because its accessing a variable that has already been destroyed.
515        */
516       int &logControlValidFlag() {
517         // We are using a POD flag that does not have a destructor,
518         // to flag if the thread_local destructors were already executed.
519         // Since TLS data is stored in a segment that is available until the thread ceases to exist it should still be readable
520         // after thread_local c++ destructors were already executed. Or so I hope.
521         static thread_local int logControlValid = 0;
522         return logControlValid;
523       }
524
525       ///////////////////////////////////////////////////////////////////
526       //
527       //        CLASS NAME : LogControlImpl
528       //
529       /** LogControl implementation (thread_local Singleton).
530        *
531        * \note There is a slight difference in using the _lineFormater and _lineWriter!
532        * \li \c _lineFormater must not be NULL (create default LogControl::LineFormater)
533        * \li \c _lineWriter is NULL if no logging is performed, this way we can pass
534        *        _no_stream as logstream to the application, and avoid unnecessary formating
535        *        of logliles, which would then be discarded when passed to some dummy
536        *        LineWriter.
537       */
538       struct LogControlImpl
539       {
540       public:
541         bool isExcessive() const
542         { return _excessive; }
543
544         void excessive( bool onOff_r )
545         { _excessive = onOff_r; }
546
547
548         /** Hint for Formater whether to hide the thread name. */
549         bool hideThreadName() const
550         {
551           if ( indeterminate(_hideThreadName) )
552             _hideThreadName = ( zyppng::ThreadData::current().name() == ZYPP_MAIN_THREAD_NAME );
553           return bool(_hideThreadName);
554         }
555         /** \overload Setter */
556         void hideThreadName( bool onOff_r )
557         { _hideThreadName = onOff_r; }
558         /** \overload Static getter */
559         static bool instanceHideThreadName()
560         {
561           auto impl = LogControlImpl::instance();
562           return impl ? impl->hideThreadName() : false;
563         }
564         /** \overload Static setter */
565         static void instanceHideThreadName( bool onOff_r )
566         {
567           auto impl = LogControlImpl::instance();
568           if ( impl ) impl->hideThreadName( onOff_r );
569         }
570
571
572         /** NULL _lineWriter indicates no loggin. */
573         void setLineWriter( const shared_ptr<LogControl::LineWriter> & writer_r )
574         { LogThread::instance().setLineWriter( writer_r ); }
575
576         shared_ptr<LogControl::LineWriter> getLineWriter() const
577         { return LogThread::instance().getLineWriter(); }
578
579         /** Assert \a _lineFormater is not NULL. */
580         void setLineFormater( const shared_ptr<LogControl::LineFormater> & format_r )
581         {
582           if ( format_r )
583             _lineFormater = format_r;
584           else
585             _lineFormater.reset( new LogControl::LineFormater );
586         }
587
588         void logfile( const Pathname & logfile_r, mode_t mode_r = 0640 )
589         {
590           if ( logfile_r.empty() )
591             setLineWriter( shared_ptr<LogControl::LineWriter>() );
592           else if ( logfile_r == Pathname( "-" ) )
593             setLineWriter( shared_ptr<LogControl::LineWriter>(new log::StderrLineWriter) );
594           else
595             setLineWriter( shared_ptr<LogControl::LineWriter>(new log::FileLineWriter(logfile_r, mode_r)) );
596         }
597
598       private:
599         LogClient    _logClient;
600         std::ostream _no_stream;
601         bool         _excessive;
602         mutable TriBool _hideThreadName = indeterminate;        ///< Hint for Formater whether to hide the thread name.
603
604         shared_ptr<LogControl::LineFormater> _lineFormater;
605
606       public:
607         /** Provide the log stream to write (logger interface) */
608         std::ostream & getStream( const std::string & group_r,
609                                   LogLevel            level_r,
610                                   const char *        file_r,
611                                   const char *        func_r,
612                                   const int           line_r )
613         {
614           if ( ! getLineWriter() )
615             return _no_stream;
616           if ( level_r == E_XXX && !_excessive )
617             return _no_stream;
618
619           if ( !_streamtable[group_r][level_r] )
620             {
621               _streamtable[group_r][level_r].reset( new Loglinestream( group_r, level_r ) );
622             }
623           std::ostream & ret( _streamtable[group_r][level_r]->getStream( file_r, func_r, line_r ) );
624           if ( !ret )
625           {
626             ret.clear();
627             ret << "---<RESET LOGSTREAM FROM FAILED STATE]" << endl;
628           }
629           return ret;
630         }
631
632         /** Format and write out a logline from Loglinebuf. */
633         void putStream( const std::string & group_r,
634                         LogLevel            level_r,
635                         const char *        file_r,
636                         const char *        func_r,
637                         int                 line_r,
638                         const std::string & message_r )
639         {
640           _logClient.pushMessage( _lineFormater->format( group_r, level_r,
641                                                                     file_r, func_r, line_r,
642                                                                     message_r ) );
643         }
644
645       private:
646         typedef shared_ptr<Loglinestream>        StreamPtr;
647         typedef std::map<LogLevel,StreamPtr>     StreamSet;
648         typedef std::map<std::string,StreamSet>  StreamTable;
649         /** one streambuffer per group and level */
650         StreamTable _streamtable;
651         zyppng::Socket::Ptr _sock;
652
653       private:
654
655         void readEnvVars () {
656           if ( getenv("ZYPP_LOGFILE") )
657             logfile( getenv("ZYPP_LOGFILE") );
658
659           if ( getenv("ZYPP_PROFILING") )
660           {
661             shared_ptr<LogControl::LineFormater> formater(new ProfilingFormater);
662             setLineFormater(formater);
663           }
664         }
665         /** Singleton ctor.
666          * No logging per default, unless enabled via $ZYPP_LOGFILE.
667         */
668         LogControlImpl()
669         : _no_stream( NULL )
670         , _excessive( getenv("ZYPP_FULLLOG") )
671         , _lineFormater( new LogControl::LineFormater )
672         {
673           logControlValidFlag() = 1;
674           std::call_once( flagReadEnvAutomatically, &LogControlImpl::readEnvVars, this);
675
676           // make sure the LogControl is invalidated when we fork
677           pthread_atfork( nullptr, nullptr, &LogControl::notifyFork );
678         }
679
680       public:
681
682         ~LogControlImpl()
683         {
684           logControlValidFlag() = 0;
685         }
686
687         /** The LogControlImpl singleton
688          * \note As most dtors log, it is inportant that the
689          * LogControlImpl instance is the last static variable
690          * destructed. At least destucted after all statics
691          * which log from their dtor.
692         */
693         static LogControlImpl *instance();
694       };
695       ///////////////////////////////////////////////////////////////////
696
697       // 'THE' LogControlImpl singleton
698       inline LogControlImpl *LogControlImpl::instance()
699       {
700         thread_local static LogControlImpl _instance;
701         if ( logControlValidFlag() > 0 )
702           return &_instance;
703         return nullptr;
704       }
705
706       ///////////////////////////////////////////////////////////////////
707
708       /** \relates LogControlImpl Stream output */
709       inline std::ostream & operator<<( std::ostream & str, const LogControlImpl & )
710       {
711         return str << "LogControlImpl";
712       }
713
714       ///////////////////////////////////////////////////////////////////
715       //
716       // Access from logger::
717       //
718       ///////////////////////////////////////////////////////////////////
719
720       std::ostream & getStream( const char * group_r,
721                                 LogLevel     level_r,
722                                 const char * file_r,
723                                 const char * func_r,
724                                 const int    line_r )
725       {
726         static std::ostream nstream(NULL);
727         auto control = LogControlImpl::instance();
728         if ( !control || !group_r || strlen(group_r ) == 0 ) {
729           return nstream;
730         }
731
732
733
734         return control->getStream( group_r,
735                                    level_r,
736                                    file_r,
737                                    func_r,
738                                    line_r );
739       }
740
741       /** That's what Loglinebuf calls.  */
742       inline void putStream( const std::string & group_r, LogLevel level_r,
743                              const char * file_r, const char * func_r, int line_r,
744                              const std::string & buffer_r )
745       {
746         auto control = LogControlImpl::instance();
747         if ( !control )
748           return;
749
750         control->putStream( group_r, level_r,
751                             file_r, func_r, line_r,
752                             buffer_r );
753       }
754
755       bool isExcessive()
756       {
757         auto impl = LogControlImpl::instance();
758         if ( !impl )
759           return false;
760         return impl->isExcessive();
761       }
762
763       /////////////////////////////////////////////////////////////////
764     } // namespace logger
765     ///////////////////////////////////////////////////////////////////
766
767     using logger::LogControlImpl;
768
769     ///////////////////////////////////////////////////////////////////
770     // LineFormater
771     ///////////////////////////////////////////////////////////////////
772     std::string LogControl::LineFormater::format( const std::string & group_r,
773                                                   logger::LogLevel    level_r,
774                                                   const char *        file_r,
775                                                   const char *        func_r,
776                                                   int                 line_r,
777                                                   const std::string & message_r )
778     {
779       static char hostname[1024];
780       static char nohostname[] = "unknown";
781       std::string now( Date::now().form( "%Y-%m-%d %H:%M:%S" ) );
782       std::string ret;
783       if ( LogControlImpl::instanceHideThreadName() )
784         ret = str::form( "%s <%d> %s(%d) [%s] %s(%s):%d %s",
785                          now.c_str(), level_r,
786                          ( gethostname( hostname, 1024 ) ? nohostname : hostname ),
787                          getpid(),
788                          group_r.c_str(),
789                          file_r, func_r, line_r,
790                          message_r.c_str() );
791       else
792         ret = str::form( "%s <%d> %s(%d) [%s] %s(%s):%d {T:%s} %s",
793                          now.c_str(), level_r,
794                          ( gethostname( hostname, 1024 ) ? nohostname : hostname ),
795                          getpid(),
796                          group_r.c_str(),
797                          file_r, func_r, line_r,
798                          zyppng::ThreadData::current().name().c_str(),
799                          message_r.c_str() );
800       return ret;
801     }
802
803     ///////////////////////////////////////////////////////////////////
804     //
805     //  CLASS NAME : LogControl
806     //  Forward to LogControlImpl singleton.
807     //
808     ///////////////////////////////////////////////////////////////////
809
810
811     void LogControl::logfile( const Pathname & logfile_r )
812     {
813       auto impl = LogControlImpl::instance();
814       if ( !impl )
815         return;
816
817       impl->logfile( logfile_r );
818     }
819
820     void LogControl::logfile( const Pathname & logfile_r, mode_t mode_r )
821     {
822       auto impl = LogControlImpl::instance();
823       if ( !impl )
824         return;
825
826       impl->logfile( logfile_r, mode_r );
827     }
828
829     shared_ptr<LogControl::LineWriter> LogControl::getLineWriter() const
830     {
831       auto impl = LogControlImpl::instance();
832       if ( !impl )
833         return nullptr;
834
835       return impl->getLineWriter();
836     }
837
838     void LogControl::setLineWriter( const shared_ptr<LineWriter> & writer_r )
839     {
840       auto impl = LogControlImpl::instance();
841       if ( !impl )
842         return;
843       impl->setLineWriter( writer_r );
844     }
845
846     void LogControl::setLineFormater( const shared_ptr<LineFormater> & formater_r )
847     {
848       auto impl = LogControlImpl::instance();
849       if ( !impl )
850         return;
851       impl->setLineFormater( formater_r );
852     }
853
854     void LogControl::logNothing()
855     {
856       auto impl = LogControlImpl::instance();
857       if ( !impl )
858         return;
859       impl->setLineWriter( shared_ptr<LineWriter>() );
860     }
861
862     void LogControl::logToStdErr()
863     {
864       auto impl = LogControlImpl::instance();
865       if ( !impl )
866         return;
867       impl->setLineWriter( shared_ptr<LineWriter>( new log::StderrLineWriter ) );
868     }
869
870     void base::LogControl::emergencyShutdown()
871     {
872       LogThread::instance().stop();
873     }
874
875     void LogControl::notifyFork()
876     {
877       logger::logControlValidFlag () = 0;
878     }
879
880     ///////////////////////////////////////////////////////////////////
881     //
882     // LogControl::TmpExcessive
883     //
884     ///////////////////////////////////////////////////////////////////
885     LogControl::TmpExcessive::TmpExcessive()
886     {
887       auto impl = LogControlImpl::instance();
888       if ( !impl )
889         return;
890       impl->excessive( true );
891     }
892     LogControl::TmpExcessive::~TmpExcessive()
893     {
894       auto impl = LogControlImpl::instance();
895       if ( !impl )
896         return;
897       impl->excessive( false );
898     }
899
900     /******************************************************************
901      **
902      ** FUNCTION NAME : operator<<
903      ** FUNCTION TYPE : std::ostream &
904     */
905     std::ostream & operator<<( std::ostream & str, const LogControl & )
906     {
907       auto impl = LogControlImpl::instance();
908       if ( !impl )
909         return str;
910       return str << *impl;
911     }
912
913     /////////////////////////////////////////////////////////////////
914   } // namespace base
915   ///////////////////////////////////////////////////////////////////
916   /////////////////////////////////////////////////////////////////
917 } // namespace zypp
918 ///////////////////////////////////////////////////////////////////