Create LogControlImpl on demand instead of using a static var.
[platform/upstream/libzypp.git] / zypp / 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
16 #include "zypp/base/Logger.h"
17 #include "zypp/base/LogControl.h"
18 #include "zypp/base/ProfilingFormater.h"
19 #include "zypp/base/String.h"
20 #include "zypp/Date.h"
21 #include "zypp/PathInfo.h"
22
23 using std::endl;
24
25 ///////////////////////////////////////////////////////////////////
26 namespace zypp
27 { /////////////////////////////////////////////////////////////////
28
29   ///////////////////////////////////////////////////////////////////
30   namespace log
31   { /////////////////////////////////////////////////////////////////
32
33     StdoutLineWriter::StdoutLineWriter()
34       : StreamLineWriter( std::cout )
35     {}
36
37     StderrLineWriter::StderrLineWriter()
38       : StreamLineWriter( std::cerr )
39     {}
40
41     FileLineWriter::FileLineWriter( const Pathname & file_r, mode_t mode_r )
42     {
43       if ( file_r == Pathname("-") )
44       {
45         _str = &std::cerr;
46       }
47       else
48       {
49         // set unbuffered write
50         std::ofstream * fstr = 0;
51         _outs.reset( (fstr = new std::ofstream( file_r.asString().c_str(), std::ios_base::app )) );
52         fstr->rdbuf()->pubsetbuf(0,0);
53         _str = &(*fstr);
54         if ( mode_r )
55         {
56           // not filesystem::chmod, as filesystem:: functions log,
57           // and this FileWriter is not yet in place.
58           ::chmod( file_r.asString().c_str(), mode_r );
59         }
60       }
61     }
62
63     /////////////////////////////////////////////////////////////////
64   } // namespace log
65   ///////////////////////////////////////////////////////////////////
66
67   ///////////////////////////////////////////////////////////////////
68   namespace base
69   { /////////////////////////////////////////////////////////////////
70
71     ///////////////////////////////////////////////////////////////////
72     // LineFormater
73     ///////////////////////////////////////////////////////////////////
74     std::string LogControl::LineFormater::format( const std::string & group_r,
75                                                   logger::LogLevel    level_r,
76                                                   const char *        file_r,
77                                                   const char *        func_r,
78                                                   int                 line_r,
79                                                   const std::string & message_r )
80     {
81       static char hostname[1024];
82       static char nohostname[] = "unknown";
83       std::string now( Date::now().form( "%Y-%m-%d %H:%M:%S" ) );
84       return str::form( "%s <%d> %s(%d) [%s] %s(%s):%d %s",
85                         now.c_str(), level_r,
86                         ( gethostname( hostname, 1024 ) ? nohostname : hostname ),
87                         getpid(),
88                         group_r.c_str(),
89                         file_r, func_r, line_r,
90                         message_r.c_str() );
91     }
92
93     ///////////////////////////////////////////////////////////////////
94     namespace logger
95     { /////////////////////////////////////////////////////////////////
96
97       inline void putStream( const std::string & group_r, LogLevel level_r,
98                              const char * file_r, const char * func_r, int line_r,
99                              const std::string & buffer_r );
100
101       ///////////////////////////////////////////////////////////////////
102       //
103       //        CLASS NAME : Loglinebuf
104       //
105       class Loglinebuf : public std::streambuf {
106
107       public:
108         /** */
109         Loglinebuf( const std::string & group_r, LogLevel level_r )
110         : _group( group_r )
111         , _level( level_r )
112         , _file( "" )
113         , _func( "" )
114         , _line( -1 )
115         {}
116         /** */
117         ~Loglinebuf()
118         {
119           if ( !_buffer.empty() )
120             writeout( "\n", 1 );
121         }
122
123         /** */
124         void tagSet( const char * fil_r, const char * fnc_r, int lne_r )
125         {
126           _file = fil_r;
127           _func = fnc_r;
128           _line = lne_r;
129         }
130
131       private:
132         /** */
133         virtual std::streamsize xsputn( const char * s, std::streamsize n )
134         { return writeout( s, n ); }
135         /** */
136         virtual int overflow( int ch = EOF )
137         {
138           if ( ch != EOF )
139             {
140               char tmp = ch;
141               writeout( &tmp, 1 );
142             }
143           return 0;
144         }
145         /** */
146         virtual int writeout( const char* s, std::streamsize n )
147         {
148           if ( s && n )
149             {
150               const char * c = s;
151               for ( int i = 0; i < n; ++i, ++c )
152                 {
153                   if ( *c == '\n' ) {
154                     _buffer += std::string( s, c-s );
155                     logger::putStream( _group, _level, _file, _func, _line, _buffer );
156                     _buffer = std::string();
157                     s = c+1;
158                   }
159                 }
160               if ( s < c )
161                 {
162                   _buffer += std::string( s, c-s );
163                 }
164             }
165           return n;
166         }
167
168       private:
169         std::string  _group;
170         LogLevel     _level;
171         const char * _file;
172         const char * _func;
173         int          _line;
174         std::string  _buffer;
175       };
176
177       ///////////////////////////////////////////////////////////////////
178
179       ///////////////////////////////////////////////////////////////////
180       //
181       //        CLASS NAME : Loglinestream
182       //
183       class Loglinestream {
184
185       public:
186         /** */
187         Loglinestream( const std::string & group_r, LogLevel level_r )
188         : _mybuf( group_r, level_r )
189         , _mystream( &_mybuf )
190         {}
191         /** */
192         ~Loglinestream()
193         { _mystream.flush(); }
194
195       public:
196         /** */
197         std::ostream & getStream( const char * fil_r, const char * fnc_r, int lne_r )
198         {
199           _mybuf.tagSet( fil_r, fnc_r, lne_r );
200           return _mystream;
201         }
202
203       private:
204         Loglinebuf   _mybuf;
205         std::ostream _mystream;
206       };
207       ///////////////////////////////////////////////////////////////////
208
209       ///////////////////////////////////////////////////////////////////
210       //
211       //        CLASS NAME : LogControlImpl
212       //
213       /** LogControl implementation (Singleton).
214        *
215        * \note There is a slight difference in using the _lineFormater and _lineWriter!
216        * \li \c _lineFormater must not be NULL (create default LogControl::LineFormater)
217        * \li \c _lineWriter is NULL if no logging is performed, this way we can pass
218        *        _no_stream as logstream to the application, and avoid unnecessary formating
219        *        of logliles, which would then be discarded when passed to some dummy
220        *        LineWriter.
221       */
222       struct LogControlImpl
223       {
224       public:
225         bool isExcessive()
226         { return _excessive; }
227
228         void excessive( bool onOff_r )
229         { _excessive = onOff_r; }
230
231         /** NULL _lineWriter indicates no loggin. */
232         void setLineWriter( const shared_ptr<LogControl::LineWriter> & writer_r )
233         { _lineWriter = writer_r; }
234
235         shared_ptr<LogControl::LineWriter> getLineWriter() const
236         { return _lineWriter; }
237
238         /** Assert \a _lineFormater is not NULL. */
239         void setLineFormater( const shared_ptr<LogControl::LineFormater> & format_r )
240         {
241           if ( format_r )
242             _lineFormater = format_r;
243           else
244             _lineFormater.reset( new LogControl::LineFormater );
245         }
246
247         void logfile( const Pathname & logfile_r, mode_t mode_r = 0640 )
248         {
249           if ( logfile_r.empty() )
250             setLineWriter( shared_ptr<LogControl::LineWriter>() );
251           else if ( logfile_r == Pathname( "-" ) )
252             setLineWriter( shared_ptr<LogControl::LineWriter>(new log::StderrLineWriter) );
253           else
254             setLineWriter( shared_ptr<LogControl::LineWriter>(new log::FileLineWriter(logfile_r, mode_r)) );
255         }
256
257       private:
258         std::ostream _no_stream;
259         bool         _excessive;
260
261         shared_ptr<LogControl::LineFormater> _lineFormater;
262         shared_ptr<LogControl::LineWriter>   _lineWriter;
263
264       public:
265         /** Provide the log stream to write (logger interface) */
266         std::ostream & getStream( const std::string & group_r,
267                                   LogLevel            level_r,
268                                   const char *        file_r,
269                                   const char *        func_r,
270                                   const int           line_r )
271         {
272           if ( ! _lineWriter )
273             return _no_stream;
274           if ( level_r == E_XXX && !_excessive )
275             return _no_stream;
276
277           if ( !_streamtable[group_r][level_r] )
278             {
279               _streamtable[group_r][level_r].reset( new Loglinestream( group_r, level_r ) );
280             }
281           return _streamtable[group_r][level_r]->getStream( file_r, func_r, line_r );
282         }
283
284         /** Format and write out a logline from Loglinebuf. */
285         void putStream( const std::string & group_r,
286                         LogLevel            level_r,
287                         const char *        file_r,
288                         const char *        func_r,
289                         int                 line_r,
290                         const std::string & message_r )
291         {
292           if ( _lineWriter )
293             _lineWriter->writeOut( _lineFormater->format( group_r, level_r,
294                                                           file_r, func_r, line_r,
295                                                           message_r ) );
296         }
297
298       private:
299         typedef shared_ptr<Loglinestream>        StreamPtr;
300         typedef std::map<LogLevel,StreamPtr>     StreamSet;
301         typedef std::map<std::string,StreamSet>  StreamTable;
302         /** one streambuffer per group and level */
303         StreamTable _streamtable;
304
305       private:
306         /** Singleton ctor.
307          * No logging per default, unless enabled via $ZYPP_LOGFILE.
308         */
309         LogControlImpl()
310         : _no_stream( NULL )
311         , _excessive( getenv("ZYPP_FULLLOG") )
312         , _lineFormater( new LogControl::LineFormater )
313         {
314           if ( getenv("ZYPP_LOGFILE") )
315             logfile( getenv("ZYPP_LOGFILE") );
316
317           if ( getenv("ZYPP_PROFILING") )
318           {
319             shared_ptr<LogControl::LineFormater> formater(new ProfilingFormater);
320             setLineFormater(formater);
321           }
322         }
323
324       public:
325         /** The LogControlImpl singleton
326          * \note As most dtors log, it is inportant that the
327          * LogControlImpl instance is the last static variable
328          * destructed. At least destucted after all statics
329          * which log from their dtor.
330         */
331         static LogControlImpl & instance();
332       };
333       ///////////////////////////////////////////////////////////////////
334
335       // 'THE' LogControlImpl singleton
336       inline LogControlImpl & LogControlImpl::instance()
337       {
338         static LogControlImpl _instance;
339         return _instance;
340       }
341
342       ///////////////////////////////////////////////////////////////////
343
344       /** \relates LogControlImpl Stream output */
345       inline std::ostream & operator<<( std::ostream & str, const LogControlImpl & obj )
346       {
347         return str << "LogControlImpl";
348       }
349
350       ///////////////////////////////////////////////////////////////////
351       //
352       // Access from logger::
353       //
354       ///////////////////////////////////////////////////////////////////
355
356       std::ostream & getStream( const char * group_r,
357                                 LogLevel     level_r,
358                                 const char * file_r,
359                                 const char * func_r,
360                                 const int    line_r )
361       {
362         return LogControlImpl::instance().getStream( group_r,
363                                                    level_r,
364                                                    file_r,
365                                                    func_r,
366                                                    line_r );
367       }
368
369       /** That's what Loglinebuf calls.  */
370       inline void putStream( const std::string & group_r, LogLevel level_r,
371                              const char * file_r, const char * func_r, int line_r,
372                              const std::string & buffer_r )
373       {
374         LogControlImpl::instance().putStream( group_r, level_r,
375                                             file_r, func_r, line_r,
376                                             buffer_r );
377       }
378
379       bool isExcessive()
380       { return LogControlImpl::instance().isExcessive(); }
381
382       /////////////////////////////////////////////////////////////////
383     } // namespace logger
384     ///////////////////////////////////////////////////////////////////
385
386     ///////////////////////////////////////////////////////////////////
387     //
388     //  CLASS NAME : LogControl
389     //  Forward to LogControlImpl singleton.
390     //
391     ///////////////////////////////////////////////////////////////////
392
393     using logger::LogControlImpl;
394
395     void LogControl::logfile( const Pathname & logfile_r )
396     { LogControlImpl::instance().logfile( logfile_r ); }
397
398     void LogControl::logfile( const Pathname & logfile_r, mode_t mode_r )
399     { LogControlImpl::instance().logfile( logfile_r, mode_r ); }
400
401     shared_ptr<LogControl::LineWriter> LogControl::getLineWriter() const
402     { return LogControlImpl::instance().getLineWriter(); }
403
404     void LogControl::setLineWriter( const shared_ptr<LineWriter> & writer_r )
405     { LogControlImpl::instance().setLineWriter( writer_r ); }
406
407     void LogControl::setLineFormater( const shared_ptr<LineFormater> & formater_r )
408     { LogControlImpl::instance().setLineFormater( formater_r ); }
409
410     void LogControl::logNothing()
411     { LogControlImpl::instance().setLineWriter( shared_ptr<LineWriter>() ); }
412
413     void LogControl::logToStdErr()
414     { LogControlImpl::instance().setLineWriter( shared_ptr<LineWriter>( new log::StderrLineWriter ) ); }
415
416     ///////////////////////////////////////////////////////////////////
417     //
418     // LogControl::TmpExcessive
419     //
420     ///////////////////////////////////////////////////////////////////
421     LogControl::TmpExcessive::TmpExcessive()
422     { LogControlImpl::instance().excessive( true ); }
423     LogControl::TmpExcessive::~TmpExcessive()
424     { LogControlImpl::instance().excessive( false );  }
425
426     /******************************************************************
427      **
428      ** FUNCTION NAME : operator<<
429      ** FUNCTION TYPE : std::ostream &
430     */
431     std::ostream & operator<<( std::ostream & str, const LogControl & obj )
432     {
433       return str << LogControlImpl::instance();
434     }
435
436     /////////////////////////////////////////////////////////////////
437   } // namespace base
438   ///////////////////////////////////////////////////////////////////
439   /////////////////////////////////////////////////////////////////
440 } // namespace zypp
441 ///////////////////////////////////////////////////////////////////