glogoutput.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2013 Graeme Walker <graeme_walker@users.sourceforge.net>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // glogoutput.cpp
19 //
20 
21 #include "gdef.h"
22 #include "glogoutput.h"
23 #include "glimits.h"
24 #include <sstream>
25 #include <string>
26 #include <ctime>
27 #include <cstdlib>
28 #include <fstream>
29 
30 // (note that the implementation here has to be reentrant and using only the standard runtime library)
31 
32 G::LogOutput::LogOutput( const std::string & prefix , bool enabled , bool summary_log ,
33  bool verbose_log , bool debug , bool level , bool timestamp , bool strip ,
34  bool use_syslog , const std::string & stderr_replacement , SyslogFacility syslog_facility ) :
35  m_prefix(prefix) ,
36  m_enabled(enabled) ,
37  m_summary_log(summary_log) ,
38  m_verbose_log(verbose_log) ,
39  m_debug(debug) ,
40  m_level(level) ,
41  m_strip(strip) ,
42  m_syslog(use_syslog) ,
43  m_std_err(err(stderr_replacement)) ,
44  m_facility(syslog_facility) ,
45  m_time(0) ,
46  m_timestamp(timestamp) ,
47  m_handle(0) ,
48  m_handle_set(false)
49 {
50  if( pthis() == NULL )
51  pthis() = this ;
52  init() ;
53 }
54 
55 G::LogOutput::LogOutput( bool enabled_and_summary , bool verbose_and_debug ,
56  const std::string & stderr_replacement ) :
57  m_enabled(enabled_and_summary) ,
58  m_summary_log(enabled_and_summary) ,
59  m_verbose_log(verbose_and_debug) ,
60  m_debug(verbose_and_debug) ,
61  m_level(false) ,
62  m_strip(false) ,
63  m_syslog(false) ,
64  m_std_err(err(stderr_replacement)) ,
65  m_facility(User) ,
66  m_time(0) ,
67  m_timestamp(false) ,
68  m_handle(0) ,
69  m_handle_set(false)
70 {
71  if( pthis() == NULL )
72  pthis() = this ;
73  init() ;
74 }
75 
76 std::ostream & G::LogOutput::err( const std::string & path_in )
77 {
78  if( !path_in.empty() )
79  {
80  std::string path = path_in ;
81  std::string::size_type pos = path.find("%d") ;
82  if( pos != std::string::npos )
83  path.replace( pos , 2U , dateString() ) ;
84 
85  static std::ofstream file( path.c_str() , std::ios_base::out | std::ios_base::app ) ;
86  // ignore errors
87  return file ;
88  }
89  else
90  {
91  return std::cerr ;
92  }
93 }
94 
96 {
97  if( pthis() == this )
98  pthis() = NULL ;
99  cleanup() ;
100 }
101 
102 G::LogOutput * & G::LogOutput::pthis()
103 {
104  static G::LogOutput * p = NULL ;
105  return p ;
106 }
107 
109 {
110  return pthis() ;
111 }
112 
113 bool G::LogOutput::enable( bool enabled )
114 {
115  bool was_enabled = m_enabled ;
116  m_enabled = enabled ;
117  return was_enabled ;
118 }
119 
120 void G::LogOutput::output( Log::Severity severity , const char * file , int line , const std::string & text )
121 {
122  if( instance() != NULL )
123  instance()->doOutput( severity , file , line , text ) ;
124 }
125 
126 void G::LogOutput::doOutput( Log::Severity severity , const char * /* file */ , int /* line */ , const std::string & text )
127 {
128  // decide what to do
129  bool do_output = m_enabled ;
130  if( severity == Log::s_Debug )
131  do_output = m_enabled && m_debug ;
132  else if( severity == Log::s_LogSummary )
133  do_output = m_enabled && m_summary_log ;
134  else if( severity == Log::s_LogVerbose )
135  do_output = m_enabled && m_verbose_log ;
136 
137  if( do_output )
138  {
139  // allocate a buffer
140  const size_type limit = static_cast<size_type>(limits::log) ;
141  std::string buffer ;
142  buffer.reserve( (text.length()>limit?limit:text.length()) + 40U ) ;
143 
144  // add the preamble to tbe buffer
145  std::string::size_type text_pos = 0U ;
146  if( m_prefix.length() )
147  {
148  buffer.append( m_prefix ) ;
149  buffer.append( ": " ) ;
150  }
151  if( m_timestamp )
152  buffer.append( timestampString() ) ;
153  if( m_level )
154  buffer.append( levelString(severity) ) ;
155  if( m_strip )
156  {
157  text_pos = text.find(' ') ;
158  if( text_pos == std::string::npos || (text_pos+1U) == text.length() )
159  text_pos = 0U ;
160  else
161  text_pos++ ;
162  }
163 
164  // add the text to the buffer, with a sanity limit
165  size_type text_len = text.length() - text_pos ;
166  bool limited = text_len > limit ;
167  text_len = text_len > limit ? limit : text_len ;
168  buffer.append( text , text_pos , text_len ) ;
169  if( limited )
170  buffer.append( " ..." ) ;
171 
172  // last ditch removal of ansi escape sequences
173  while( buffer.find('\033') != std::string::npos )
174  buffer[buffer.find('\033')] = '.' ;
175 
176  // do the actual output in an o/s-specific manner
177  rawOutput( m_std_err , severity , buffer ) ;
178  }
179 }
180 
182 {
183  // no-op
184 }
185 
186 std::string G::LogOutput::timestampString()
187 {
188  // use a data member buffer to optimise away calls to localtime() and strftime()
189  std::time_t now = std::time(NULL) ;
190  if( m_time == 0 || m_time != now )
191  {
192  m_time = now ;
193  static struct std::tm zero_broken_down_time ;
194  struct std::tm broken_down_time = zero_broken_down_time ;
195  getLocalTime( m_time , &broken_down_time ) ;
196  m_time_buffer[0] = '\0' ;
197  std::strftime( m_time_buffer, sizeof(m_time_buffer)-1U, "%Y" "%m" "%d." "%H" "%M" "%S: ", &broken_down_time ) ;
198  m_time_buffer[sizeof(m_time_buffer)-1U] = '\0' ;
199  }
200  return std::string(m_time_buffer) ;
201 }
202 
203 std::string G::LogOutput::dateString()
204 {
205  static struct std::tm zero_broken_down_time ;
206  struct std::tm broken_down_time = zero_broken_down_time ;
207  getLocalTime( std::time(NULL) , &broken_down_time ) ;
208  char buffer[10] = { 0 } ;
209  std::strftime( buffer , sizeof(buffer)-1U , "%Y" "%m" "%d" , &broken_down_time ) ;
210  buffer[sizeof(buffer)-1U] = '\0' ;
211  return std::string( buffer ) ;
212 }
213 
214 std::string G::LogOutput::fileAndLine( const char * file , int line )
215 {
216  if( file != NULL )
217  {
218  std::string basename( file ) ;
219  std::string::size_type slash_pos = basename.find_last_of( "/\\" ) ;
220  if( slash_pos != std::string::npos && (slash_pos+1U) < basename.length() )
221  basename.erase( 0U , slash_pos+1U ) ;
222  return basename + "(" + itoa(line) + "): " ;
223  }
224  else
225  {
226  return std::string() ;
227  }
228 }
229 
230 void G::LogOutput::assertion( const char * file , int line , bool test , const std::string & test_string )
231 {
232  if( !test )
233  {
234  if( instance() )
235  instance()->doAssertion( file , line , test_string ) ;
236  else
237  std::cerr << "assertion error: " << file << "(" << line << "): " << test_string << std::endl ;
238  halt() ;
239  }
240 }
241 
242 void G::LogOutput::doAssertion( const char * file , int line , const std::string & test_string )
243 {
244  // forward to derived classes -- overrides may safely re-enter this
245  // method since all code in this class is re-entrant
246  onAssert() ;
247 
248  rawOutput( m_std_err , Log::s_Assertion ,
249  std::string() + "Assertion error: " + fileAndLine(file,line) + test_string ) ;
250 }
251 
252 void G::LogOutput::halt()
253 {
254  std::abort() ;
255 }
256 
257 std::string G::LogOutput::levelString( Log::Severity s )
258 {
259  if( s == Log::s_Debug ) return "debug: " ;
260  else if( s == Log::s_LogSummary ) return "info: " ;
261  else if( s == Log::s_LogVerbose ) return "info: " ;
262  else if( s == Log::s_Warning ) return "warning: " ;
263  else if( s == Log::s_Error ) return "error: " ;
264  else if( s == Log::s_Assertion ) return "fatal: " ;
265  return std::string() ;
266 }
267 
268 std::string G::LogOutput::itoa( int n_ )
269 {
270  // diy implementation for speed and portability
271  if( n_ < 0 ) return std::string(1U,'0') ;
272  char buffer[20] = { '0' , '\0' } ;
273  unsigned int buffer_size = sizeof(buffer) ;
274  unsigned int n = static_cast<unsigned int>(n_) ;
275  n %= 1000000U ;
276  bool zero = n == 0U ;
277  char * p = buffer + buffer_size - 1U ;
278  for( *p-- = '\0' ; n > 0U ; --p , n /= 10U )
279  *p = static_cast<char>( '0' + (n % 10U) ) ;
280  return zero ? buffer : (p+1U) ;
281 }
282 
bool enable(bool enabled=true)
Enables or disables output. Returns the previous setting.
Definition: glogoutput.cpp:113
static LogOutput * instance()
Returns a pointer to the controlling LogOutput object.
Definition: glogoutput.cpp:108
LogOutput(const std::string &prefix, bool output, bool with_logging, bool with_verbose_logging, bool with_debug, bool with_level, bool with_timestamp, bool strip_context, bool use_syslog, const std::string &stderr_replacement=std::string(), SyslogFacility syslog_facility=User)
Constructor.
Definition: glogoutput.cpp:32
std::string::size_type size_type
A std::size_t type.
Definition: md5.h:43
static void assertion(const char *file, int line, bool test, const std::string &)
Makes an assertion check (regardless of any LogOutput object).
Definition: glogoutput.cpp:230
virtual void onAssert()
Called during an assertion failure.
Definition: glogoutput.cpp:181
virtual ~LogOutput()
Destructor.
Definition: glogoutput.cpp:95
static void output(G::Log::Severity, const char *file, int line, const std::string &)
Generates output if there is an existing LogOutput object which is enabled.
Definition: glogoutput.cpp:120
Severity
Definition: glog.h:53
Controls and implements low-level logging output, as used by the Log interface.
Definition: glogoutput.h:41