37 namespace LogOutputImp
39 static std::string s_info ;
40 static std::string s_warning ;
41 static std::string s_error ;
42 static std::string s_fatal ;
43 constexpr int stderr_fileno = 2 ;
45 constexpr std::size_t margin = 7U ;
46 constexpr std::size_t buffer_base_size = limits::log + 40U ;
47 std::array<char,buffer_base_size+margin> buffer_1 ;
48 std::array<char,8> buffer_2 ;
52 void reset() { clear() ; seekp(0) ; }
54 std::ostream & ostream1()
56 static G::omembuf buf( &buffer_1[0] , buffer_1.size() ) ;
61 std::ostream & ostream2()
64 static G::omembuf buf( &buffer_2[0] , buffer_2.size() ) ;
65 static ostream s( &buf ) ;
69 std::size_t tellp( std::ostream & s )
72 return static_cast<std::size_t
>( std::max( std::streampos(0) , s.tellp() ) ) ;
78 const std::string & path ) :
84 open( m_path ,
true ) ;
87 if( LogOutputImp::this_ ==
nullptr )
88 LogOutputImp::this_ = this ;
92 bool verbose_info_and_debug ,
const std::string & path ) :
96 .set_output_enabled(output_enabled_and_summary_info)
97 .set_summary_info(output_enabled_and_summary_info)
98 .set_verbose_info(verbose_info_and_debug)
99 .set_debug(verbose_info_and_debug) ;
102 open( m_path ,
true ) ;
105 if( LogOutputImp::this_ ==
nullptr )
106 LogOutputImp::this_ = this ;
121 if( LogOutputImp::this_ ==
this )
122 LogOutputImp::this_ = nullptr ;
123 if( !m_path.empty() && m_fd != LogOutputImp::stderr_fileno && m_fd >= 0 )
130 return LogOutputImp::this_ ;
138 p->m_context_fn = fn ;
139 p->m_context_fn_arg = fn_arg ;
146 return p ? p->m_context_fn_arg : nullptr ;
151 bool do_output = m_config.m_output_enabled ;
152 if( severity == Log::Severity::s_Debug )
153 do_output = m_config.m_output_enabled && m_config.m_debug ;
154 else if( severity == Log::Severity::s_InfoSummary )
155 do_output = m_config.m_output_enabled && m_config.m_summary_info ;
156 else if( severity == Log::Severity::s_InfoVerbose )
157 do_output = m_config.m_output_enabled && m_config.m_verbose_info ;
164 return instance()->start( severity ) ;
166 return LogOutputImp::ostream2() ;
172 instance()->output( ss , 0 ) ;
175void G::LogOutput::open( std::string path ,
bool do_throw )
179 m_fd = LogOutputImp::stderr_fileno ;
183 std::size_t pos = path.find(
"%d" ) ;
184 if( pos != std::string::npos )
185 path.replace( pos , 2U , std::string(&m_time_buffer[0],8U) ) ;
187 int fd =
File::open( path.c_str() , File::InOutAppend::Append ) ;
191 throw std::runtime_error(
"cannot open log file: " + path ) ;
195 if( m_fd >= 0 && m_fd != LogOutputImp::stderr_fileno )
206 return LogOutputImp::ostream2() ;
209 open( m_path ,
false ) ;
211 std::ostream & ss = LogOutputImp::ostream1() ;
213 if( m_exename.length() )
214 ss << m_exename <<
": " ;
215 if( m_config.m_with_timestamp )
217 if( m_config.m_with_level )
218 ss << levelString( severity ) ;
219 if( m_config.m_with_context && m_context_fn )
220 ss << (*m_context_fn)( m_context_fn_arg ) ;
222 m_start_pos = LogOutputImp::tellp( ss ) ;
223 m_severity = severity ;
230 if( m_depth ) m_depth-- ;
231 if( m_depth ) return ;
233 char * buffer = &LogOutputImp::buffer_1[0] ;
234 std::size_t n = LogOutputImp::tellp( ss ) ;
237 if( n >= LogOutputImp::buffer_base_size )
239 char * margin = buffer + LogOutputImp::buffer_base_size ;
244 n = LogOutputImp::buffer_base_size + 4U ;
249 if( m_config.m_strip )
251 char * end = buffer + n ;
252 char * text = buffer + m_start_pos ;
253 char * space = std::find( text , end ,
' ' ) ;
254 if( space != end && (space+1) != end )
257 p = std::copy_backward( buffer , text , space+1 ) ;
264 for(
char * pp = std::strchr(buffer,
'\033') ; pp ; pp = std::strchr(p+1,
'\033') )
269 osoutput( m_fd , m_severity , p , n ) ;
277 std::ostream & ss = LogOutputImp::ostream1() ;
278 ss <<
"assertion error: " << basename(file) <<
"(" << line <<
"): " << test_expression ;
279 char * p = &LogOutputImp::buffer_1[0] ;
280 std::size_t n = LogOutputImp::tellp( ss ) ;
281 instance()->osoutput( instance()->m_fd , Log::Severity::s_Assertion , p , n ) ;
285 std::cerr <<
"assertion error: " << basename(file) <<
"(" << line <<
"): " << test_expression << std::endl ;
294bool G::LogOutput::updateTime()
297 m_time_us = now.
us() ;
298 bool new_day = false ;
299 if( m_time_s == 0 || m_time_s != now.
s() || m_time_buffer.empty() )
302 m_time_buffer.resize( 17U ) ;
303 now.
local().
format( m_time_buffer ,
"%Y%m%d.%H%M%S." ) ;
304 m_time_buffer[16U] =
'\0' ;
305 new_day = 0 != std::memcmp( &m_date_buffer[0] , &m_time_buffer[0] , m_date_buffer.size() ) ;
306 std::memcpy( &m_date_buffer[0] , &m_time_buffer[0] , m_date_buffer.size() ) ;
311void G::LogOutput::appendTimeTo( std::ostream & ss )
315 <<
static_cast<char>(
'0' + ( ( m_time_us / 100000U ) % 10U ) )
316 <<
static_cast<char>(
'0' + ( ( m_time_us / 10000U ) % 10U ) )
317 <<
static_cast<char>(
'0' + ( ( m_time_us / 1000U ) % 10U ) )
321const char * G::LogOutput::basename(
const char * file )
noexcept
323 if( file ==
nullptr )
return "" ;
324 const char * p1 = std::strrchr( file ,
'/' ) ;
325 const char * p2 = std::strrchr( file ,
'\\' ) ;
326 return p1 > p2 ? (p1+1) : (p2?(p2+1):file) ;
329const char * G::LogOutput::levelString( Log::Severity s )
noexcept
331 namespace imp = LogOutputImp ;
332 if( s == Log::Severity::s_Debug )
return "debug: " ;
333 else if( s == Log::Severity::s_InfoSummary )
return imp::s_info.empty() ?
"info: " : imp::s_info.c_str() ;
334 else if( s == Log::Severity::s_InfoVerbose )
return imp::s_info.empty() ?
"info: " : imp::s_info.c_str() ;
335 else if( s == Log::Severity::s_Warning )
return imp::s_warning.empty() ?
"warning: " : imp::s_warning.c_str() ;
336 else if( s == Log::Severity::s_Error )
return imp::s_error.empty() ?
"error: " : imp::s_error.c_str() ;
337 else if( s == Log::Severity::s_Assertion )
return imp::s_fatal.empty() ?
"fatal: " : imp::s_fatal.c_str() ;
342 const std::string & error ,
const std::string & fatal )
344 namespace imp = LogOutputImp ;
346 imp::s_warning = warning ;
347 imp::s_error = error ;
348 imp::s_fatal = fatal ;
353G::LogOutput::Config::Config()
358 m_output_enabled = value ;
364 m_summary_info = value ;
370 m_verbose_info = value ;
382 m_with_level = value ;
388 m_with_timestamp = value ;
394 m_with_context = value ;
406 m_quiet_stderr = value ;
412 m_use_syslog = value ;
418 m_allow_bad_syslog = value ;
424 m_facility = facility ;
void format(std::vector< char > &out, const char *fmt) const
Puts the formatted date, including a terminating null character, into the given buffer.
static void open(std::ofstream &, const Path &)
Calls open() on the given output file stream.
static void close(int fd) noexcept
Calls ::close() or equivalent.
Controls and implements low-level logging output, as used by G::Log.
bool at(Log::Severity) const noexcept
Returns true if logging should occur for the given severity level.
static void translate(const std::string &info, const std::string &warning, const std::string &error, const std::string &fatal)
Sets the prefix string for the various log levels (including trailing punctuation).
Config config() const
Returns the current configuration.
static void output(std::ostream &)
Emits the current log line (see start()).
static void * contextarg() noexcept
Returns the functor argument as set by the last call to context().
LogOutput(const std::string &exename, const Config &config, const std::string &filename=std::string())
Constructor.
void configure(const Config &)
Updates the current configuration.
static std::ostream & start(Log::Severity, const char *file, int line)
Prepares the internal ostream for a new log line and returns a reference to it.
static void assertionFailure(const char *file, int line, const char *test_expression) noexcept
Reports an assertion failure.
static LogOutput * instance() noexcept
Returns a pointer to the controlling LogOutput object.
static void context(std::string(*fn)(void *)=nullptr, void *fn_arg=nullptr) noexcept
Sets a functor that is used to provide a context string for every log line, if configured.
static void assertionAbort() GDEF_NORETURN
Aborts the program when an assertion has failed.
Represents a unix-epoch time with microsecond resolution.
static SystemTime now()
Factory function for the current time.
std::time_t s() const noexcept
Returns the number of seconds since the start of the epoch.
unsigned int us() const
Returns the microsecond fraction.
BrokenDownTime local() const
Returns the locale-dependent local broken-down time.
An output streambuf that writes to a fixed-size char buffer.
An ostream using a G::omembuf streambuf.
A configuration structure for G::LogOutput.