00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #ifndef __PION_LOGCODEC_HEADER__
00021 #define __PION_LOGCODEC_HEADER__
00022
00023 #include <vector>
00024 #include <boost/bind.hpp>
00025 #include <boost/shared_ptr.hpp>
00026 #include <boost/scoped_array.hpp>
00027 #include <boost/lexical_cast.hpp>
00028 #include <pion/PionConfig.hpp>
00029 #include <pion/PionException.hpp>
00030 #include <pion/PionHashMap.hpp>
00031 #include <pion/PionDateTime.hpp>
00032 #include <pion/PionAlgorithms.hpp>
00033 #include <pion/platform/Codec.hpp>
00034 #include <pion/platform/Vocabulary.hpp>
00035
00036 #ifdef PION_WIN32
00037 #define OSEOL "\r\n"
00038 #else
00039 #define OSEOL "\n"
00040 #endif
00041
00042 namespace pion {
00043 namespace plugins {
00044
00045
00049 class LogCodec :
00050 public pion::platform::Codec
00051 {
00052 public:
00053
00055 class EmptyFieldException : public PionException {
00056 public:
00057 EmptyFieldException(const std::string& codec_id)
00058 : PionException("LogCodec configuration includes an empty field name: ", codec_id) {}
00059 };
00060
00062 class EmptyTermException : public PionException {
00063 public:
00064 EmptyTermException(const std::string& codec_id)
00065 : PionException("LogCodec configuration is missing a term identifier: ", codec_id) {}
00066 };
00067
00069 class UnknownTermException : public PionException {
00070 public:
00071 UnknownTermException(const std::string& term_id)
00072 : PionException("LogCodec configuration maps field to an unknown term: ", term_id) {}
00073 };
00074
00076 class BadFormatException : public PionException {
00077 public:
00078 BadFormatException(const std::string& term_id)
00079 : PionException("LogCodec format contains an unknown term: ", term_id) {}
00080 };
00081
00082
00084 LogCodec(void)
00085 : pion::platform::Codec(), m_read_buf(new char[READ_BUFFER_SIZE+1]),
00086 m_read_end(m_read_buf.get() + READ_BUFFER_SIZE),
00087 m_flush_after_write(false), m_handle_elf_headers(false), m_wrote_elf_headers(false),
00088 m_time_offset(0),
00089 m_event_split(EVENT_SPLIT_SET), m_event_join(EVENT_JOIN_STRING), m_comment_chars(COMMENT_CHAR_SET),
00090 m_field_split(FIELD_SPLIT_SET), m_field_join(FIELD_JOIN_STRING), m_consume_delims(true)
00091 {}
00092
00094 virtual ~LogCodec() {}
00095
00097 virtual const std::string& getContentType(void) const { return CONTENT_TYPE; }
00098
00104 virtual pion::platform::CodecPtr clone(void) const;
00105
00112 virtual void write(std::ostream& out, const pion::platform::Event& e);
00113
00119 virtual void finish(std::ostream& out) {};
00120
00128 virtual bool read(std::istream& in, pion::platform::Event& e);
00129
00137 virtual void setConfig(const pion::platform::Vocabulary& v, const xmlNodePtr config_ptr);
00138
00145 virtual void updateVocabulary(const pion::platform::Vocabulary& v);
00146
00148 inline void reset(void) {
00149 m_field_map.clear();
00150 m_format.clear();
00151 m_wrote_elf_headers = false;
00152 }
00153
00154
00155 private:
00156
00158 struct LogField {
00160 LogField(const std::string& field, const pion::platform::Vocabulary::Term& term, char delim_start,
00161 char delim_end, bool opt_delims, bool urlencode, char escape_char, const std::string& empty_val,
00162 bool do_time_offset, const PionDateTime::time_duration_type& time_offset)
00163 : log_field(field), log_term(term), log_delim_start(delim_start), log_delim_end(delim_end),
00164 log_opt_delims(opt_delims), log_urlencode(urlencode), log_escape_char(escape_char),
00165 log_empty_val(empty_val), log_do_time_offset(do_time_offset), log_time_offset(time_offset)
00166 {}
00167
00169 LogField(const LogField& f)
00170 : log_field(f.log_field), log_term(f.log_term), log_delim_start(f.log_delim_start),
00171 log_delim_end(f.log_delim_end), log_opt_delims(f.log_opt_delims),
00172 log_urlencode(f.log_urlencode), log_escape_char(f.log_escape_char),
00173 log_empty_val(f.log_empty_val), log_do_time_offset(f.log_do_time_offset), log_time_offset(f.log_time_offset)
00174 {}
00175
00177 inline LogField& operator=(const LogField& f) {
00178 log_field = f.log_field;
00179 log_term = f.log_term;
00180 log_delim_start = f.log_delim_start;
00181 log_delim_end = f.log_delim_end;
00182 log_opt_delims = f.log_opt_delims;
00183 log_urlencode = f.log_urlencode;
00184 log_escape_char = f.log_escape_char;
00185 log_empty_val = f.log_empty_val;
00186 log_do_time_offset = f.log_do_time_offset;
00187 log_time_offset = f.log_time_offset;
00188 return *this;
00189 }
00190
00192 inline void writeEmptyValue(std::ostream& out) const {
00193 if (log_delim_start != '\0')
00194 out << log_delim_start;
00195 out << log_empty_val;
00196 if (log_delim_end != '\0')
00197 out << log_delim_end;
00198 }
00199
00206 inline void write(std::ostream& out, const pion::platform::Event::ParameterValue& value);
00207
00214 inline void read(const char *buf, pion::platform::Event& e);
00215
00217 std::string log_field;
00219 pion::platform::Vocabulary::Term log_term;
00221 PionTimeFacet log_time_facet;
00223 char log_delim_start;
00225 char log_delim_end;
00227 bool log_opt_delims;
00229 bool log_urlencode;
00231 char log_escape_char;
00233 std::string log_empty_val;
00235 bool log_do_time_offset;
00237 PionDateTime::time_duration_type log_time_offset;
00238 };
00239
00241 typedef boost::shared_ptr<LogField> LogFieldPtr;
00242
00244 typedef PION_HASH_MAP<std::string,
00245 LogFieldPtr, PION_HASH_STRING> FieldMap;
00246
00248 typedef std::vector<LogFieldPtr> CurrentFormat;
00249
00251 typedef std::istream::traits_type traits_type;
00252
00254 typedef std::basic_streambuf<std::istream::char_type,
00255 std::istream::traits_type> streambuf_type;
00256
00258 typedef std::istream::int_type int_type;
00259
00260
00273 inline void mapFieldToTerm(const std::string& field, const pion::platform::Vocabulary::Term& term,
00274 char delim_start, char delim_end, bool opt_delims,
00275 bool urlencode, char escape_char, const std::string& empty_val,
00276 bool do_time_offset, const PionDateTime::time_duration_type& time_offset);
00277
00283 inline char * cstyle(char *cstring);
00284
00290 inline void changeELFFormat(char *fmt);
00291
00297 inline void writeELFHeaders(std::ostream& out) const;
00298
00307 inline int_type consumeVoidsAndComments(streambuf_type *buf_ptr);
00308
00309
00311 static const std::string CONTENT_TYPE;
00312
00314 static const std::string FLUSH_ELEMENT_NAME;
00315
00317 static const std::string HEADERS_ELEMENT_NAME;
00318
00320 static const std::string TIME_OFFSET_ELEMENT_NAME;
00321
00323 static const std::string FIELD_ELEMENT_NAME;
00324
00326 static const std::string TERM_ATTRIBUTE_NAME;
00327
00329 static const std::string START_ATTRIBUTE_NAME;
00330
00332 static const std::string END_ATTRIBUTE_NAME;
00333
00335 static const std::string OPTIONAL_ATTRIBUTE_NAME;
00336
00338 static const std::string URLENCODE_ATTRIBUTE_NAME;
00339
00341 static const std::string ESCAPE_ATTRIBUTE_NAME;
00342
00344 static const std::string EMPTY_ATTRIBUTE_NAME;
00345
00347 static const std::string EVENTS_ELEMENT_NAME;
00348
00350 static const std::string FIELDS_ELEMENT_NAME;
00351
00353 static const std::string SPLIT_ATTRIBUTE_NAME;
00354
00356 static const std::string JOIN_ATTRIBUTE_NAME;
00357
00359 static const std::string COMMENT_ATTRIBUTE_NAME;
00360
00362 static const std::string CONSUME_ATTRIBUTE_NAME;
00363
00365 static const unsigned int READ_BUFFER_SIZE;
00366
00367
00369 static const std::string EVENT_SPLIT_SET;
00370 static const std::string EVENT_JOIN_STRING;
00371 static const std::string COMMENT_CHAR_SET;
00372 static const std::string FIELD_SPLIT_SET;
00373 static const std::string FIELD_JOIN_STRING;
00374 static const bool CONSUME_DELIMS_FLAG;
00375
00376
00378 static const std::string VERSION_ELF_HEADER;
00379 static const std::string DATE_ELF_HEADER;
00380 static const std::string SOFTWARE_ELF_HEADER;
00381 static const std::string FIELDS_ELF_HEADER;
00382
00383
00385 boost::scoped_array<char> m_read_buf;
00386
00388 const char * const m_read_end;
00389
00391 FieldMap m_field_map;
00392
00394 CurrentFormat m_format;
00395
00397 bool m_flush_after_write;
00398
00400 bool m_handle_elf_headers;
00401
00403 bool m_wrote_elf_headers;
00404
00406 boost::int32_t m_time_offset;
00407
00409 std::string m_event_split;
00410
00412 std::string m_event_join;
00413
00415 std::string m_comment_chars;
00416
00418 std::string m_field_split;
00419
00421 std::string m_field_join;
00422
00424 bool m_consume_delims;
00425 };
00426
00427
00428
00429
00430 inline void LogCodec::mapFieldToTerm(const std::string& field, const pion::platform::Vocabulary::Term& term,
00431 char delim_start, char delim_end, bool opt_delims,
00432 bool urlencode, char escape_char, const std::string& empty_val,
00433 bool do_time_offset, const PionDateTime::time_duration_type& time_offset)
00434 {
00435 for (FieldMap::const_iterator i = m_field_map.begin(); i != m_field_map.end(); ++i) {
00436 if (i->second->log_term.term_ref == term.term_ref)
00437 throw PionException("Duplicate Field Term");
00438 }
00439
00440 if (m_field_map.find(field) != m_field_map.end())
00441 throw PionException("Duplicate Field Name");
00442
00443
00444 LogFieldPtr field_ptr(new LogField(field, term, delim_start, delim_end, opt_delims, urlencode, escape_char, empty_val, do_time_offset, time_offset));
00445 switch (term.term_type) {
00446 case pion::platform::Vocabulary::TYPE_DATE_TIME:
00447 case pion::platform::Vocabulary::TYPE_DATE:
00448 case pion::platform::Vocabulary::TYPE_TIME:
00449 field_ptr->log_time_facet.setFormat(term.term_format);
00450 break;
00451 default:
00452 break;
00453 }
00454
00455 m_field_map[field] = field_ptr;
00456
00457 m_format.push_back(field_ptr);
00458 }
00459
00460 inline char * LogCodec::cstyle(char *cstring)
00461 {
00462 char *ptr = cstring;
00463 size_t len = strlen(cstring);
00464 size_t num, nlen;
00465
00466 while ( (ptr = strchr(ptr, '\\')) ) {
00467 nlen = 1;
00468 switch (ptr[1]) {
00469 case 'a': *ptr = '\a'; break;
00470 case 'b': *ptr = '\b'; break;
00471 case 'f': *ptr = '\f'; break;
00472 case 'n': *ptr = '\n'; break;
00473 case 'r': *ptr = '\r'; break;
00474 case 't': *ptr = '\t'; break;
00475 case 'v': *ptr = '\v'; break;
00476 case '_': *ptr = ' '; break;
00477 case '0': case '1': case '2': case '3':
00478 case '4': case '5': case '6': case '7':
00479 nlen = sscanf(ptr + 1, "%o", &num);
00480 *ptr = (char)num;
00481 break;
00482 case 'x':
00483 nlen = sscanf(ptr + 1, "%x", &num);
00484 *ptr = (char)num;
00485 break;
00486 }
00487 num = ptr - cstring + nlen;
00488 ptr++;
00489 memmove(ptr, ptr + nlen, len - num);
00490 }
00491
00492 return cstring;
00493 }
00494
00495 inline void LogCodec::changeELFFormat(char *fmt)
00496 {
00497 m_format.clear();
00498 char *ptr;
00499 bool last_field = false;
00500 while (!last_field && *fmt != '\0' && m_event_split.find(*fmt) == std::string::npos) {
00501
00502 while (*fmt == ' ') ++fmt;
00503
00504 ptr = fmt;
00505 while (*ptr != '\0' && m_event_split.find(*ptr) == std::string::npos && *ptr != ' ') ++ptr;
00506
00507 if (*ptr == '\0' || m_event_split.find(*ptr) != std::string::npos) last_field = true;
00508 *ptr = '\0';
00509 FieldMap::const_iterator i = m_field_map.find(fmt);
00510 if (i == m_field_map.end())
00511 throw BadFormatException(fmt);
00512 m_format.push_back(i->second);
00513 fmt = ptr + 1;
00514 }
00515 }
00516
00517 inline void LogCodec::writeELFHeaders(std::ostream& out) const
00518 {
00519 PionDateTime time_now(boost::posix_time::second_clock::universal_time());
00520 out << VERSION_ELF_HEADER << " 1.0" << m_event_join;
00521 out << DATE_ELF_HEADER << ' ' << time_now << m_event_join;
00522 out << SOFTWARE_ELF_HEADER << " Pion v" << PION_VERSION << m_event_join;
00523 out << FIELDS_ELF_HEADER;
00524 CurrentFormat::const_iterator i = m_format.begin();
00525 while (i != m_format.end())
00526 out << ' ' << (*i++)->log_field;
00527 out << m_event_join;
00528 }
00529
00530 inline LogCodec::int_type LogCodec::consumeVoidsAndComments(streambuf_type *buf_ptr)
00531 {
00532 int_type c = buf_ptr->sgetc();
00533 char * const read_buf = m_read_buf.get();
00534 char * read_ptr;
00535
00536 while (!traits_type::eq_int_type(c, traits_type::eof())) {
00537 if (m_field_split.find(c) != std::string::npos || m_event_split.find(c) != std::string::npos) {
00538 c = buf_ptr->snextc();
00539 } else if (m_comment_chars.find(c) != std::string::npos) {
00540
00541 read_ptr = read_buf;
00542 do {
00543
00544 if (m_event_split.find(c) != std::string::npos)
00545 break;
00546
00547 if (read_ptr < m_read_end)
00548 *(read_ptr++) = c;
00549
00550 c = buf_ptr->snextc();
00551 } while (!traits_type::eq_int_type(c, traits_type::eof()));
00552 *read_ptr = '\0';
00553 if (m_handle_elf_headers) {
00554
00555 read_buf[FIELDS_ELF_HEADER.size()] = '\0';
00556 if (FIELDS_ELF_HEADER == read_buf)
00557 changeELFFormat(read_buf + FIELDS_ELF_HEADER.size() + 1);
00558 }
00559 } else {
00560 break;
00561 }
00562 }
00563 return c;
00564 }
00565
00566
00567
00568
00569 inline void LogCodec::LogField::write(std::ostream& out, const pion::platform::Event::ParameterValue& value)
00570 {
00571 std::ostringstream oss;
00572
00573 switch(log_term.term_type) {
00574 case pion::platform::Vocabulary::TYPE_NULL:
00575 case pion::platform::Vocabulary::TYPE_OBJECT:
00576 break;
00577 case pion::platform::Vocabulary::TYPE_INT8:
00578 case pion::platform::Vocabulary::TYPE_INT16:
00579 case pion::platform::Vocabulary::TYPE_INT32:
00580 oss << boost::get<boost::int32_t>(value);
00581 break;
00582 case pion::platform::Vocabulary::TYPE_INT64:
00583 oss << boost::get<boost::int64_t>(value);
00584 break;
00585 case pion::platform::Vocabulary::TYPE_UINT8:
00586 case pion::platform::Vocabulary::TYPE_UINT16:
00587 case pion::platform::Vocabulary::TYPE_UINT32:
00588 oss << boost::get<boost::uint32_t>(value);
00589 break;
00590 case pion::platform::Vocabulary::TYPE_UINT64:
00591 oss << boost::get<boost::uint64_t>(value);
00592 break;
00593 case pion::platform::Vocabulary::TYPE_FLOAT:
00594 oss << boost::get<float>(value);
00595 break;
00596 case pion::platform::Vocabulary::TYPE_DOUBLE:
00597
00598 oss << boost::lexical_cast<std::string>(boost::get<double>(value));
00599 break;
00600 case pion::platform::Vocabulary::TYPE_LONG_DOUBLE:
00601
00602 oss << boost::lexical_cast<std::string>(boost::get<long double>(value));
00603 break;
00604 case pion::platform::Vocabulary::TYPE_SHORT_STRING:
00605 case pion::platform::Vocabulary::TYPE_STRING:
00606 case pion::platform::Vocabulary::TYPE_LONG_STRING:
00607 case pion::platform::Vocabulary::TYPE_BLOB:
00608 case pion::platform::Vocabulary::TYPE_ZBLOB:
00609 {
00610 const pion::platform::Event::BlobType& ss = boost::get<const pion::platform::Event::BlobType&>(value);
00611 if (ss.size() > 0) {
00612 if (log_urlencode) {
00613 std::string temp_str(ss.get());
00614 oss << algo::url_encode(temp_str);
00615 } else {
00616 oss.write(ss.get(), ss.size());
00617 }
00618 }
00619 break;
00620 }
00621 case pion::platform::Vocabulary::TYPE_CHAR:
00622 {
00623 const pion::platform::Event::BlobType& ss = boost::get<const pion::platform::Event::BlobType&>(value);
00624 if (ss.size() > 0) {
00625 if (log_urlencode) {
00626 std::string temp_str(ss.get());
00627 temp_str = algo::url_encode(temp_str);
00628 if (temp_str.size() > log_term.term_size)
00629 temp_str.resize(log_term.term_size);
00630 oss << temp_str;
00631 } else {
00632 oss.write(ss.get(), ss.size() < log_term.term_size ? ss.size() : log_term.term_size);
00633 }
00634 }
00635 break;
00636 }
00637 case pion::platform::Vocabulary::TYPE_DATE_TIME:
00638 case pion::platform::Vocabulary::TYPE_DATE:
00639 case pion::platform::Vocabulary::TYPE_TIME:
00640 {
00641 PionDateTime dt = boost::get<const PionDateTime&>(value);
00642 if (log_do_time_offset) {
00643 dt -= log_time_offset;
00644 }
00645 if (log_urlencode) {
00646 std::string temp_str;
00647 log_time_facet.toString(temp_str, dt);
00648 oss << algo::url_encode(temp_str);
00649 } else {
00650 log_time_facet.write(oss, dt);
00651 }
00652 break;
00653 }
00654 }
00655
00656 if (log_delim_start != '\0')
00657 out << log_delim_start;
00658 if (oss.str().empty())
00659 out << log_empty_val;
00660 else {
00661 std::string src = oss.str();
00662 std::string dst;
00663 for (std::string::iterator i = src.begin(); i != src.end(); i++) {
00664 if (*i == log_delim_end)
00665 dst.push_back(log_escape_char);
00666 dst.push_back(*i);
00667 }
00668 out << dst;
00669 }
00670 if (log_delim_end != '\0')
00671 out << log_delim_end;
00672 }
00673
00674 inline void LogCodec::LogField::read(const char *buf, pion::platform::Event& e)
00675 {
00676 switch(log_term.term_type) {
00677 case pion::platform::Vocabulary::TYPE_NULL:
00678 case pion::platform::Vocabulary::TYPE_OBJECT:
00679 break;
00680 case pion::platform::Vocabulary::TYPE_INT8:
00681 case pion::platform::Vocabulary::TYPE_INT16:
00682 case pion::platform::Vocabulary::TYPE_INT32:
00683 e.setInt(log_term.term_ref, boost::lexical_cast<boost::int32_t>(buf));
00684 break;
00685 case pion::platform::Vocabulary::TYPE_INT64:
00686 e.setBigInt(log_term.term_ref, boost::lexical_cast<boost::int64_t>(buf));
00687 break;
00688 case pion::platform::Vocabulary::TYPE_UINT8:
00689 case pion::platform::Vocabulary::TYPE_UINT16:
00690 case pion::platform::Vocabulary::TYPE_UINT32:
00691 e.setUInt(log_term.term_ref, boost::lexical_cast<boost::uint32_t>(buf));
00692 break;
00693 case pion::platform::Vocabulary::TYPE_UINT64:
00694 e.setUBigInt(log_term.term_ref, boost::lexical_cast<boost::uint64_t>(buf));
00695 break;
00696 case pion::platform::Vocabulary::TYPE_FLOAT:
00697 e.setFloat(log_term.term_ref, boost::lexical_cast<float>(buf));
00698 break;
00699 case pion::platform::Vocabulary::TYPE_DOUBLE:
00700 e.setDouble(log_term.term_ref, boost::lexical_cast<double>(buf));
00701 break;
00702 case pion::platform::Vocabulary::TYPE_LONG_DOUBLE:
00703 e.setLongDouble(log_term.term_ref, boost::lexical_cast<long double>(buf));
00704 break;
00705 case pion::platform::Vocabulary::TYPE_SHORT_STRING:
00706 case pion::platform::Vocabulary::TYPE_STRING:
00707 case pion::platform::Vocabulary::TYPE_LONG_STRING:
00708 case pion::platform::Vocabulary::TYPE_BLOB:
00709 case pion::platform::Vocabulary::TYPE_ZBLOB:
00710 if (log_urlencode) {
00711 std::string temp_str(algo::url_decode(buf));
00712 e.setString(log_term.term_ref, temp_str);
00713 } else {
00714 e.setString(log_term.term_ref, buf);
00715 }
00716 break;
00717 case pion::platform::Vocabulary::TYPE_CHAR:
00718 if (log_urlencode) {
00719 std::string temp_str(algo::url_decode(buf));
00720 if (temp_str.size() > log_term.term_size)
00721 temp_str.resize(log_term.term_size);
00722 e.setString(log_term.term_ref, temp_str);
00723 } else if (strlen(buf) > log_term.term_size) {
00724 e.setString(log_term.term_ref, std::string(buf, log_term.term_size));
00725 } else {
00726 e.setString(log_term.term_ref, buf);
00727 }
00728 break;
00729 case pion::platform::Vocabulary::TYPE_DATE_TIME:
00730 case pion::platform::Vocabulary::TYPE_DATE:
00731 case pion::platform::Vocabulary::TYPE_TIME:
00732 {
00733 PionDateTime dt;
00734 if (log_urlencode) {
00735 std::string temp_str(algo::url_decode(buf));
00736 log_time_facet.fromString(temp_str, dt);
00737 } else {
00738 log_time_facet.fromString(buf, dt);
00739 }
00740 if (log_do_time_offset) {
00741 dt += log_time_offset;
00742 }
00743 e.setDateTime(log_term.term_ref, dt);
00744 break;
00745 }
00746 }
00747 }
00748
00749
00750 }
00751 }
00752
00753 #endif