Line data Source code
1 : /** \file logFileRaw.hpp
2 : * \brief Manage a raw log file.
3 : * \ingroup logger_files
4 : */
5 :
6 : #ifndef logger_logFileRaw_hpp
7 : #define logger_logFileRaw_hpp
8 :
9 : #include <iostream>
10 :
11 : #include <mx/ioutils/fileUtils.hpp>
12 : #include <mx/ioutils/stringUtils.hpp>
13 :
14 : #include <flatlogs/flatlogs.hpp>
15 :
16 : #include "../file/fileTimes.hpp"
17 :
18 : namespace MagAOX
19 : {
20 : namespace logger
21 : {
22 :
23 : /// A class to manage raw binary log files
24 : /** Manages a binary file containing MagAO-X logs.
25 : *
26 : * The log entries are written as a binary stream of a configurable
27 : * maximum size. If this size will be exceed by the next entry, then a new file is created.
28 : *
29 : * Filenames have a standard form of: `[path]/[name]/[name]_YYYYMMDDHHMMSSNNNNNNNNN.[ext]` where fields in [] are
30 : * configurable.
31 : *
32 : * The timestamp in the file name is from the first entry of the file.
33 : *
34 : */
35 : template <class verboseT = XWC_DEFAULT_VERBOSITY>
36 : class logFileRaw
37 : {
38 :
39 : protected:
40 : /** \name Configurable Parameters
41 : *@{
42 : */
43 : std::string m_logPath{ "." }; ///< The base path for the log files.
44 : std::string m_logName{ "xlog" }; ///< The base name for the log files.
45 : std::string m_logExt{ MAGAOX_default_logExt }; ///< The extension for the log files.
46 :
47 : size_t m_maxLogSize{ MAGAOX_default_max_logSize }; ///< The maximum file size in bytes. Default is 10 MB.
48 : ///@}
49 :
50 : /** \name Internal State
51 : *@{
52 : */
53 :
54 : FILE *m_fout{ 0 }; ///< The file pointer
55 :
56 : size_t m_currFileSize{ 0 }; ///< The current file size.
57 :
58 : ///@}
59 :
60 : public:
61 : /// Default constructor
62 : /** Currently does nothing.
63 : */
64 : logFileRaw();
65 :
66 : /// Destructor
67 : /** Closes the file if open
68 : */
69 : ~logFileRaw();
70 :
71 : /// Set the path.
72 : /**
73 : *
74 : * \returns 0 on success
75 : * \returns -1 on error
76 : */
77 : mx::error_t logPath( const std::string &newPath /**< [in] the new value of _path */ );
78 :
79 : /// Get the path.
80 : /**
81 : * \returns the current value of m_logPath.
82 : */
83 : std::string logPath();
84 :
85 : /// Set the log name
86 : /**
87 : *
88 : * \returns 0 on success
89 : * \returns -1 on error
90 : */
91 : mx::error_t logName( const std::string &newName /**< [in] the new value of m_logName */ );
92 :
93 : /// Get the name
94 : /**
95 : * \returns the current value of _name.
96 : */
97 : std::string logName();
98 :
99 : /// Set the log extension
100 : /**
101 : *
102 : * \returns 0 on success
103 : * \returns -1 on error
104 : */
105 : mx::error_t logExt( const std::string &newExt /**< [in] the new value of m_logExt */ );
106 :
107 : /// Get the log extension
108 : /**
109 : * \returns the current value of m_logExt.
110 : */
111 : std::string logExt();
112 :
113 : /// Set the maximum file size
114 : /**
115 : *
116 : * \returns 0 on success
117 : * \returns -1 on error
118 : */
119 : mx::error_t maxLogSize( size_t newMaxFileSize /**< [in] the new value of _maxLogSize */ );
120 :
121 : /// Get the maximum file size
122 : /**
123 : * \returns the current value of m_maxLogSize
124 : */
125 : size_t maxLogSize();
126 :
127 : /// Write a log entry to the file
128 : /** Checks if this write will exceed m_maxLogSize, and if so opens a new file.
129 : * The new file will have the timestamp of this log entry.
130 : *
131 : * \returns 0 on success
132 : * \returns -1 on error
133 : */
134 : mx::error_t writeLog( flatlogs::bufferPtrT &data /**< [in] the log entry to write to disk */ );
135 :
136 : /// Flush the stream
137 : /** Calls `fflush`. See issue #192
138 : *
139 : * \returns 0 on success
140 : * \returns -1 on error
141 : */
142 : mx::error_t flush();
143 :
144 : /// Close the file pointer
145 : /** Sets \ref m_fout to nullptr after calling fclose regardless of error.
146 : *
147 : * \returns 0 on success
148 : * \returns -1 on error
149 : */
150 : mx::error_t close();
151 :
152 : protected:
153 : /// Create a new file
154 : /** Closes the current file if open. Then creates a new file with a name of the form
155 : * [path]/[name]/YYYY_MM_DD/[name]_YYYYMMDDHHMMSSNNNNNNNNN.[ext]
156 : *
157 : *
158 : * \returns 0 on success
159 : * \returns -1 on error
160 : */
161 : mx::error_t createFile( flatlogs::timespecX &ts /**< [in] A MagAOX timespec, used to set the timestamp */ );
162 : };
163 :
164 : template <class verboseT>
165 2442 : logFileRaw<verboseT>::logFileRaw()
166 : {
167 407 : }
168 :
169 : template <class verboseT>
170 407 : logFileRaw<verboseT>::~logFileRaw()
171 : {
172 407 : close();
173 407 : }
174 :
175 : template <class verboseT>
176 34 : mx::error_t logFileRaw<verboseT>::logPath( const std::string &newPath )
177 : {
178 : try
179 : {
180 34 : m_logPath = newPath;
181 : }
182 0 : catch( const std::bad_alloc &e )
183 : {
184 0 : std::throw_with_nested( xwcException( "string assignment" ) );
185 : }
186 0 : catch( const std::exception &e )
187 : {
188 0 : return mx::error_report( mx::error_t::std_exception, std::string( "string assignment: " ) + e.what() );
189 : }
190 :
191 34 : return mx::error_t::noerror;
192 : }
193 :
194 : template <class verboseT>
195 16 : std::string logFileRaw<verboseT>::logPath()
196 : {
197 16 : return m_logPath;
198 : }
199 :
200 : template <class verboseT>
201 24 : mx::error_t logFileRaw<verboseT>::logName( const std::string &newName )
202 : {
203 : try
204 : {
205 24 : m_logName = newName;
206 : }
207 0 : catch( const std::bad_alloc &e )
208 : {
209 0 : std::throw_with_nested( xwcException( "string assignment" ) );
210 : }
211 0 : catch( const std::exception &e )
212 : {
213 0 : return mx::error_report( mx::error_t::std_exception, std::string( "string assignment: " ) + e.what() );
214 : }
215 :
216 24 : return mx::error_t::noerror;
217 : }
218 :
219 : template <class verboseT>
220 12 : std::string logFileRaw<verboseT>::logName()
221 : {
222 12 : return m_logName;
223 : }
224 :
225 : template <class verboseT>
226 6 : mx::error_t logFileRaw<verboseT>::logExt( const std::string &newExt )
227 : {
228 : try
229 : {
230 6 : m_logExt = newExt;
231 : }
232 0 : catch( const std::bad_alloc &e )
233 : {
234 0 : std::throw_with_nested( xwcException( "string assignment" ) );
235 : }
236 0 : catch( const std::exception &e )
237 : {
238 0 : return mx::error_report( mx::error_t::std_exception, std::string( "string assignment: " ) + e.what() );
239 : }
240 :
241 6 : return mx::error_t::noerror;
242 : }
243 :
244 : template <class verboseT>
245 13 : std::string logFileRaw<verboseT>::logExt()
246 : {
247 13 : return m_logExt;
248 : }
249 :
250 : template <class verboseT>
251 2 : mx::error_t logFileRaw<verboseT>::maxLogSize( size_t newMaxFileSize )
252 : {
253 : try
254 : {
255 2 : m_maxLogSize = newMaxFileSize;
256 : }
257 : catch( const std::bad_alloc &e )
258 : {
259 : std::throw_with_nested( xwcException( "string assignment" ) );
260 : }
261 : catch( const std::exception &e )
262 : {
263 : return mx::error_report( mx::error_t::std_exception, std::string( "string assignment: " ) + e.what() );
264 : }
265 :
266 2 : return mx::error_t::noerror;
267 : }
268 :
269 : template <class verboseT>
270 2 : size_t logFileRaw<verboseT>::maxLogSize()
271 : {
272 2 : return m_maxLogSize;
273 : }
274 :
275 : template <class verboseT>
276 1433 : mx::error_t logFileRaw<verboseT>::writeLog( flatlogs::bufferPtrT &data )
277 : {
278 1433 : size_t N = flatlogs::logHeader::totalSize( data );
279 :
280 : // Check if we need a new file
281 1433 : if( m_currFileSize + N > m_maxLogSize || m_fout == 0 )
282 : {
283 37 : flatlogs::timespecX ts = flatlogs::logHeader::timespec( data );
284 :
285 37 : mx_error_check( createFile( ts ) );
286 : }
287 :
288 1433 : size_t nwr = fwrite( data.get(), sizeof( char ), N, m_fout );
289 :
290 1433 : if( nwr != N * sizeof( char ) )
291 : {
292 0 : return mx::error_report<verboseT>( mx::errno2error_t( errno ), "Error from fwrite" );
293 : }
294 :
295 1433 : m_currFileSize += N;
296 :
297 1433 : return mx::error_t::noerror;
298 : }
299 :
300 : template <class verboseT>
301 68 : mx::error_t logFileRaw<verboseT>::flush()
302 : {
303 : ///\todo this probably should be fsync, with appropriate error handling (see fsyncgate) [issue #192]
304 :
305 68 : if( m_fout )
306 : {
307 66 : if( fflush( m_fout ) != 0 )
308 : {
309 0 : return mx::error_report<verboseT>( mx::errno2error_t( errno ), "Error from fflush" );
310 : }
311 : }
312 68 : return mx::error_t::noerror;
313 : }
314 :
315 : template <class verboseT>
316 452 : mx::error_t logFileRaw<verboseT>::close()
317 : {
318 452 : if( m_fout )
319 : {
320 41 : errno = 0;
321 :
322 41 : if( fclose( m_fout ) != 0 )
323 : {
324 0 : m_fout = nullptr;
325 :
326 0 : return mx::error_report<verboseT>( mx::errno2error_t( errno ), "Error from fflush" );
327 : }
328 :
329 41 : m_fout = nullptr;
330 : }
331 :
332 452 : return mx::error_t::noerror;
333 : }
334 :
335 : template <class verboseT>
336 44 : mx::error_t logFileRaw<verboseT>::createFile( flatlogs::timespecX &ts )
337 : {
338 44 : std::string fileName;
339 44 : std::string relPath;
340 :
341 : try
342 : {
343 44 : mx::error_t errc = file::fileTimeRelPath( fileName, relPath, m_logName, m_logExt, ts.time_s, ts.time_ns );
344 :
345 44 : if( !!errc )
346 : {
347 1 : return mx::error_report<verboseT>( errc );
348 : }
349 : }
350 0 : catch( ... )
351 : {
352 0 : std::throw_with_nested( mx::exception<verboseT>(mx::error_t::exception));
353 : }
354 :
355 43 : std::string fullPath = m_logPath + '/' + relPath + '/';
356 :
357 : // Create directory
358 43 : mx::error_t errc = mx::ioutils::createDirectories( fullPath );
359 :
360 43 : if( !!errc )
361 : {
362 2 : return mx::error_report<verboseT>( errc, "creating directory" );
363 : }
364 :
365 42 : fullPath += fileName;
366 :
367 42 : if( mx::ioutils::exists( fullPath, errc ) )
368 : {
369 1 : return mx::error_report<verboseT>( mx::error_t::eexist, "file " + fullPath + " exists" );
370 : }
371 :
372 41 : if( !!errc )
373 : {
374 0 : return mx::error_report<verboseT>( errc, "checking directory" );
375 : }
376 :
377 : // Close current file if it's open
378 41 : errc = close();
379 41 : if( errc != mx::error_t::noerror )
380 : {
381 0 : mx::error_report<verboseT>( errc, "Error from close, attempting to continue");
382 : }
383 :
384 41 : errno = 0;
385 :
386 41 : m_fout = fopen( fullPath.c_str(), "wb" );
387 :
388 41 : if( m_fout == 0 )
389 : {
390 0 : return mx::error_report<verboseT>( mx::errno2error_t( errno ), "Error from fopen on " + fullPath );
391 : }
392 :
393 : // Reset counters.
394 41 : m_currFileSize = 0;
395 :
396 41 : return mx::error_t::noerror;
397 44 : }
398 :
399 : extern template class logFileRaw<XWC_DEFAULT_VERBOSITY>;
400 :
401 : } // namespace logger
402 : } // namespace MagAOX
403 :
404 : #endif // logger_logFileRaw_hpp
|