VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
glogoutput.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2017 Graeme Walker
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 "gdatetime.h"
23 #include "glogoutput.h"
24 #include "glimits.h"
25 #include <sstream>
26 #include <string>
27 #include <ctime>
28 #include <cstdlib>
29 #include <fstream>
30 
31 // (note that the implementation here has to be reentrant and using only the standard runtime library)
32 
33 G::LogOutput::LogOutput( const std::string & prefix , bool enabled , bool summary_log ,
34  bool verbose_log , bool debug , bool level , bool timestamp , bool strip ,
35  bool use_syslog , const std::string & stderr_replacement , SyslogFacility syslog_facility ) :
36  m_prefix(prefix) ,
37  m_enabled(enabled) ,
38  m_summary_log(summary_log) ,
39  m_verbose_log(verbose_log) ,
40  m_debug(debug) ,
41  m_level(level) ,
42  m_strip(strip) ,
43  m_syslog(use_syslog) ,
44  m_std_err(err(stderr_replacement)) ,
45  m_facility(syslog_facility) ,
46  m_time(0) ,
47  m_timestamp(timestamp) ,
48  m_handle(0) ,
49  m_handle_set(false)
50 {
51  if( pthis() == nullptr )
52  pthis() = this ;
53  init() ;
54 }
55 
56 G::LogOutput::LogOutput( bool enabled_and_summary , bool verbose_and_debug ,
57  const std::string & stderr_replacement ) :
58  m_enabled(enabled_and_summary) ,
59  m_summary_log(enabled_and_summary) ,
60  m_verbose_log(verbose_and_debug) ,
61  m_debug(verbose_and_debug) ,
62  m_level(false) ,
63  m_strip(false) ,
64  m_syslog(false) ,
65  m_std_err(err(stderr_replacement)) ,
66  m_facility(User) ,
67  m_time(0) ,
68  m_timestamp(false) ,
69  m_handle(0) ,
70  m_handle_set(false)
71 {
72  if( pthis() == nullptr )
73  pthis() = this ;
74  init() ;
75 }
76 
77 std::ostream & G::LogOutput::err( const std::string & path_in )
78 {
79  if( !path_in.empty() )
80  {
81  std::string path = path_in ;
82  std::string::size_type pos = path.find("%d") ;
83  if( pos != std::string::npos )
84  path.replace( pos , 2U , dateString() ) ;
85 
86  static std::ofstream file( path.c_str() , std::ios_base::out | std::ios_base::app ) ;
87  // ignore errors
88  return file ;
89  }
90  else
91  {
92  return std::cerr ;
93  }
94 }
95 
97 {
98  if( pthis() == this )
99  pthis() = nullptr ;
100  cleanup() ;
101 }
102 
103 G::LogOutput * & G::LogOutput::pthis()
104 {
105  static G::LogOutput * p = nullptr ;
106  return p ;
107 }
108 
110 {
111  return pthis() ;
112 }
113 
114 bool G::LogOutput::enable( bool enabled )
115 {
116  bool was_enabled = m_enabled ;
117  m_enabled = enabled ;
118  return was_enabled ;
119 }
120 
121 void G::LogOutput::verbose( bool verbose_log )
122 {
123  m_verbose_log = verbose_log ;
124 }
125 
126 void G::LogOutput::output( Log::Severity severity , const char * file , int line , const std::string & text )
127 {
128  if( instance() != nullptr )
129  instance()->doOutput( severity , file , line , text ) ;
130 }
131 
132 bool G::LogOutput::at( Log::Severity severity ) const
133 {
134  bool do_output = m_enabled ;
135  if( severity == Log::s_Debug )
136  do_output = m_enabled && m_debug ;
137  else if( severity == Log::s_LogSummary )
138  do_output = m_enabled && m_summary_log ;
139  else if( severity == Log::s_LogVerbose )
140  do_output = m_enabled && m_verbose_log ;
141  return do_output ;
142 }
143 
144 void G::LogOutput::doOutput( Log::Severity severity , const char * /*file*/ , int /*line*/ , const std::string & text )
145 {
146  bool do_output = at( severity ) ;
147  if( do_output )
148  {
149  // allocate a buffer
150  const size_type limit = static_cast<size_type>(limits::log) ;
151  std::string buffer ;
152  buffer.reserve( (text.length()>limit?limit:text.length()) + 40U ) ;
153 
154  // add the preamble to the buffer
155  std::string::size_type text_pos = 0U ;
156  if( m_prefix.length() )
157  {
158  buffer.append( m_prefix ) ;
159  buffer.append( ": " ) ;
160  }
161  if( m_timestamp )
162  appendTimestampStringTo( buffer ) ;
163  if( m_level )
164  buffer.append( levelString(severity) ) ;
165 
166  // strip the first word from the text - expected to be the method name
167  if( m_strip )
168  {
169  text_pos = text.find(' ') ;
170  if( text_pos == std::string::npos || (text_pos+1U) == text.length() )
171  text_pos = 0U ;
172  else
173  text_pos++ ;
174  }
175 
176  // add the text to the buffer, with a sanity limit
177  size_type text_len = text.length() - text_pos ;
178  bool limited = text_len > limit ;
179  text_len = text_len > limit ? limit : text_len ;
180  buffer.append( text , text_pos , text_len ) ;
181  if( limited )
182  buffer.append( " ..." ) ;
183 
184  // last ditch removal of ansi escape sequences
185  while( buffer.find('\033') != std::string::npos )
186  buffer[buffer.find('\033')] = '.' ;
187 
188  // do the actual output in an o/s-specific manner
189  rawOutput( m_std_err , severity , buffer ) ;
190  }
191 }
192 
194 {
195  // no-op
196 }
197 
198 void G::LogOutput::appendTimestampStringTo( std::string & result )
199 {
200  // use a cache to optimise away calls to localtime() and strftime()
202  if( m_time == 0 || m_time != now.s )
203  {
204  m_time = now.s ;
205  static struct std::tm zero_broken_down_time ;
206  struct std::tm broken_down_time = zero_broken_down_time ;
207  getLocalTime( m_time , &broken_down_time ) ;
208  m_time_buffer.reserve( 30U ) ;
209  m_time_buffer.assign( 17U , '\0' ) ;
210  std::strftime( &m_time_buffer[0] , m_time_buffer.size() , "%Y" "%m" "%d." "%H" "%M" "%S.", &broken_down_time ) ;
211  m_time_buffer.pop_back() ;
212  }
213 
214  result.append( &m_time_buffer[0] , m_time_buffer.size() ) ;
215  result.append( 1U , '0' + ( ( now.us / 100000 ) % 10 ) ) ;
216  result.append( 1U , '0' + ( ( now.us / 10000 ) % 10 ) ) ;
217  result.append( 1U , '0' + ( ( now.us / 1000 ) % 10 ) ) ;
218  result.append( ": " ) ;
219 }
220 
221 std::string G::LogOutput::dateString()
222 {
223  // (prefer to not use G::Date here to avoid reentrancy)
224  static struct std::tm zero_broken_down_time ;
225  struct std::tm broken_down_time = zero_broken_down_time ;
226  getLocalTime( std::time(nullptr) , &broken_down_time ) ;
227  char buffer[10] = { 0 } ;
228  std::strftime( buffer , sizeof(buffer)-1U , "%Y" "%m" "%d" , &broken_down_time ) ;
229  buffer[sizeof(buffer)-1U] = '\0' ;
230  return std::string( buffer ) ;
231 }
232 
233 std::string G::LogOutput::fileAndLine( const char * file , int line )
234 {
235  if( file != nullptr )
236  {
237  std::string basename( file ) ;
238  std::string::size_type slash_pos = basename.find_last_of( "/\\" ) ;
239  if( slash_pos != std::string::npos && (slash_pos+1U) < basename.length() )
240  basename.erase( 0U , slash_pos+1U ) ;
241  return basename + "(" + itoa(line) + "): " ;
242  }
243  else
244  {
245  return std::string() ;
246  }
247 }
248 
249 void G::LogOutput::assertion( const char * file , int line , bool test , const char * test_string )
250 {
251  if( !test )
252  {
253  if( instance() )
254  instance()->doAssertion( file , line , test_string ) ;
255  else
256  std::cerr << "assertion error: " << file << "(" << line << "): " << test_string << std::endl ;
257  halt() ;
258  }
259 }
260 
261 void G::LogOutput::doAssertion( const char * file , int line , const std::string & test_string )
262 {
263  // forward to derived classes -- overrides may safely re-enter this
264  // method since all code in this class is re-entrant
265  onAssert() ;
266 
267  rawOutput( m_std_err , Log::s_Assertion ,
268  std::string() + "Assertion error: " + fileAndLine(file,line) + test_string ) ;
269 }
270 
271 void G::LogOutput::halt()
272 {
273  std::abort() ;
274 }
275 
276 const char * G::LogOutput::levelString( Log::Severity s )
277 {
278  if( s == Log::s_Debug ) return "debug: " ;
279  else if( s == Log::s_LogSummary ) return "info: " ;
280  else if( s == Log::s_LogVerbose ) return "info: " ;
281  else if( s == Log::s_Warning ) return "warning: " ;
282  else if( s == Log::s_Error ) return "error: " ;
283  else if( s == Log::s_Assertion ) return "fatal: " ;
284  return "" ;
285 }
286 
287 std::string G::LogOutput::itoa( int n_ )
288 {
289  // diy implementation for speed and portability
290  if( n_ < 0 ) return std::string(1U,'0') ;
291  char buffer[20] = { '0' , '\0' } ;
292  unsigned int buffer_size = sizeof(buffer) ;
293  unsigned int n = static_cast<unsigned int>(n_) ;
294  n %= 1000000U ;
295  bool zero = n == 0U ;
296  char * p = buffer + buffer_size - 1U ;
297  for( *p-- = '\0' ; n > 0U ; --p , n /= 10U )
298  *p = static_cast<char>( '0' + (n % 10U) ) ;
299  return zero ? buffer : (p+1U) ;
300 }
301 
303 {
304  return m_syslog ;
305 }
306 /// \file glogoutput.cpp
A subsecond-resolution timestamp based on a time_t.
Definition: gdatetime.h:39
bool syslog() const
Returns true if syslog output is enabled.
Definition: glogoutput.cpp:302
bool enable(bool enabled=true)
Enables or disables output. Returns the previous setting.
Definition: glogoutput.cpp:114
static bool at(Severity)
Returns true if G::LogOutput::output() would log at the given level.
Definition: glog.cpp:43
static LogOutput * instance()
Returns a pointer to the controlling LogOutput object.
Definition: glogoutput.cpp:109
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:33
std::string::size_type size_type
A std::size_t type.
Definition: md5.h:43
virtual void onAssert()
Called during an assertion failure.
Definition: glogoutput.cpp:193
static EpochTime now()
Returns the current epoch time.
virtual ~LogOutput()
Destructor.
Definition: glogoutput.cpp:96
static void assertion(const char *file, int line, bool test, const char *)
Makes an assertion check (regardless of any LogOutput object).
Definition: glogoutput.cpp:249
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:126
bool at(G::Log::Severity) const
Returns true if output() generates output at the given severity level.
Definition: glogoutput.cpp:132
Controls and implements low-level logging output, as used by the Log interface.
Definition: glogoutput.h:41
void verbose(bool verbose_log=true)
Enables or disables verbose logging.
Definition: glogoutput.cpp:121