00001 #ifndef FILE_LOGGER_IMPLEMENTATION_FILE
00002 #define FILE_LOGGER_IMPLEMENTATION_FILE
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include "console_logger.h"
00019 #include "file_logger.h"
00020
00021 #include <basis/chaos.h>
00022 #include <basis/function.h>
00023 #include <basis/istring.h>
00024 #include <basis/mutex.h>
00025 #include <basis/portable.h>
00026 #include <data_struct/static_memory_gremlin.h>
00027 #include <opsystem/byte_filer.h>
00028 #include <opsystem/critical_events.h>
00029 #include <opsystem/directory.h>
00030 #include <opsystem/filename.h>
00031 #include <opsystem/path_configuration.h>
00032 #include <opsystem/rendezvous.h>
00033 #include <textual/byte_format.h>
00034
00035 #include <stdio.h>
00036 #ifdef __WIN32__
00037 #include <io.h>
00038 #endif
00039
00040 const int REDUCE_FACTOR = 5;
00041
00042
00043
00044
00045 const int MAXIMUM_BUFFER_SIZE = 140000;
00046
00047
00048
00049 int static_chaos() { return chaos().inclusive(0, 1280004); }
00050
00052
00053 istring app_name_for_file_logger()
00054 {
00055 filename prog = path_configuration::application_name();
00056 return path_configuration::make_logfile_name(prog.rootname() + ".log");
00057 }
00058
00059 #ifndef OMIT_PROGRAM_WIDE_LOGGER
00060 log_base *set_PW_logger_for_file(const istring &file_name, int limit)
00061 {
00062 file_logger *new_pw = new file_logger(file_name, limit);
00063 return retask_program_wide_logger(new_pw);
00064 }
00065 #endif
00066
00068
00069 file_logger::file_logger()
00070 : _filename(new istring()),
00071 _file_limit(LOG_FILE_SIZE),
00072 _outfile(NIL),
00073 _flock(new mutex)
00074 {
00075 name("");
00076 }
00077
00078 file_logger::file_logger(const istring &initial_filename, int limit)
00079 : _filename(new istring()),
00080 _file_limit(limit),
00081 _outfile(NIL),
00082 _flock(new mutex)
00083 {
00084 name(initial_filename);
00085
00086
00087 }
00088
00089 file_logger::~file_logger()
00090 {
00091 close_file();
00092 WHACK(_filename);
00093 WHACK(_flock);
00094 }
00095
00096 bool file_logger::reopen()
00097 {
00098 auto_synchronizer l(*_flock);
00099 name(*_filename);
00100 return open_file();
00101 }
00102
00103 void file_logger::close_file()
00104 {
00105 auto_synchronizer l(*_flock);
00106 if (_outfile) _outfile->flush();
00107
00108 WHACK(_outfile);
00109 }
00110
00111 void file_logger::name(const istring &new_name)
00112 {
00113 auto_synchronizer l(*_flock);
00114 close_file();
00115 *_filename = new_name;
00116 }
00117
00118 int file_logger::size_reduction() const
00119 {
00120 auto_synchronizer l(*_flock);
00121 return int(_file_limit / REDUCE_FACTOR);
00122 }
00123
00124 bool file_logger::good() const
00125 {
00126 auto_synchronizer l(*_flock);
00127 if (!_outfile && !_file_limit) return true;
00128 if (!_outfile) return false;
00129 return _outfile->good();
00130 }
00131
00132 istring file_logger::name() const
00133 {
00134 auto_synchronizer l(*_flock);
00135 return *_filename;
00136 }
00137
00138 void file_logger::flush()
00139 {
00140 auto_synchronizer l(*_flock);
00141 if (!_outfile) open_file();
00142 if (_outfile) _outfile->flush();
00143 }
00144
00145 bool file_logger::open_file()
00146 {
00147 auto_synchronizer l(*_flock);
00148 close_file();
00149
00150 if (!_file_limit) {
00151
00152 return true;
00153 }
00154
00155
00156 if (!*_filename) {
00157
00158 _outfile = new byte_filer;
00159 return true;
00160 }
00161
00162
00163
00164
00165 for (int i = 0; i < _filename->length(); i++)
00166 if ((*_filename)[i] == '\\') (*_filename)[i] = '/';
00167
00168
00169 filename temp_file(*_filename);
00170 filename temp_dir(temp_file.dirname());
00171 if (!temp_dir.good() || !temp_dir.is_directory()) {
00172 directory::recursive_create(temp_dir);
00173 }
00174
00175
00176 _outfile = new byte_filer(*_filename, "a+b");
00177 return _outfile->good();
00178 }
00179
00180 outcome file_logger::log(const istring &to_show, int filter)
00181 {
00182 if (!_file_limit) return common::OKAY;
00183
00184 size_t current_size = 0;
00185 {
00186 auto_synchronizer l(*_flock);
00187 if (!member(filter)) return common::OKAY;
00188 if (!_outfile) open_file();
00189 if (!_outfile) return common::BAD_INPUT;
00190 if (!_outfile->good()) return common::BAD_INPUT;
00191
00192
00193
00194 if (to_show.length())
00195 _outfile->write((byte *)to_show.s(), to_show.length());
00196 if (eol() != NO_ENDING) {
00197 istring end = get_ending();
00198 _outfile->write((byte *)end.s(), end.length());
00199 }
00200 current_size = _outfile->tell();
00201 flush();
00202 }
00203
00204
00205 if (current_size > _file_limit) truncate(_file_limit - size_reduction());
00206 return common::OKAY;
00207 }
00208
00209 outcome file_logger::log_bytes(const byte_array &to_log, int filter)
00210 {
00211 if (!_file_limit) return common::OKAY;
00212
00213 size_t current_size = 0;
00214 {
00215 auto_synchronizer l(*_flock);
00216 if (!member(filter)) return common::OKAY;
00217 if (!_outfile) open_file();
00218 if (!_outfile) return common::BAD_INPUT;
00219 if (!_outfile->good()) return common::BAD_INPUT;
00220
00221
00222
00223 if (to_log.length())
00224 _outfile->write(to_log.observe(), to_log.length());
00225 current_size = _outfile->tell();
00226 flush();
00227 }
00228
00229
00230 if (current_size > _file_limit)
00231 truncate(_file_limit - size_reduction());
00232 return common::OKAY;
00233 }
00234
00235 outcome file_logger::format_bytes(const byte_array &to_log, int filter)
00236 {
00237 if (!_file_limit) return common::OKAY;
00238
00239 {
00240 auto_synchronizer l(*_flock);
00241 if (!member(filter)) return common::OKAY;
00242 if (!_outfile) open_file();
00243 if (!_outfile) return common::BAD_INPUT;
00244 if (!_outfile->good()) return common::BAD_INPUT;
00245
00246 }
00247
00248
00249 if (to_log.length()) {
00250 istring dumped_form;
00251 byte_format::text_dump(dumped_form, to_log);
00252 log(dumped_form);
00253 }
00254
00255
00256
00257
00258
00259
00260 return common::OKAY;
00261 }
00262
00263
00264
00265
00266 void file_logger::truncate(size_t new_size)
00267 {
00268 auto_synchronizer l(*_flock);
00269 if (!_outfile) open_file();
00270 if (!_outfile) return;
00271 if (!_outfile->good()) return;
00272
00273
00274 size_t current_size = 0;
00275
00276
00277
00278
00279
00280 rendezvous file_lock(*_filename + "_trunclock");
00281 if (!file_lock.healthy()) {
00282 critical_events::write_to_critical_events((istring("could not create "
00283 "lock for ") + *_filename).s());
00284 return;
00285 }
00286
00287
00288 bool got_lock = file_lock.lock(rendezvous::ENDLESS_WAIT);
00289 if (!got_lock) {
00290 critical_events::write_to_critical_events((istring("could not acquire "
00291 "lock for ") + *_filename).s());
00292 return;
00293 }
00294
00295
00296
00297 _outfile->seek(0, byte_filer::FROM_END);
00298 current_size = _outfile->tell();
00299 if (current_size <= new_size) {
00300
00301 file_lock.unlock();
00302 return;
00303 }
00304
00305 istring new_file(istring::SPRINTF, "%s.tmp.%d", name().s(),
00306 static_chaos());
00307
00308
00309 unlink(new_file.s());
00310
00311
00312 current_size = _outfile->tell();
00313
00314
00315 WHACK(_outfile);
00316 _outfile = new byte_filer(*_filename, "rb");
00317
00318
00319 byte_filer *hold_stream = new byte_filer(new_file, "w+b");
00320
00321 int start_of_keep = int(current_size - new_size);
00322
00323
00324 _outfile->seek(start_of_keep, byte_filer::FROM_START);
00325
00326 istring buff(' ', MAXIMUM_BUFFER_SIZE + 1);
00327
00328
00329
00330
00331
00332
00333 size_t bytes_written = 0;
00334
00335
00336
00337 while (!_outfile->eof() && (bytes_written <= new_size)) {
00338
00339 buff[0] = '\0';
00340 int bytes_read = _outfile->read((byte *)buff.s(), MAXIMUM_BUFFER_SIZE);
00341 if (!bytes_read)
00342 break;
00343 bytes_written += bytes_read;
00344
00345 if (!_outfile->eof() || bytes_read)
00346 hold_stream->write((byte *)buff.s(), bytes_read);
00347 }
00348 WHACK(_outfile);
00349 _outfile = new byte_filer(*_filename, "w+b");
00350
00351
00352 size_t hold_size = hold_stream->tell();
00353
00354 bytes_written = 0;
00355
00356
00357 hold_stream->seek(0, byte_filer::FROM_START);
00358 while (!hold_stream->eof() && (bytes_written <= hold_size) ) {
00359
00360 int bytes_read = hold_stream->read((byte *)buff.s(), MAXIMUM_BUFFER_SIZE);
00361 if (!bytes_read)
00362 break;
00363
00364 bytes_written += bytes_read;
00365 if (!hold_stream->eof() || bytes_read)
00366 _outfile->write((byte *)buff.s(), bytes_read);
00367 }
00368 WHACK(hold_stream);
00369 unlink(new_file.s());
00370 file_lock.unlock();
00371 name(*_filename);
00372 }
00373
00375
00376 combo_logger::combo_logger(const istring &filename, int limit,
00377 bool standard_error)
00378 : file_logger(filename, limit),
00379 _out(new console_logger(standard_error))
00380 {
00381 }
00382
00383 combo_logger::~combo_logger() { WHACK(_out); }
00384
00385 void combo_logger::add_filter(int new_filter)
00386 {
00387 file_logger::add_filter(new_filter);
00388 _out->add_filter(new_filter);
00389 }
00390
00391 void combo_logger::remove_filter(int old_filter)
00392 {
00393 file_logger::remove_filter(old_filter);
00394 _out->remove_filter(old_filter);
00395 }
00396
00397 void combo_logger::clear_filters()
00398 {
00399 file_logger::clear_filters();
00400 _out->clear_filters();
00401 }
00402
00403 void combo_logger::eol(line_ending to_set)
00404 {
00405 file_logger::eol(to_set);
00406 _out->eol(to_set);
00407 }
00408
00409 outcome combo_logger::log(const istring &info, int filter)
00410 {
00411 _out->log(info, filter);
00412 return file_logger::log(info, filter);
00413 }
00414
00415 #ifndef OMIT_PROGRAM_WIDE_LOGGER
00416 log_base *set_PW_logger_for_combo(const istring &file_name, int limit,
00417 bool standard_error)
00418 {
00419 combo_logger *new_pw = new combo_logger(file_name, limit, standard_error);
00420 return retask_program_wide_logger(new_pw);
00421 }
00422 #endif
00423
00424
00425 #endif //FILE_LOGGER_IMPLEMENTATION_FILE
00426