E-MailRelay
glogoutput.h
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2024 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/// \file glogoutput.h
19///
20
21#ifndef G_LOG_OUTPUT_H
22#define G_LOG_OUTPUT_H
23
24#include "gdef.h"
25#include "glogstream.h"
26#include "gprocess.h"
27#include "gpath.h"
28#include "glimits.h"
29#include "gomembuf.h"
30#include "gexception.h"
31#include "gstringview.h"
32#include <string>
33#include <vector>
34#include <fstream>
35#include <ctime>
36#include <array>
37#include <utility>
38
39#ifndef G_LOG_THREAD_LOCAL
40#define G_LOG_THREAD_LOCAL
41#endif
42
43namespace G
44{
45 class LogOutput ;
46}
47
48//| \class G::LogOutput
49/// Controls and implements low-level logging output, as used by G::Log.
50///
51/// Applications should instantiate a LogOutput object in main() to
52/// enable and configure log output.
53///
54/// The implementation uses a file descriptor for osoutput() rather than
55/// a stream because windows file-sharing options are not accessible
56/// when building with mingw streams, and to avoid double buffering
57/// and mixed output from multiple threads.
58///
59/// \see G::Log
60///
62{
63public:
64 G_EXCEPTION( LogFileError , tx("cannot open log file") )
65 G_EXCEPTION( EventLogError , tx("cannot access the system event log") )
66
67 enum class Severity
68 {
69 Debug ,
70 InfoMoreVerbose ,
71 InfoVerbose ,
72 InfoSummary ,
73 Warning ,
74 Error ,
75 Assertion
76 } ;
77
78 enum class SyslogFacility
79 {
80 User ,
81 Daemon ,
82 Mail ,
83 Cron ,
84 Local0 ,
85 Local1 ,
86 Local2 ,
87 Local3 ,
88 Local4 ,
89 Local5 ,
90 Local6 ,
91 Local7
92 } ;
93
94 struct Config /// A configuration structure for G::LogOutput.
95 {
96 bool m_output_enabled {false} ;
97 bool m_summary_info {false} ;
98 bool m_verbose_info {false} ;
99 bool m_more_verbose_info {false} ;
100 bool m_debug {false} ;
101 bool m_with_level {false} ;
102 bool m_with_timestamp {false} ;
103 bool m_with_context {false} ;
104 bool m_strip {false} ; // strip first word
105 bool m_quiet_stderr {false} ;
106 bool m_use_syslog {false} ;
107 bool m_allow_bad_syslog {false} ;
108 bool m_stdout {false} ;
109 SyslogFacility m_facility {SyslogFacility::User} ;
110 Process::Umask::Mode m_umask {Process::Umask::Mode::NoChange} ;
111 Config() noexcept ;
112 Config( bool enabled , bool verbose ) noexcept ;
113 Config & set_output_enabled( bool value = true ) noexcept ;
114 Config & set_summary_info( bool value = true ) noexcept ;
115 Config & set_verbose_info( bool value = true ) noexcept ;
116 Config & set_more_verbose_info( bool value = true ) noexcept ;
117 Config & set_debug( bool value = true ) noexcept ;
118 Config & set_with_level( bool value = true ) noexcept ;
119 Config & set_with_timestamp( bool value = true ) noexcept ;
120 Config & set_with_context( bool value = true ) noexcept ;
121 Config & set_strip( bool value = true ) noexcept ;
122 Config & set_quiet_stderr( bool value = true ) noexcept ;
123 Config & set_use_syslog( bool value = true ) noexcept ;
124 Config & set_allow_bad_syslog( bool value = true ) noexcept ;
125 Config & set_facility( SyslogFacility ) noexcept ;
126 Config & set_umask( Process::Umask::Mode ) noexcept ;
127 Config & set_stdout( bool value = true ) noexcept ;
128 } ;
129
130 struct Instance /// A set of convenience functions for calling LogOutput methods on LogOutput::instance().
131 {
132 static bool at( LogOutput::Severity ) noexcept ;
133 static bool atVerbose() noexcept ;
134 static bool atDebug() noexcept ;
135 static void context( std::string_view (*fn)(void*) = nullptr , void * fn_arg = nullptr ) noexcept ;
136 static void * contextarg() noexcept ;
137 static LogStream start( LogOutput::Severity , const char * file , int line ) noexcept ;
138 static void output( LogStream & ) noexcept ;
139 static int fd() noexcept ;
140 static LogOutput::Config config() noexcept ;
141 Instance() = delete ;
142 } ;
143
144 LogOutput( const std::string & exename , const Config & config ,
145 const Path & logfile = {} ) ;
146 ///< Constructor. If there is no LogOutput object, or if
147 ///< 'config.output_enabled' is false, then there is no
148 ///< output at all except for assertions to stderr.
149 ///<
150 ///< The 'exename' value is emitted as an additional prefix
151 ///< on each log line if it is non-empty. See also
152 ///< G::Arg::prefix().
153 ///<
154 ///< The log-file path can contain substitution variables
155 ///< "%d" or "%h" for the current day and hour respectively.
156 ///< If no path is given then logging is sent to the standard
157 ///< error stream by default; stderr should be re-opened
158 ///< onto /dev/null if only syslog logging is required.
159 ///<
160 ///< More than one LogOutput object may be created but
161 ///< only the first one controls output (see instance()).
162
163 LogOutput( const std::string & exename , const Config & config , int fd ) ;
164 ///< Constructor overload taking a file descriptor.
165 ///< Typically used for non-main threads when instance()
166 ///< is thread-local.
167
168 explicit LogOutput( bool enabled , bool verbose = true , const G::Path & logfile = {} ) ;
169 ///< Constructor overload for test programs.
170
171 ~LogOutput() ;
172 ///< Destructor.
173
174 static LogOutput * instance() noexcept ;
175 ///< Returns a pointer to the controlling LogOutput object.
176 ///< Returns nullptr if none.
177 ///<
178 ///< Non-main threads can do logging safely if the (private)
179 ///< instance pointer is declared 'thread_local' at build-time.
180 ///< Thread functions can then instantiate their own LogOutput
181 ///< object on the stack, typically with the Config object
182 ///< and file descriptor passed in from the main thread.
183
184 Config config() const noexcept ;
185 ///< Returns the current configuration.
186
187 int fd() const noexcept ;
188 ///< Returns the output file descriptor.
189
190 void configure( const Config & ) ;
191 ///< Updates the current configuration.
192
193 bool at( Severity ) const noexcept ;
194 ///< Returns true if logging should occur for the given severity level.
195
196 void context( std::string_view (*fn)(void*) = nullptr , void * fn_arg = nullptr ) noexcept ;
197 ///< Sets a functor that is used to provide a context string for
198 ///< every log line, if configured. The functor should return
199 ///< the context string with trailing punctuation, typically
200 ///< colon and space.
201
202 void * contextarg() noexcept ;
203 ///< Returns the functor argument as set by the last call to context().
204
205 LogStream start( Severity , const char * file , int line ) noexcept ;
206 ///< Returns an ostream for a new log line. The caller should stream out
207 ///< the rest of the log line into the ostream and then call output().
208 ///< Calls to start() and output() should be in pairs.
209
210 void output( LogStream & ) noexcept ;
211 ///< Emits the current log line (see start()). Does nothing if there
212 ///< is no LogOutput instance.
213
214 static void assertion( LogOutput * , const char * file , int line , bool test , const char * test_string ) ;
215 ///< Performs an assertion check.
216
217 static void assertion( LogOutput * , const char * file , int line , void * test , const char * test_string ) ;
218 ///< Performs an assertion check. This overload, using a test on a
219 ///< pointer, is motivated by MSVC warnings.
220
221 static void assertionFailure( LogOutput * , const char * file , int line , const char * test_string ) noexcept ;
222 ///< Reports an assertion failure.
223
224 GDEF_NORETURN_LHS static void assertionAbort() GDEF_NORETURN_RHS ;
225 ///< Aborts the program when an assertion has failed.
226
227 static void register_( const Path & exe ) ;
228 ///< Registers the given executable as a source of logging.
229 ///< This is called from osinit(), but it might also need to be
230 ///< done as a program installation step with the necessary
231 ///< process permissions.
232
233public:
234 LogOutput( const LogOutput & ) = delete ;
235 LogOutput( LogOutput && ) = delete ;
236 LogOutput & operator=( const LogOutput & ) = delete ;
237 LogOutput & operator=( LogOutput && ) = delete ;
238
239private:
240 struct Private {} ;
241 LogOutput( Private , const std::string & , const Config & ) ;
242 void init() ;
243 void osinit() ;
244 void open( const Path & , bool ) ;
245 LogStream start( Severity ) ;
246 void output( LogStream & , int ) ;
247 void osoutput( int , Severity , char * , std::size_t ) ;
248 void oscleanup() const noexcept ;
249 bool updateTime() ;
250 bool updatePath( const Path & , Path & ) const ;
251 Path makePath( const Path & ) const ;
252 void appendTimeTo( LogStream & ) ;
253 static std::string_view levelString( Severity ) noexcept ;
254 static const char * basename( const char * ) noexcept ;
255
256private:
257 struct Stream : public std::ostream
258 {
259 explicit Stream( omembuf * buf ) : std::ostream(buf) {}
260 void reset() { clear() ; seekp(0) ; }
261 } ;
262
263private:
264 friend struct Instance ;
265 static G_LOG_THREAD_LOCAL LogOutput * m_instance ;
266 std::string m_exename ;
267 Config m_config ;
268 std::time_t m_time_s {0} ;
269 unsigned int m_time_us {0U} ;
270 std::array<char,17U> m_time_buffer {} ;
271 std::array<char,17U> m_time_change_buffer {} ;
272 static constexpr std::size_t m_rhs_margin = 7U ;
273 static constexpr std::size_t m_buffer_base_size = Limits<>::log + 40U ;
274 static constexpr std::size_t m_buffer_size = m_buffer_base_size + m_rhs_margin ;
275 std::vector<char> m_buffer ;
276 G::omembuf m_streambuf ; // omembuf over m_buffer
277 Stream m_stream ; // std::ostream over m_streambuf
278 HANDLE m_handle {0} ; // windows only
279 Path m_path ; // with %d etc
280 Path m_real_path ;
281 int m_fd {-1} ;
282 unsigned int m_depth {0U} ;
283 Severity m_severity {Severity::Debug} ;
284 std::size_t m_start_pos {0U} ;
285 std::string_view (*m_context_fn)(void *) {nullptr} ;
286 void * m_context_fn_arg {nullptr} ;
287} ;
288
289inline void G::LogOutput::assertion( LogOutput * instance , const char * file , int line , bool test , const char * test_string )
290{
291 if( !test )
292 {
293 assertionFailure( instance , file , line , test_string ) ;
295 }
296}
297
298inline void G::LogOutput::assertion( LogOutput * instance , const char * file , int line , void * test , const char * test_string )
299{
300 if( !test )
301 {
302 assertionFailure( instance , file , line , test_string ) ;
303 assertionAbort() ;
304 }
305}
306
307inline G::LogOutput::Config & G::LogOutput::Config::set_output_enabled( bool value ) noexcept { m_output_enabled = value ; return *this ; }
308inline G::LogOutput::Config & G::LogOutput::Config::set_summary_info( bool value ) noexcept { m_summary_info = value ; return *this ; }
309inline G::LogOutput::Config & G::LogOutput::Config::set_verbose_info( bool value ) noexcept { m_verbose_info = value ; return *this ; }
310inline G::LogOutput::Config & G::LogOutput::Config::set_more_verbose_info( bool value ) noexcept { m_more_verbose_info = value ; return *this ; }
311inline G::LogOutput::Config & G::LogOutput::Config::set_debug( bool value ) noexcept { m_debug = value ; return *this ; }
312inline G::LogOutput::Config & G::LogOutput::Config::set_with_level( bool value ) noexcept { m_with_level = value ; return *this ; }
313inline G::LogOutput::Config & G::LogOutput::Config::set_with_timestamp( bool value ) noexcept { m_with_timestamp = value ; return *this ; }
314inline G::LogOutput::Config & G::LogOutput::Config::set_with_context( bool value ) noexcept { m_with_context = value ; return *this ; }
315inline G::LogOutput::Config & G::LogOutput::Config::set_strip( bool value ) noexcept { m_strip = value ; return *this ; }
316inline G::LogOutput::Config & G::LogOutput::Config::set_quiet_stderr( bool value ) noexcept { m_quiet_stderr = value ; return *this ; }
317inline G::LogOutput::Config & G::LogOutput::Config::set_use_syslog( bool value ) noexcept { m_use_syslog = value ; return *this ; }
318inline G::LogOutput::Config & G::LogOutput::Config::set_allow_bad_syslog( bool value ) noexcept { m_allow_bad_syslog = value ; return *this ; }
319inline G::LogOutput::Config & G::LogOutput::Config::set_facility( SyslogFacility facility ) noexcept { m_facility = facility ; return *this ; }
320inline G::LogOutput::Config & G::LogOutput::Config::set_umask( Process::Umask::Mode umask ) noexcept { m_umask = umask ; return *this ; }
321inline G::LogOutput::Config & G::LogOutput::Config::set_stdout( bool value ) noexcept { m_stdout = value ; return *this ; }
322
323inline bool G::LogOutput::Instance::at( LogOutput::Severity s ) noexcept { return LogOutput::m_instance && LogOutput::m_instance->at( s ) ; }
324inline bool G::LogOutput::Instance::atVerbose() noexcept { return LogOutput::m_instance && LogOutput::m_instance->at( LogOutput::Severity::InfoVerbose ) ; }
325inline bool G::LogOutput::Instance::atDebug() noexcept { return LogOutput::m_instance && LogOutput::m_instance->at( LogOutput::Severity::Debug ) ; }
326inline void G::LogOutput::Instance::context( std::string_view (*fn)(void*) , void * fn_arg ) noexcept { if( LogOutput::m_instance ) LogOutput::m_instance->context( fn , fn_arg ) ; }
327inline void * G::LogOutput::Instance::contextarg() noexcept { if( LogOutput::m_instance ) return LogOutput::m_instance->contextarg() ; else return nullptr ; }
328inline G::LogStream G::LogOutput::Instance::start( LogOutput::Severity s , const char * file , int line ) noexcept { if( LogOutput::m_instance ) return LogOutput::m_instance->start( s , file , line ) ; else return LogStream( nullptr ) ; }
329inline void G::LogOutput::Instance::output( LogStream & stream ) noexcept { if( LogOutput::m_instance ) LogOutput::m_instance->output( stream ) ; }
330inline int G::LogOutput::Instance::fd() noexcept { return LogOutput::m_instance ? LogOutput::m_instance->fd() : -1 ; }
331inline G::LogOutput::Config G::LogOutput::Instance::config() noexcept { return LogOutput::m_instance ? LogOutput::m_instance->config() : LogOutput::Config() ; }
332
333#endif
A static interface for daemonising the calling process.
Definition: gdaemon.h:43
Controls and implements low-level logging output, as used by G::Log.
Definition: glogoutput.h:62
LogStream start(Severity, const char *file, int line) noexcept
Returns an ostream for a new log line.
Definition: glogoutput.cpp:194
bool at(Severity) const noexcept
Returns true if logging should occur for the given severity level.
Definition: glogoutput.cpp:180
static GDEF_NORETURN_LHS void assertionAbort() GDEF_NORETURN_RHS
Aborts the program when an assertion has failed.
Definition: glogoutput.cpp:363
LogOutput(const std::string &exename, const Config &config, const Path &logfile={})
Constructor.
Definition: glogoutput.cpp:86
static void assertionFailure(LogOutput *, const char *file, int line, const char *test_string) noexcept
Reports an assertion failure.
Definition: glogoutput.cpp:341
void context(std::string_view(*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.
Definition: glogoutput.cpp:162
static void register_(const Path &exe)
Registers the given executable as a source of logging.
Config config() const noexcept
Returns the current configuration.
Definition: glogoutput.cpp:121
void * contextarg() noexcept
Returns the functor argument as set by the last call to context().
Definition: glogoutput.cpp:173
void output(LogStream &) noexcept
Emits the current log line (see start()).
Definition: glogoutput.cpp:209
~LogOutput()
Destructor.
Definition: glogoutput.cpp:138
void configure(const Config &)
Updates the current configuration.
Definition: glogoutput.cpp:133
static LogOutput * instance() noexcept
Returns a pointer to the controlling LogOutput object.
Definition: glogoutput.cpp:157
int fd() const noexcept
Returns the output file descriptor.
Definition: glogoutput.cpp:127
static void assertion(LogOutput *, const char *file, int line, bool test, const char *test_string)
Performs an assertion check.
Definition: glogoutput.h:289
A Path object represents a file system path.
Definition: gpath.h:82
bool enabled() noexcept
Returns true if pop code is built in.
Low-level classes.
Definition: garg.h:36
constexpr const char * tx(const char *p) noexcept
A briefer alternative to G::gettext_noop().
Definition: ggettext.h:84
STL namespace.
A configuration structure for G::LogOutput.
Definition: glogoutput.h:95
A set of convenience functions for calling LogOutput methods on LogOutput::instance().
Definition: glogoutput.h:131
A non-throwing copyable wrapper for std::ostream, used by G::LogOutput and associated logging macros.
Definition: glogstream.h:38