00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 #include "mbl_log.h"
00013 #include <vcl_cstddef.h>
00014 #include <vcl_fstream.h>
00015 #include <vcl_memory.h>
00016 #include <vcl_algorithm.h>
00017 #include <vcl_cassert.h>
00018 #include <vcl_iostream.h>
00019 #include <vcl_sstream.h>
00020 #include <vcl_utility.h>
00021 #include <vul/vul_string.h>
00022 #include <mbl/mbl_read_props.h>
00023 #include <mbl/mbl_exception.h>
00024
00025 vcl_ostream& operator<<(vcl_ostream&os, mbl_logger::levels level)
00026 {
00027 switch (level)
00028 {
00029 case mbl_logger::NONE:
00030 os << "NONE";
00031 break;
00032 case mbl_logger::EMERG:
00033 os << "EMERG";
00034 break;
00035 case mbl_logger::ALERT:
00036 os << "ALERT";
00037 break;
00038 case mbl_logger::CRIT:
00039 os << "CRIT";
00040 break;
00041 case mbl_logger::ERR:
00042 os << "ERR";
00043 break;
00044 case mbl_logger::WARN:
00045 os << "WARN";
00046 break;
00047 case mbl_logger::NOTICE:
00048 os << "NOTICE";
00049 break;
00050 case mbl_logger::INFO:
00051 os << "INFO";
00052 break;
00053 case mbl_logger::DEBUG:
00054 os << "DEBUG";
00055 break;
00056 case mbl_logger::ALL:
00057 os << "ALL";
00058 break;
00059 default:
00060 os << "LOG" << level;
00061 break;
00062 }
00063 return os;
00064 }
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076 int mbl_log_streambuf::sync()
00077 {
00078 #ifndef MBL_LOG_DISABLE_ALL_LOGGING
00079
00080 int n = static_cast<int>(pptr() - pbase());
00081
00082 if (n)
00083 logger_->output_->append(pbase(), n);
00084 logger_->output_->terminate_flush();
00085
00086 pbump(-n);
00087 #endif
00088 return 0;
00089 }
00090
00091 int mbl_log_streambuf::overflow(int ch)
00092 {
00093 #ifndef MBL_LOG_DISABLE_ALL_LOGGING
00094 int n = static_cast<int>(pptr() - pbase());
00095
00096 if (n)
00097 logger_->output_->append(pbase(), n);
00098 pbump(-n);
00099
00100 if (ch == EOF)
00101 return 0;
00102
00103 char cbuf = (char)ch;
00104 logger_->output_->append(&cbuf, 1);
00105 return ch;
00106 #else
00107 return EOF;
00108 #endif
00109 }
00110
00111 vcl_streamsize mbl_log_streambuf::xsputn( const char *ptr, vcl_streamsize nchar)
00112 {
00113 #ifndef MBL_LOG_DISABLE_ALL_LOGGING
00114
00115
00116 int n = static_cast<int>(pptr() - pbase());
00117 if (n)
00118 logger_->output_->append(pbase(), n);
00119 pbump(-n);
00120
00121 logger_->output_->append(ptr, nchar);
00122 return nchar;
00123 #else
00124 return 0;
00125 #endif
00126 }
00127
00128 #ifndef MBL_LOG_DISABLE_ALL_LOGGING
00129
00130
00131 mbl_logger::mbl_logger():
00132 level_(NOTICE),
00133 output_(new mbl_log_output_stream(vcl_cerr, "")),
00134 streambuf_(this),
00135 logstream_(&streambuf_),
00136 mt_logstream_(&logstream_)
00137 {
00138
00139
00140
00141 root().all_loggers_.insert(this);
00142 }
00143
00144 mbl_logger::~mbl_logger()
00145 {
00146 root().all_loggers_.erase(this);
00147 delete output_;
00148 }
00149
00150 mbl_log_output_stream::mbl_log_output_stream(vcl_ostream& real_stream, const char *id):
00151 real_stream_(&real_stream), id_(id), has_started_(false)
00152 {}
00153
00154
00155 void mbl_log_output_stream::start()
00156 {
00157
00158 if (has_started_)
00159 (*real_stream_) << "LOG_MESSAGE_TERMINATED_EARLY\n";
00160
00161
00162 real_stream_->flush();
00163
00164 has_started_=true;
00165 }
00166
00167
00168
00169 void mbl_log_output_stream::start_with_manual_termination(int level, const char *srcfile, int srcline)
00170 {
00171 ignore_flush_=true;
00172 start();
00173 (*real_stream_) << static_cast<mbl_logger::levels>(level) << ": " << id_ << ' ';
00174 }
00175
00176
00177
00178 void mbl_log_output_stream::start_with_flush_termination(int level, const char *srcfile, int srcline)
00179 {
00180 ignore_flush_=false;
00181 start();
00182 (*real_stream_) << static_cast<mbl_logger::levels>(level) << ": " << id_ << ' ';
00183 }
00184
00185 void mbl_log_output_stream::append(const char * contents, vcl_streamsize n_chars)
00186 {
00187
00188 if (!has_started_)
00189 {
00190 (*real_stream_) << "UNKNOWN_START_LOG: " << id_ << ' ';
00191 has_started_ = true;
00192 ignore_flush_ = true;
00193 }
00194
00195 real_stream_->rdbuf()->sputn(contents, n_chars);
00196 }
00197
00198
00199 void mbl_log_output_stream::terminate_manual()
00200 {
00201 real_stream_->flush();
00202 has_started_=false;
00203 }
00204
00205
00206 void mbl_log_output_stream::terminate_flush()
00207 {
00208 if (!ignore_flush_)
00209 {
00210 real_stream_->flush();
00211 has_started_=false;
00212 }
00213 }
00214
00215 mbl_log_output_file::mbl_log_output_file(const vcl_string &filename, const char *id):
00216 file_(filename.c_str(), vcl_ios_app), id_(id), has_started_(false)
00217 {}
00218
00219
00220 void mbl_log_output_file::start()
00221 {
00222
00223 if (has_started_)
00224 file_ << "LOG_MESSAGE_TERMINATED_EARLY\n";
00225
00226
00227 file_.flush();
00228
00229 has_started_=true;
00230 }
00231
00232
00233
00234 void mbl_log_output_file::start_with_manual_termination(int level, const char *srcfile, int srcline)
00235 {
00236 ignore_flush_=true;
00237 start();
00238 file_ << static_cast<mbl_logger::levels>(level) << ": " << id_ << ' ';
00239 }
00240
00241
00242
00243 void mbl_log_output_file::start_with_flush_termination(int level, const char *srcfile, int srcline)
00244 {
00245 ignore_flush_=false;
00246 start();
00247 file_ << static_cast<mbl_logger::levels>(level) << ": " << id_ << ' ';
00248 }
00249
00250
00251 void mbl_log_output_file::append(const char * contents, vcl_streamsize n_chars)
00252 {
00253
00254 if (!has_started_)
00255 {
00256 file_ << "UNKNOWN_START_LOG: " << id_ << ' ';
00257 has_started_=true;
00258 ignore_flush_=false;
00259 }
00260
00261 file_.rdbuf()->sputn(contents, n_chars);
00262 }
00263
00264
00265 void mbl_log_output_file::terminate_manual()
00266 {
00267 file_.flush();
00268 has_started_=false;
00269 }
00270
00271
00272 void mbl_log_output_file::terminate_flush()
00273 {
00274 if (!ignore_flush_)
00275 {
00276 file_.flush();
00277 has_started_=false;
00278 }
00279 }
00280
00281
00282 mbl_logger::mbl_logger(const char *id):
00283 output_(0),
00284 streambuf_(this),
00285 logstream_(&streambuf_),
00286 mt_logstream_(&logstream_)
00287 {
00288 const mbl_log_categories::cat_spec &cat =
00289 mbl_logger::root().categories().get(id);
00290
00291 level_ = cat.level;
00292
00293 if (cat.output == mbl_log_categories::cat_spec::NAMED_STREAM)
00294 {
00295 output_ = new mbl_log_output_stream(*cat.stream, id);
00296
00297 }
00298 else if (cat.output == mbl_log_categories::cat_spec::FILE_OUT)
00299 {
00300 output_ = new mbl_log_output_file(cat.name, id);
00301
00302 }
00303
00304 root().all_loggers_.insert(this);
00305 }
00306
00307 void mbl_logger::reinitialise()
00308 {
00309 const char *id = output_->id();
00310 const mbl_log_categories::cat_spec &cat =
00311 mbl_logger::root().categories().get(id);
00312
00313 level_ = cat.level;
00314
00315 if (cat.output == mbl_log_categories::cat_spec::NAMED_STREAM)
00316 {
00317 delete output_;
00318 output_ = new mbl_log_output_stream(vcl_cout, id);
00319
00320 }
00321 else if (cat.output == mbl_log_categories::cat_spec::FILE_OUT)
00322 {
00323 delete output_;
00324 output_ = new mbl_log_output_file(cat.name, id);
00325
00326 }
00327 }
00328
00329 void mbl_logger::set(int level, mbl_log_output_base* output)
00330 {
00331 level_ = level;
00332 delete output_;
00333 output_ = output;
00334
00335 }
00336
00337 vcl_ostream &mbl_logger::log(int level, const char * srcfile, int srcline)
00338 {
00339 if (level_ < level)
00340 return root().null_stream_;
00341 output_->start_with_flush_termination(level, srcfile, srcline);
00342 return logstream_;
00343 }
00344
00345 void mbl_logger::mtstart(int level, const char * srcfile, int srcline)
00346 {
00347 if (level_ < level)
00348 {
00349 mt_logstream_ = &root().null_stream_;
00350 return;
00351 }
00352 mt_logstream_ = &logstream_;
00353 output_->start_with_manual_termination(level, srcfile, srcline);
00354 }
00355
00356 void mbl_logger::mtstop()
00357 {
00358 logstream_.flush();
00359 output_->terminate_manual();
00360 }
00361 #endif
00362
00363
00364 mbl_logger_root &mbl_logger::root()
00365 {
00366 static vcl_auto_ptr<mbl_logger_root> root_;
00367
00368 if (!root_.get())
00369 root_ = vcl_auto_ptr<mbl_logger_root>(new mbl_logger_root());
00370 return *root_;
00371 }
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381 void mbl_logger_root::load_log_config_file(
00382 const vcl_map<vcl_string, vcl_ostream *> &stream_names)
00383 {
00384 #ifndef MBL_LOG_DISABLE_ALL_LOGGING
00385
00386
00387 vcl_ifstream config_file("mbl_log.properties");
00388 if (!config_file.is_open())
00389 config_file.open("~/mbl_log.properties");
00390 if (!config_file.is_open())
00391 config_file.open("~/.mbl_log.properties");
00392 if (!config_file.is_open())
00393 {
00394 vcl_string home1("${HOME}/mbl_log.properties");
00395 vcl_string home2("${HOME}/.mbl_log.properties");
00396 vcl_string home3("${HOMESHARE}/mbl_log.properties");
00397 vcl_string home4("${HOMEDRIVE}${HOMEDIR}/mbl_log.properties");
00398 vcl_string home5("${HOMEDRIVE}${HOMEPATH}/mbl_log.properties");
00399 vcl_string home6("${USERPROFILE}/mbl_log.properties");
00400 if (vul_string_expand_var(home1))
00401 config_file.open(home1.c_str());
00402 if (!config_file.is_open() && vul_string_expand_var(home2))
00403 config_file.open(home2.c_str());
00404 if (!config_file.is_open() && vul_string_expand_var(home3))
00405 config_file.open(home3.c_str());
00406 if (!config_file.is_open() && vul_string_expand_var(home4))
00407 config_file.open(home4.c_str());
00408 if (!config_file.is_open() && vul_string_expand_var(home5))
00409 config_file.open(home5.c_str());
00410 if (!config_file.is_open() && vul_string_expand_var(home6))
00411 config_file.open(home6.c_str());
00412 }
00413 if (!config_file.is_open())
00414 config_file.open("C:\\mbl_log.properties");
00415
00416 if (!config_file.is_open())
00417 {
00418 vcl_cerr << "WARNING: No mbl_log.properties file found.\n";
00419 return;
00420 }
00421
00422 config_file.clear();
00423 load_log_config(config_file, stream_names);
00424 #endif
00425 }
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435 void mbl_logger_root::load_log_config(vcl_istream& is,
00436 const vcl_map<vcl_string, vcl_ostream *> &stream_names)
00437 {
00438 #ifndef MBL_LOG_DISABLE_ALL_LOGGING
00439 categories_.config(is, stream_names);
00440 update_all_loggers();
00441 #endif
00442 }
00443
00444
00445 void mbl_logger_root::update_all_loggers()
00446 {
00447 #ifndef MBL_LOG_DISABLE_ALL_LOGGING
00448 for (vcl_set<mbl_logger *>::iterator it=all_loggers_.begin(),
00449 end=all_loggers_.end(); it!=end; ++it)
00450 (*it)->reinitialise();
00451 #endif
00452 }
00453
00454
00455 mbl_log_categories::mbl_log_categories()
00456 {
00457 cat_spec default_spec;
00458 default_spec.level = mbl_logger::NOTICE;
00459 default_spec.output = cat_spec::NAMED_STREAM;
00460 default_spec.name = "vcl_cerr";
00461 default_spec.stream = &vcl_cerr;
00462
00463 cat_list_[""] = default_spec;
00464 }
00465
00466 typedef vcl_map<vcl_string, vcl_ostream*> stream_names_t;
00467
00468
00469 inline mbl_log_categories::cat_spec parse_cat_spec(const vcl_string &str,
00470 const stream_names_t& stream_names)
00471 {
00472 mbl_log_categories::cat_spec spec;
00473 vcl_istringstream ss(str);
00474 mbl_read_props_type props = mbl_read_props_ws(ss);
00475
00476 vcl_string s = props.get_required_property("level");
00477 if (s == "NONE")
00478 spec.level = mbl_logger::NONE;
00479 else if (s == "EMERG")
00480 spec.level = mbl_logger::EMERG;
00481 else if (s == "ALERT")
00482 spec.level = mbl_logger::ALERT;
00483 else if (s == "CRIT")
00484 spec.level = mbl_logger::CRIT;
00485 else if (s == "ERR")
00486 spec.level = mbl_logger::ERR;
00487 else if (s == "WARN")
00488 spec.level = mbl_logger::WARN;
00489 else if (s == "NOTICE")
00490 spec.level = mbl_logger::NOTICE;
00491 else if (s == "INFO")
00492 spec.level = mbl_logger::INFO;
00493 else if (s == "DEBUG")
00494 spec.level = mbl_logger::DEBUG;
00495 else if (s == "ALL")
00496 spec.level = mbl_logger::ALL;
00497 else
00498 {
00499 mbl_exception_warning(
00500 mbl_exception_parse_error(
00501 vcl_string("mbl_log_categories.cxx:parse_cat_spec: unknown level: ") + s) );
00502
00503 spec.level = mbl_logger::NOTICE;
00504 }
00505
00506 if (props.find("file_output") != props.end())
00507 {
00508 spec.output = mbl_log_categories::cat_spec::FILE_OUT;
00509 spec.name = props["file_output"];
00510 props.erase("file_output");
00511 }
00512 else if (props.find("stream_output") != props.end())
00513 {
00514 spec.name = "";
00515 vcl_string s = props["stream_output"];
00516 spec.output = mbl_log_categories::cat_spec::NAMED_STREAM;
00517 spec.name = s;
00518 stream_names_t::const_iterator it = stream_names.find(s);
00519
00520 if (s == "cout" || s == "vcl_cout" || s == "std::cout")
00521 spec.stream = &vcl_cout;
00522 else if (s == "cerr" || s == "vcl_cerr" || s == "std::cerr")
00523 spec.stream = &vcl_cerr;
00524 else if (it != stream_names.end())
00525 spec.stream = it->second;
00526 else
00527 {
00528 mbl_exception_warning(
00529 mbl_exception_parse_error(
00530 vcl_string("mbl_log.cxx:parse_cat_spec: unknown stream output name: ")
00531 + props["stream_output"]) );
00532
00533 spec.stream = &vcl_cerr;
00534 spec.name = "vcl_cerr";
00535 }
00536 props.erase("stream_output");
00537 }
00538 else
00539 {
00540 spec.output = mbl_log_categories::cat_spec::NAMED_STREAM;
00541 spec.stream = &vcl_cerr;
00542 spec.name = "vcl_cerr";
00543 }
00544
00545 mbl_read_props_look_for_unused_props("mbl_log.cxx::parse_cat_spec", props);
00546
00547 return spec;
00548 }
00549
00550
00551
00552 void mbl_log_categories::config(vcl_istream&s, const stream_names_t& stream_names)
00553 {
00554 mbl_read_props_type props = mbl_read_props_ws(s);
00555
00556
00557 mbl_read_props_type::iterator it1=props.find("root");
00558 if (it1 == props.end())
00559 it1 = props.find("ROOT");
00560 if (it1 != props.end())
00561 {
00562 cat_spec spec = parse_cat_spec(it1->second, stream_names);
00563 cat_list_[""] = spec;
00564 props.erase(it1);
00565 }
00566
00567 for (mbl_read_props_type::const_iterator it2=props.begin(), end = props.end();
00568 it2 != end; ++it2)
00569 {
00570 cat_spec spec = parse_cat_spec(it2->second, stream_names);
00571 cat_list_[it2->first] = spec;
00572 }
00573 }
00574
00575
00576
00577 void mbl_log_categories::clear()
00578 {
00579 cat_list_.clear();
00580 cat_spec default_spec;
00581 default_spec.level = mbl_logger::NOTICE;
00582 default_spec.name = "cerr";
00583 default_spec.stream = &vcl_cerr;
00584 default_spec.output = cat_spec::NAMED_STREAM;
00585 cat_list_[""] = default_spec;
00586 }
00587
00588 struct mbl_log_prefix_comp
00589 {
00590 vcl_string s2;
00591 mbl_log_prefix_comp(const vcl_string& s): s2(s) {}
00592
00593 bool operator() (const vcl_pair<vcl_string, mbl_log_categories::cat_spec>& s1)
00594 {
00595
00596
00597
00598 if (s1.first.size() == s2.size())
00599 return s1.first == s2;
00600 else if (s1.first.size() > s2.size())
00601 return false;
00602 else if (s1.first.empty())
00603 return true;
00604 else
00605 return s1.first == s2.substr(0,s1.first.size()) && s2[s1.first.size()] == '.';
00606 }
00607 };
00608
00609
00610 const mbl_log_categories::cat_spec&
00611 mbl_log_categories::get(const vcl_string& category) const
00612 {
00613 typedef vcl_map<vcl_string, cat_spec>::const_reverse_iterator iter;
00614
00615 iter it = vcl_find_if(cat_list_.rbegin(), cat_list_.rend(),
00616 mbl_log_prefix_comp(category));
00617
00618 assert(it != cat_list_.rend());
00619
00620
00621 return it->second;
00622 }
00623
00624
00625 vcl_ostream& operator<<(vcl_ostream&os, const mbl_log_categories::cat_spec& spec)
00626 {
00627 os << "{ level: " << static_cast<mbl_logger::levels>(spec.level);
00628 switch (spec.output)
00629 {
00630 case mbl_log_categories::cat_spec::FILE_OUT:
00631 os << " file_output: " << spec.name;
00632 break;
00633 case mbl_log_categories::cat_spec::NAMED_STREAM:
00634 os << " stream_output: " << spec.name;
00635 break;
00636 default:
00637 assert(!"This should not happen: invalid spec.output");
00638 break;
00639 }
00640 os << " }";
00641 return os;
00642 }
00643
00644 void mbl_log_categories::print(vcl_ostream& os) const
00645 {
00646 typedef vcl_map<vcl_string, cat_spec>::const_iterator iter;
00647 assert(!cat_list_.empty());
00648
00649 iter it = cat_list_.begin(), end = cat_list_.end();
00650 assert(it->first.empty());
00651
00652 os << "root:\n " << it->second << '\n';
00653
00654 ++it;
00655 for (; it!=end; ++it)
00656 os << it->first << ":\n " << it->second << '\n';
00657
00658 os.flush();
00659 };