42 namespace LogOutputImp
44 constexpr int stdout_fileno = 1 ;
45 constexpr int stderr_fileno = 2 ;
46 LogOutput * this_ = nullptr ;
47 constexpr std::size_t margin = 7U ;
48 constexpr std::size_t buffer_base_size = Limits<>::log + 40U ;
49 std::array<char,buffer_base_size+margin> buffer {} ;
50 struct ostream : std::ostream
53 void reset() { clear() ; seekp(0) ; }
55 LogStream & ostream1()
57 static G::omembuf buf( &buffer[0] , buffer.size() ) ;
58 static ostream s( &buf ) ;
59 static LogStream logstream( &s ) ;
63 LogStream & ostream2() noexcept
66 static LogStream logstream(
nullptr ) ;
69 std::size_t tellp( LogStream & logstream )
71 if( !logstream.m_ostream )
return 0U ;
72 logstream.m_ostream->clear() ;
73 return static_cast<std::size_t
>( std::max( std::streampos(0) , logstream.m_ostream->tellp() ) ) ;
77 static std::string s(
txt(
"info: ") ) ;
78 return {s.data(),s.size()} ;
82 static std::string s(
txt(
"warning: ") ) ;
83 return {s.data(),s.size()} ;
87 static std::string s(
txt(
"error: ") ) ;
88 return {s.data(),s.size()} ;
92 static std::string s(
txt(
"fatal: ") ) ;
93 return {s.data(),s.size()} ;
99 const std::string & path ) :
105 updatePath( m_path , m_real_path ) ;
106 open( m_real_path ,
true ) ;
108 if( LogOutputImp::this_ ==
nullptr )
109 LogOutputImp::this_ = this ;
114 bool verbose_info_and_debug ,
const std::string & path ) :
118 .set_output_enabled(output_enabled_and_summary_info)
119 .set_summary_info(output_enabled_and_summary_info)
120 .set_verbose_info(verbose_info_and_debug)
121 .set_more_verbose_info(verbose_info_and_debug)
122 .set_debug(verbose_info_and_debug) ;
125 updatePath( m_path , m_real_path ) ;
126 open( m_real_path ,
true ) ;
128 if( LogOutputImp::this_ ==
nullptr )
129 LogOutputImp::this_ = this ;
145 if( LogOutputImp::this_ ==
this )
147 LogOutputImp::this_ = nullptr ;
149 if( !m_path.empty() && m_fd >= 0 &&
150 m_fd != LogOutputImp::stderr_fileno &&
151 m_fd != LogOutputImp::stdout_fileno )
160 return LogOutputImp::this_ ;
168 p->m_context_fn = fn ;
169 p->m_context_fn_arg = fn_arg ;
176 return p ? p->m_context_fn_arg : nullptr ;
181 bool do_output = m_config.m_output_enabled ;
182 if( severity == Log::Severity::Debug )
183 do_output = m_config.m_output_enabled && m_config.m_debug ;
184 else if( severity == Log::Severity::InfoSummary )
185 do_output = m_config.m_output_enabled && m_config.m_summary_info ;
186 else if( severity == Log::Severity::InfoVerbose )
187 do_output = m_config.m_output_enabled && m_config.m_verbose_info ;
188 else if( severity == Log::Severity::InfoMoreVerbose )
189 do_output = m_config.m_output_enabled && m_config.m_more_verbose_info ;
198 return instance()->start( severity ) ;
200 return LogOutputImp::ostream2() ;
204 return LogOutputImp::ostream2() ;
213 instance()->output( s , 0 ) ;
220bool G::LogOutput::updatePath(
const std::string & path_in , std::string & path_out )
const
222 bool changed = false ;
223 if( !path_in.empty() )
225 std::string new_path_out = makePath( path_in ) ;
226 changed = new_path_out != path_out ;
227 path_out.swap( new_path_out ) ;
232std::string G::LogOutput::makePath(
const std::string & path_in )
const
235 std::string path_out = path_in ;
236 std::size_t pos = 0U ;
237 if( (pos=path_out.find(
"%d")) != std::string::npos )
239 std::string yyyymmdd( &m_time_buffer[0] , 8U ) ;
240 path_out.replace( pos , 2U , yyyymmdd ) ;
242 if( (pos=path_out.find(
"%h")) != std::string::npos )
244 path_out[pos] = m_time_buffer[9] ;
245 path_out[pos+1U] = m_time_buffer[10] ;
250void G::LogOutput::open(
const std::string & path ,
bool do_throw )
254 m_fd = m_config.m_stdout ? LogOutputImp::stdout_fileno : LogOutputImp::stderr_fileno ;
260 Process::Umask set_umask( m_config.m_umask ) ;
262 fd =
File::open( path.c_str() , File::InOutAppend::Append ) ;
263 if( fd < 0 && do_throw )
264 throw LogFileError( path ) ;
268 if( m_fd >= 0 && m_fd != LogOutputImp::stderr_fileno && m_fd != LogOutputImp::stdout_fileno )
279 return LogOutputImp::ostream2() ;
281 if( updateTime() && updatePath(m_path,m_real_path) )
282 open( m_real_path ,
false ) ;
284 LogStream & logstream = LogOutputImp::ostream1() ;
285 logstream << std::dec ;
286 if( m_exename.length() )
287 logstream << m_exename <<
": " ;
288 if( m_config.m_with_timestamp )
289 appendTimeTo( logstream ) ;
290 if( m_config.m_with_level )
291 logstream << levelString( severity ) ;
292 if( m_config.m_with_context && m_context_fn )
293 logstream << (*m_context_fn)( m_context_fn_arg ) ;
295 m_start_pos = LogOutputImp::tellp( logstream ) ;
296 m_severity = severity ;
303 if( m_depth ) m_depth-- ;
304 if( m_depth ) return ;
306 char * buffer = &LogOutputImp::buffer[0] ;
307 std::size_t n = LogOutputImp::tellp( logstream ) ;
310 if( n >= LogOutputImp::buffer_base_size )
312 char * margin = buffer + LogOutputImp::buffer_base_size ;
317 n = LogOutputImp::buffer_base_size + 4U ;
322 if( m_config.m_strip )
324 char * end = buffer + n ;
325 char * text = buffer + m_start_pos ;
326 char * space = std::find( text , end ,
' ' ) ;
327 if( space != end && (space+1) != end )
330 p = std::copy_backward( buffer , text , space+1 ) ;
337 for(
char * pp = std::strchr(buffer,
'\033') ; pp ; pp = std::strchr(p+1,
'\033') )
340 if( m_fd == LogOutputImp::stdout_fileno )
345 osoutput( m_fd , m_severity , p , n ) ;
353 LogStream & logstream = LogOutputImp::ostream1() ;
354 logstream <<
txt(
"assertion error: ") << basename(file) <<
"(" << line <<
"): " << test_expression ;
355 char * p = &LogOutputImp::buffer[0] ;
356 std::size_t n = LogOutputImp::tellp( logstream ) ;
357 instance()->osoutput( instance()->m_fd , Log::Severity::Assertion , p , n ) ;
361 std::cerr <<
txt(
"assertion error: ") << basename(file) <<
"(" << line <<
"): " << test_expression << std::endl ;
370bool G::LogOutput::updateTime()
373 m_time_us = now.
us() ;
374 bool new_hour = false ;
375 if( m_time_s == 0 || m_time_s != now.
s() || m_time_buffer[0] ==
'\0' )
378 m_time_buffer[0] =
'\0' ;
379 now.
local().
format( &m_time_buffer[0] , m_time_buffer.size() ,
"%Y%m%d.%H%M%S." ) ;
380 m_time_buffer[16U] =
'\0' ;
382 new_hour = 0 != std::memcmp( &m_time_change_buffer[0] , &m_time_buffer[0] , 11U ) ;
384 static_assert(
sizeof(m_time_change_buffer) ==
sizeof(m_time_buffer) ,
"" ) ;
385 std::memcpy( &m_time_change_buffer[0] , &m_time_buffer[0] , m_time_buffer.size() ) ;
390void G::LogOutput::appendTimeTo( LogStream & logstream )
394 <<
static_cast<char>(
'0' + ( ( m_time_us / 100000U ) % 10U ) )
395 <<
static_cast<char>(
'0' + ( ( m_time_us / 10000U ) % 10U ) )
396 <<
static_cast<char>(
'0' + ( ( m_time_us / 1000U ) % 10U ) )
400const char * G::LogOutput::basename(
const char * file )
noexcept
402 if( file ==
nullptr )
return "" ;
403 const char * p1 = std::strrchr( file ,
'/' ) ;
404 const char * p2 = std::strrchr( file ,
'\\' ) ;
405 return p1 > p2 ? (p1+1) : (p2?(p2+1):file) ;
408G::string_view G::LogOutput::levelString( Log::Severity s )
noexcept
410 namespace imp = LogOutputImp ;
411 if( s == Log::Severity::Debug )
return "debug: " ;
412 else if( s == Log::Severity::InfoSummary )
return imp::info() ;
413 else if( s == Log::Severity::InfoVerbose )
return imp::info() ;
414 else if( s == Log::Severity::InfoMoreVerbose )
return imp::info() ;
415 else if( s == Log::Severity::Warning )
return imp::warning() ;
416 else if( s == Log::Severity::Error )
return imp::error() ;
417 else if( s == Log::Severity::Assertion )
return imp::fatal() ;
423G::LogOutput::Config::Config()
428 m_output_enabled = value ;
434 m_summary_info = value ;
440 m_verbose_info = value ;
446 m_more_verbose_info = value ;
458 m_with_level = value ;
464 m_with_timestamp = value ;
470 m_with_context = value ;
482 m_quiet_stderr = value ;
488 m_use_syslog = value ;
494 m_allow_bad_syslog = value ;
500 m_facility = facility ;
bool format(char *out, std::size_t out_size, const char *fmt) const
Puts the formatted date, including a terminating null character, into the given output 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.
static GDEF_NORETURN_LHS void assertionAbort() GDEF_NORETURN_RHS
Aborts the program when an assertion has failed.
bool at(Log::Severity) const noexcept
Returns true if logging should occur for the given severity level.
static LogStream & start(Log::Severity, const char *file, int line) noexcept
Prepares the internal ostream for a new log line and returns a reference to it.
Config config() const
Returns the current configuration.
static void * contextarg() noexcept
Returns the functor argument as set by the last call to context().
static void output(LogStream &) noexcept
Emits the current log line (see start()).
LogOutput(const std::string &exename, const Config &config, const std::string &filename={})
Constructor.
void configure(const Config &)
Updates the current configuration.
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.
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.
A class like c++17's std::string_view.
const char * txt(const char *p)
A briefer alternative to G::gettext().
A configuration structure for G::LogOutput.
A non-throwing wrapper for std::ostream, used by G::Log.