E-MailRelay

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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