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