You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

254 lines
6.7 KiB

  1. // Module: Log4CPLUS
  2. // File: log4judpappender.cxx
  3. // Created: 7/2012
  4. // Author: Siva Chandran P
  5. //
  6. //
  7. // Copyright 2012-2013 Siva Chandran P
  8. //
  9. // Licensed under the Apache License, Version 2.0 (the "License");
  10. // you may not use this file except in compliance with the License.
  11. // You may obtain a copy of the License at
  12. //
  13. // http://www.apache.org/licenses/LICENSE-2.0
  14. //
  15. // Unless required by applicable law or agreed to in writing, software
  16. // distributed under the License is distributed on an "AS IS" BASIS,
  17. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. // See the License for the specific language governing permissions and
  19. // limitations under the License.
  20. #include <log4cplus/log4judpappender.h>
  21. #include <log4cplus/layout.h>
  22. #include <log4cplus/streams.h>
  23. #include <log4cplus/helpers/loglog.h>
  24. #include <log4cplus/helpers/property.h>
  25. #include <log4cplus/spi/loggingevent.h>
  26. #include <log4cplus/internal/internal.h>
  27. #include <log4cplus/thread/syncprims-pub-impl.h>
  28. #include <iomanip>
  29. #include <cstring>
  30. #if defined (UNICODE)
  31. #include <cwctype>
  32. #else
  33. #include <cctype>
  34. #endif
  35. namespace log4cplus
  36. {
  37. namespace
  38. {
  39. static inline bool
  40. is_control (tchar ch)
  41. {
  42. #if defined (UNICODE)
  43. return !! std::iswcntrl (std::char_traits<tchar>::to_int_type (ch));
  44. #else
  45. return !! std::iscntrl (std::char_traits<tchar>::to_int_type (ch));
  46. #endif
  47. }
  48. //! Outputs str with reserved XML characters escaped.
  49. static
  50. void
  51. output_xml_escaped (tostream & os, tstring const & str)
  52. {
  53. for (tstring::const_iterator it = str.begin (); it != str.end (); ++it)
  54. {
  55. tchar const & ch = *it;
  56. switch (ch)
  57. {
  58. case LOG4CPLUS_TEXT ('<'):
  59. os << LOG4CPLUS_TEXT ("&lt;");
  60. break;
  61. case LOG4CPLUS_TEXT ('>'):
  62. os << LOG4CPLUS_TEXT ("&gt;");
  63. break;
  64. case LOG4CPLUS_TEXT ('&'):
  65. os << LOG4CPLUS_TEXT ("&amp;");
  66. break;
  67. case LOG4CPLUS_TEXT ('\''):
  68. os << LOG4CPLUS_TEXT ("&apos;");
  69. break;
  70. case LOG4CPLUS_TEXT ('"'):
  71. os << LOG4CPLUS_TEXT ("&quot;");
  72. break;
  73. default:
  74. if (is_control (ch))
  75. {
  76. tchar const prev_fill = os.fill ();
  77. std::ios_base::fmtflags const prev_flags = os.flags ();
  78. os.flags (std::ios_base::hex | std::ios_base::right);
  79. os.fill (LOG4CPLUS_TEXT ('0'));
  80. os << std::setw (0) << LOG4CPLUS_TEXT ("&#x")
  81. << std::setw (2) << std::char_traits<tchar>::to_int_type (ch)
  82. << std::setw (0) << LOG4CPLUS_TEXT (";");
  83. os.fill (prev_fill);
  84. os.flags (prev_flags);
  85. }
  86. else
  87. os.put (ch);
  88. }
  89. }
  90. }
  91. //! Helper manipulator like class for escaped XML output.
  92. struct outputXMLEscaped
  93. {
  94. outputXMLEscaped (tstring const & s)
  95. : str (s)
  96. { }
  97. tstring const & str;
  98. };
  99. //! Overload stream insertion for outputXMLEscaped.
  100. static
  101. tostream &
  102. operator << (tostream & os, outputXMLEscaped const & x)
  103. {
  104. output_xml_escaped (os, x.str);
  105. return os;
  106. }
  107. } // namespace
  108. //////////////////////////////////////////////////////////////////////////////
  109. // Log4jUdpAppender ctors and dtor
  110. //////////////////////////////////////////////////////////////////////////////
  111. Log4jUdpAppender::Log4jUdpAppender(const tstring& host_, int port_)
  112. : host(host_)
  113. , port(port_)
  114. {
  115. layout.reset (new PatternLayout (LOG4CPLUS_TEXT ("%m")));
  116. openSocket();
  117. }
  118. Log4jUdpAppender::Log4jUdpAppender(const helpers::Properties & properties)
  119. : Appender(properties)
  120. , port(5000)
  121. {
  122. host = properties.getProperty( LOG4CPLUS_TEXT("host"),
  123. LOG4CPLUS_TEXT ("localhost") );
  124. properties.getInt (port, LOG4CPLUS_TEXT ("port"));
  125. openSocket();
  126. }
  127. Log4jUdpAppender::~Log4jUdpAppender()
  128. {
  129. destructorImpl();
  130. }
  131. //////////////////////////////////////////////////////////////////////////////
  132. // Log4jUdpAppender public methods
  133. //////////////////////////////////////////////////////////////////////////////
  134. void
  135. Log4jUdpAppender::close()
  136. {
  137. helpers::getLogLog().debug(
  138. LOG4CPLUS_TEXT("Entering Log4jUdpAppender::close()..."));
  139. socket.close();
  140. closed = true;
  141. }
  142. //////////////////////////////////////////////////////////////////////////////
  143. // Log4jUdpAppender protected methods
  144. //////////////////////////////////////////////////////////////////////////////
  145. void
  146. Log4jUdpAppender::openSocket()
  147. {
  148. if(!socket.isOpen()) {
  149. socket = helpers::Socket(host, port, true);
  150. }
  151. }
  152. void
  153. Log4jUdpAppender::append(const spi::InternalLoggingEvent& event)
  154. {
  155. if(!socket.isOpen()) {
  156. openSocket();
  157. if(!socket.isOpen()) {
  158. helpers::getLogLog().error(
  159. LOG4CPLUS_TEXT("Log4jUdpAppender::append()- Cannot connect to server"));
  160. return;
  161. }
  162. }
  163. tstring & str = formatEvent (event);
  164. internal::appender_sratch_pad & appender_sp
  165. = internal::get_appender_sp ();
  166. tostringstream & buffer = appender_sp.oss;
  167. detail::clear_tostringstream (buffer);
  168. buffer << LOG4CPLUS_TEXT("<log4j:event logger=\"")
  169. << outputXMLEscaped (event.getLoggerName())
  170. << LOG4CPLUS_TEXT("\" level=\"")
  171. // TODO: Some escaping of special characters is needed here.
  172. << outputXMLEscaped (getLogLevelManager().toString(event.getLogLevel()))
  173. << LOG4CPLUS_TEXT("\" timestamp=\"")
  174. << event.getTimestamp().getFormattedTime(LOG4CPLUS_TEXT("%s%q"))
  175. << LOG4CPLUS_TEXT("\" thread=\"") << event.getThread()
  176. << LOG4CPLUS_TEXT("\">")
  177. << LOG4CPLUS_TEXT("<log4j:message>")
  178. // TODO: Some escaping of special characters is needed here.
  179. << outputXMLEscaped (str)
  180. << LOG4CPLUS_TEXT("</log4j:message>")
  181. << LOG4CPLUS_TEXT("<log4j:NDC>")
  182. // TODO: Some escaping of special characters is needed here.
  183. << outputXMLEscaped (event.getNDC())
  184. << LOG4CPLUS_TEXT("</log4j:NDC>")
  185. << LOG4CPLUS_TEXT("<log4j:locationInfo class=\"\" file=\"")
  186. // TODO: Some escaping of special characters is needed here.
  187. << outputXMLEscaped (event.getFile())
  188. << LOG4CPLUS_TEXT("\" method=\"")
  189. << outputXMLEscaped (event.getFunction())
  190. << LOG4CPLUS_TEXT("\" line=\"")
  191. << event.getLine()
  192. << LOG4CPLUS_TEXT("\"/>")
  193. << LOG4CPLUS_TEXT("</log4j:event>");
  194. LOG4CPLUS_TSTRING_TO_STRING (buffer.str ()).swap (appender_sp.chstr);
  195. bool ret = socket.write(appender_sp.chstr);
  196. if (!ret)
  197. {
  198. helpers::getLogLog().error(
  199. LOG4CPLUS_TEXT(
  200. "Log4jUdpAppender::append()- Cannot write to server"));
  201. }
  202. }
  203. } // namespace log4cplus