A new abstract type is to provide a general interface for all kinds of logging devices, not only file-system as firstly attempted. With this in mind, a log just need to be opened, written-to and finally closed when done. I tried to reflect such a model as shown next.
The general abstract interface:
1 #ifndef FRAMEWORK_HXX
2 #define FRAMEWORK_HXX
3
4 namespace framework
5 {
6 namespace log
7 {
8
9 struct file
10 {
11 virtual ~file() {}
12
13 virtual bool open() = 0;
14 virtual bool close() = 0;
15
16 virtual bool write( const char *, ... ) const = 0;
17 };
18
19 }
20 }
21
22 #endif /* FRAMEWORK_HXX */
23
One partial-implementation by a sub-library or application:
1 /* ...
10 * 11 * Created on May 14, 2017, 8:56 PM 12 */ 13 14 #ifndef LOG_HXX 15 #define LOG_HXX 16 17 #include <sys/types.h> 18 #include <sys/stat.h> 19 #include <fcntl.h> 20 21 #include <unistd.h> 22 23 #include <ctime> 24 #include <cstdarg> 25 #include <cstring> 26 #include <cstdlib> 27 #include <cstdio> 28 29 #include <functional> 30 31 #include "framework.hxx" 32 33 namespace application 34 { 35 namespace log 36 { 37 38 namespace implementation 39 { 40 41 struct file : public framework::log::file 42 { 43 44 file() : fd { -1 } 45 { 46 } 47 48 ~file() 49 { 50 close(); 51 } 52 53 bool close() override 54 { 55 return fd >= 0 56 ? ::close( fd ) == 0 57 ? ( fd = -1, true ) 58 : false 59 : true; 60 } 61 62 bool write( const char * format, ... ) const override 63 { 64 ::ssize_t rv = -1; 65 66 if ( ! timestamp() ) 67 return false; 68 69 va_list ap; 70 va_start( ap, format ); 71 72 // MT-safe if setlocale(3C) not called 73 // Hence, possibly a serious limitation! 74 // More advanced C++ to the rescue! 75 char * line = 0; 76 ::vasprintf( &line, format, ap ); 77 rv = ::write( fd, line, ::strlen(line) ); 78 ::free( line ); 79 80 va_end( ap ); 81 82 return rv != -1; 83 } 84 85 private: 86 87 bool timestamp() const 88 { 89 ::time_t utc; 90 if ( ::time( &utc ) < 0 ) 91 return false; 92 93 ::tm local; 94 if ( ::localtime_r( &utc, &local ) == 0 ) 95 return false; 96 97 const int size = 32; 98 char ts[ size ]; 99 if ( ::strftime( ts, size, "[%F %T] ", &local ) == 0 ) 100 return false; 101 102 return ::write( fd, ts, ::strlen(ts) ) != -1; 103 } 104 105 protected: 106 107 const ::mode_t mode { S_IRUSR | S_IWUSR | S_IRGRP }; 108 const int flags { O_WRONLY | O_APPEND | O_CREAT }; 109 110 int fd; 111 }; 112 113 } 114 115 struct file : implementation::file 116 { 117 using get_fd_t = int ( const int /* flags */, const ::mode_t ); 118 using callback_open = std::function< get_fd_t >; 119 120 file( callback_open get_fd ) : get_fd( get_fd ) 121 { 122 } 123 124 bool open() override 125 { 126 return fd >= 0 ? true : ( fd = get_fd( flags, mode ) ) >= 0; 127 } 128 129 private: 130 131 callback_open get_fd; 132 }; 133 134 } 135 } 136 137 #endif /* LOG_HXX */ 138
An application could use or extend the above partial-library as follows:
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <fcntl.h>
4
5 #include <cstdlib>
6 #include <string>
7
8 #include "framework.hxx"
9 #include "log.hxx"
10
11 void use( framework::log::file & log, const char * message )
12 {
13 if ( ! log.open() )
14 exit( EXIT_FAILURE );
15
16 log.write( message );
17 }
18
19 namespace application
20 {
21 namespace log
22 {
23
24 int open_1( const int flags, const ::mode_t mode )
25 {
26 return ::open( "trace_1.log", flags, mode );
27 }
28
29 struct open_2
30 {
31 open_2( const std::string & file_name ) :
file_name { file_name }
{
}
32
33 int operator () ( const int flags, const ::mode_t mode ) const
34 {
35 return ::open( file_name.c_str(), flags, mode );
36 }
37
38 const std::string file_name;
39 };
40
41 struct log3 : public implementation::file
42 {
43 bool open() override
44 {
45 return fd >= 0
46 ? true
47 : ( fd = ::open( "trace_3.log", flags, mode ) ) >= 0;
48 }
49 };
50
51 }
52 }
53
54 int main()
55 {
56 namespace log = application::log;
57
58 log::open_2 open_2( "trace_2.log" );
59
60 log::file log1( log::open_1 );
61 log::file log2( open_2 );
62 log::log3 log3;
63
64 log::file log4( log::open_2( "trace_4.log" ) );
65
66 use( log1, "Test message 1!\n" );
67 use( log2, "Test message 2!\n" );
68 use( log3, "Test message 3!\n" );
69
70 use( log4, "Test message 4!\n" );
71
72 return EXIT_SUCCESS;
73 }
74
As seen above, now there's a general interface which usage is demonstrated in the use() global function and this addresses the previous version issue #1. The new call-back or derivation mechanisms for open() addresses the previous version issues #2 and #4. Both strategies are very powerful as global-functions, function-objects or derived-classes can be easily integrated into the framework / libraries adding great versatility without sacrificing the general abstract interface.
The pending issue #3 about write() is more delicate. The problem could be mitigated by making the function accept just std::string message to be printed, which could be previously formatted using standard stream formatting techniques. But this work-around adds a lot of verbosity to the program which programmers are not fond of. The versatility of printf()-like functions is tremendously appealing to programmers, but at the price of being error-prone to say the least. The best solution would be if the C++ STL officially supported up-to-date variadic printf()-like function templates that could approach the good behavior and flexibility of the legacy printf()-like functions, allied to the natural safety of improved type-checking of C++. But it seems that this will be left for third-parties or individual / isolated efforts.
The pending issue #5 would require extensive redesign and rewrite and could completely abolish pending issue #3. Nevertheless, as logging activity is timestamp-line-record-based, extra care would be necessary to be devised (if possible or viable at all) for providing line-record delimiters within series of chained put-to stream operators. This extra-care trade-off would favor not pursuing a solution to pending issue #5 in favor of #3. If I get some time off I plan to experiment with some coding artifacts for better assessing these issues on each of the strategies.