tcpip_appender.cpp

Go to the documentation of this file.
00001 
00008 /**************************************************************************
00009 
00010    begin                : Fri Sep 14 2007
00011    copyright            : (C) 2007 by Ewald Arnold
00012    email                : log4sendpp at ewald-arnold dot de
00013 
00014    This program is free software; you can redistribute it and/or modify
00015    it under the terms of the GNU Lesser General Public License as
00016    published by the Free Software Foundation; either version 2 of the License,
00017    or (at your option) any later version.
00018 
00019    This program is distributed in the hope that it will be useful,
00020    but WITHOUT ANY WARRANTY; without even the implied warranty of
00021    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00022    GNU General Public License for more details.
00023 
00024    You should have received a copy of the GNU Lesser General Public License
00025    along with this program; if not, write to the Free Software
00026    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00027 
00028  **/
00029 
00031 #define LOG4SENDPP_NEED_EXPORTS
00032 #include <log4sendpp/log4sendpp.h>  // always first header
00033 
00034 #if defined(__WIN32__) || defined(_MSC_VER)
00035 #include <winsock2.h>
00036 //#include <windows.h>
00037 typedef int socklen_t;
00038 #endif
00039 
00040 #ifdef __unix__
00041 #include <unistd.h>
00042 #include <netdb.h>
00043 #include <arpa/inet.h>
00044 #include <netinet/in.h>
00045 #include <sys/socket.h>
00046 #endif
00047 
00048 #include <csignal>
00049 #include <cerrno>
00050 
00051 #include <log4sendpp/tcpip_appender.h>
00052 #include <log4sendpp/exception.h>
00053 #include <log4sendpp/formatter.h>
00054 
00055 
00056 LOG4SENDPP_NS_START
00057 
00058 
00061 struct TcpIpAppender::Impl
00062 {
00063    unsigned            timeout;    
00064    struct sockaddr_in  hostdata;   
00065    int                 fd_handle;  
00066    unsigned            port;       
00067    bool                net_error;  
00068 };
00069 
00070 
00071 #if defined(__WIN32__)  && !defined (LOG4SENDPP_NO_WSA_STARTUP)
00072 
00073 bool TcpIpAppender::WSA_is_initialized = false; 
00074 
00075 #endif
00076 
00078 
00079 
00080 LOG4SENDPP_API_IMPL(int) TcpIpAppender::getLastError()
00081 {
00082 #ifdef __WIN32__
00083     return(::WSAGetLastError());
00084 #else
00085     return(errno);
00086 #endif
00087 }
00088 
00089 
00090 LOG4SENDPP_API_IMPL(LOG4SENDPP_STD_NS::string)
00091   TcpIpAppender::getErrorString(int err_number)
00092 {
00093 #ifdef __WIN32__
00094     LPSTR lpMsgBuf;
00095     int ok = FormatMessage(
00096         FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
00097         NULL,
00098         err_number,
00099         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
00100         (LPSTR)&lpMsgBuf,
00101         0,
00102         NULL);
00103 
00104     if (ok != 0 && lpMsgBuf != 0)
00105       return lpMsgBuf;
00106 
00107     else
00108     {
00109       char s[40];
00110       LOG4SENDPP_STD_NS::string errn  = itoa(err_number, s, 10);
00111       return LOG4SENDPP_STD_NS::string("Unknown connection problem, Windows error code: #")+errn;
00112     }
00113 #else
00114     return LOG4SENDPP_STD_NS::strerror(err_number);
00115 #endif
00116 }
00117 
00118 
00119 LOG4SENDPP_API_IMPL(void)
00120   TcpIpAppender::asciiToInAddr(const char *address, struct in_addr &saddr)
00121 {
00122   memset (&saddr, 0, sizeof(in_addr));
00123   struct hostent *host;
00124 
00125   /* First try it as aaa.bbb.ccc.ddd. */
00126   saddr.s_addr = inet_addr(address);
00127   if ((int)saddr.s_addr == -1)
00128   {
00129     LOG4SENDPP_THROW(Exception(__LINE__, __FILE__, LOG4SENDPP_STD_NS::string("inet_addr() failed: ")
00130                                    + getErrorString(getLastError())));
00131 #ifdef LOG4SENDPP_NO_EXCEPTIONS
00132     pimpl->net_error = true;
00133     return;
00134 #endif
00135   }
00136 
00137   host = ::gethostbyname(address);
00138   if (host == 0)
00139   {
00140     LOG4SENDPP_THROW(Exception(__LINE__, __FILE__, LOG4SENDPP_STD_NS::string("gethostbyname() failed: ")
00141                                    + getErrorString(getLastError())));
00142 #ifdef LOG4SENDPP_NO_EXCEPTIONS
00143     pimpl->net_error = true;
00144     return;
00145 #endif
00146   }
00147 
00148   memmove((void*)&saddr, host->h_addr_list, sizeof(in_addr));
00149 }
00150 
00151 
00152 LOG4SENDPP_API_IMPL(struct hostent *)
00153   TcpIpAppender::getHostAdress(const LOG4SENDPP_STD_NS::string &dom)
00154 {
00155   return ::gethostbyname(dom.c_str() );
00156 }
00157 
00158 
00159 #if defined(__WIN32__)  && !defined (LOG4SENDPP_NO_WSA_STARTUP)
00160 
00161 
00162 LOG4SENDPP_API_IMPL(void) TcpIpAppender::WSA_init()
00163 {
00164   if (!WSA_is_initialized)
00165   {
00166     WSA_is_initialized = true;
00167     WORD wVersionRequested;
00168     WSADATA wsaData;
00169     wVersionRequested = MAKEWORD( 2, 0 );
00170 
00171     if (::WSAStartup( wVersionRequested, &wsaData) != 0)
00172     {
00173       LOG4SENDPP_THROW(Exception(__LINE__, __FILE__, LOG4SENDPP_STD_NS::string("Initializing Windows sockets failed: ")
00174                                   + getErrorString(getLastError())));
00175 #ifdef LOG4SENDPP_NO_EXCEPTIONS
00176       pimpl->net_error = true;
00177       return;
00178 #endif
00179     }
00180   }
00181 }
00182 
00183 #endif
00184 
00185 
00186 LOG4SENDPP_API_IMPL0 TcpIpAppender::TcpIpAppender(Formatter *formatter, long adr, unsigned prt)
00187   : Appender(formatter)
00188 {
00189   init(prt);
00190   pimpl->hostdata.sin_addr.s_addr = htonl(adr);
00191 
00192   LOG4SENDPP_TRY
00193   {
00194     open();
00195   }
00196   LOG4SENDPP_CATCH_ALL
00197   {
00198     pimpl->net_error = true;
00199   }
00200 }
00201 
00202 
00203 LOG4SENDPP_API_IMPL(LOG4SENDPP_STD_NS::string) TcpIpAppender::gethostname()
00204 {
00205 #if defined(__WIN32__)  && !defined (LOG4SENDPP_NO_WSA_STARTUP)
00206   WSA_init();
00207 #endif
00208 
00209   char buffer[1000];
00210   memset(buffer, 0, sizeof(buffer));
00211 
00212   int ret = ::gethostname ( buffer, sizeof(buffer)-1);
00213   if (ret != 0)
00214   {
00215     LOG4SENDPP_THROW(Exception(__LINE__, __FILE__, LOG4SENDPP_STD_NS::string("gethostname() failed: ")
00216                                    + getErrorString(getLastError())));
00217 #ifdef LOG4SENDPP_NO_EXCEPTIONS
00218     return "";
00219 #endif
00220   }
00221   return LOG4SENDPP_STD_NS::string(buffer);
00222 }
00223 
00224 
00225 LOG4SENDPP_API_IMPL0 TcpIpAppender::TcpIpAppender(Formatter *formatter, const LOG4SENDPP_STD_NS::string &host, unsigned prt)
00226   : Appender(formatter)
00227 {
00228   init(prt);
00229 
00230   struct hostent *hp = getHostAdress(host);
00231   if (hp == 0)
00232   {
00233 //    LOG4SENDPP_THROW(Exception(__LINE__, __FILE__, LOG4SENDPP_STD_NS::string("Host adress not found: ") + host));
00234 #ifdef LOG4SENDPP_NO_EXCEPTIONS
00235     pimpl->net_error = true;
00236     return;
00237 #endif
00238   }
00239 
00240   memcpy(&(pimpl->hostdata.sin_addr), hp->h_addr_list[0], hp->h_length);
00241 
00242   LOG4SENDPP_TRY
00243   {
00244     open();
00245   }
00246 
00247   LOG4SENDPP_CATCH_ALL
00248   {
00249     pimpl->net_error = true;
00250   }
00251 }
00252 
00253 LOG4SENDPP_API_IMPL(void) TcpIpAppender::init(unsigned prt)
00254 {
00255 #if !defined(__WIN32__) && !defined(_WIN32)
00256   signal (SIGPIPE, SIG_IGN);  // prevent SIGKILL while write()-ing in closing pipe
00257 #endif
00258 
00259 #if defined(__WIN32__)  && !defined (LOG4SENDPP_NO_WSA_STARTUP)
00260   WSA_init();
00261 #endif
00262 
00263   pimpl = new Impl;
00264   pimpl->timeout = 10;
00265   pimpl->port = prt;
00266   memset(&pimpl->hostdata, 0, sizeof(pimpl->hostdata));
00267   pimpl->hostdata.sin_port = htons(prt);
00268   pimpl->hostdata.sin_family = AF_INET;
00269   pimpl->net_error = false;
00270   pimpl->fd_handle = -1;
00271 }
00272 
00273 
00274 LOG4SENDPP_API_IMPL0 TcpIpAppender::~TcpIpAppender()
00275 {
00276   LOG4SENDPP_TRY
00277   {
00278     close();
00279   }
00280 
00281   LOG4SENDPP_CATCH_ALL
00282   {
00283     // forget it ?
00284   }
00285 
00286   delete pimpl;
00287   pimpl = 0;
00288 }
00289 
00290 
00291 LOG4SENDPP_API_IMPL(bool) TcpIpAppender::isWorking() const
00292 {
00293    return pimpl->fd_handle > 0 && !pimpl->net_error;
00294 }
00295 
00296 
00297 LOG4SENDPP_API_IMPL(void) TcpIpAppender::open()
00298 {
00299   if (pimpl->fd_handle >= 0)
00300      return;
00301 
00302   pimpl->fd_handle = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
00303   if (pimpl->fd_handle < 0)
00304   {
00305     LOG4SENDPP_THROW(Exception(__LINE__, __FILE__, LOG4SENDPP_STD_NS::string("socket() failed: ")
00306                                    + getErrorString(getLastError())));
00307 #ifdef LOG4SENDPP_NO_EXCEPTIONS
00308     pimpl->net_error = true;
00309     return;
00310 #endif
00311   }
00312 
00313   int iOptVal = pimpl->timeout * 1000;
00314   int iOptLen = sizeof(int);
00315   int ret;
00316   ret = ::setsockopt(pimpl->fd_handle, SOL_SOCKET, SO_RCVTIMEO, (char*)&iOptVal, iOptLen);
00317   ret = ::setsockopt(pimpl->fd_handle, SOL_SOCKET, SO_SNDTIMEO, (char*)&iOptVal, iOptLen);
00318 
00319   if(::connect(pimpl->fd_handle, (struct sockaddr *)&pimpl->hostdata, sizeof(pimpl->hostdata)) < 0)
00320   {
00321     LOG4SENDPP_THROW(Exception(__LINE__, __FILE__, LOG4SENDPP_STD_NS::string("connect() failed: ")
00322                                    + getErrorString(getLastError())));
00323 #ifdef LOG4SENDPP_NO_EXCEPTIONS
00324     pimpl->net_error = true;
00325     return;
00326 #endif
00327   }
00328 
00329   pimpl->net_error = false;
00330 
00331   LOG4SENDPP_UNUSED(ret);
00332 }
00333 
00334 
00335 LOG4SENDPP_API_IMPL(signed long) TcpIpAppender::low_level_write(char const *buff, long len)
00336 {
00337 #ifndef __unix__
00338       return ::send(pimpl->fd_handle, buff, len, 0);
00339 #else
00340       return ::write(pimpl->fd_handle, buff, len);
00341 #endif
00342 
00343 }
00344 
00345 
00346 LOG4SENDPP_API_IMPL(void) TcpIpAppender::append (Logger::Level level,
00347                                                 LOG4SENDPP_STD_NS::string msg,
00348                                                 LOG4SENDPP_STD_NS::string category,
00349                                                 LOG4SENDPP_INT64 stamp,
00350                                                 const LocationInformation *info,
00351                                                 const Logger::DiagnosticInformation *diaginfo )
00352 {
00353   if (!isWorking())
00354     return;
00355 
00356   LOG4SENDPP_TRY
00357   {
00358     Formatter *formatter = getFormatter();
00359     if (formatter)
00360     {
00361       LOG4SENDPP_STD_NS::vector<LOG4SENDPP_STD_NS::string> msgs = formatter->format(level, msg, category, stamp, info, diaginfo);
00362 
00363       LOG4SENDPP_STD_NS::string s;
00364       for (unsigned i = 0; i < msgs.size(); ++i)
00365          s += msgs[i] + "\n";
00366 
00367       writeString(s);
00368     }
00369   }
00370 
00371   LOG4SENDPP_CATCH_ALL
00372   {
00373     pimpl->net_error = true;
00374     LOG4SENDPP_RETHROW;
00375   }
00376 }
00377 
00378 
00379 LOG4SENDPP_API_IMPL(void) TcpIpAppender::writeString(const LOG4SENDPP_STD_NS::string &data)
00380 {
00381   long written;
00382 
00383   if (!isWorking() || data.length() == 0)
00384     return;
00385 
00386   fd_set wfd;
00387 
00388   timeval wait;
00389   wait.tv_sec = pimpl->timeout;
00390   wait.tv_usec = 0;
00391 
00392   unsigned len = data.length();
00393   char *buff = (char*) data.data();
00394 
00395   while (len > 0)
00396   {
00397     FD_ZERO(&wfd);
00398     FD_SET((unsigned) pimpl->fd_handle, &wfd);
00399     int ready;
00400     wait.tv_sec = pimpl->timeout;
00401     wait.tv_usec = 0;
00402     while((ready = select(pimpl->fd_handle+1, 0, &wfd, 0, &wait)) < 0)
00403     {
00404       if(errno == EINTR || errno == EAGAIN)
00405       {
00406         wait.tv_sec = pimpl->timeout;
00407         wait.tv_usec = 0;
00408         continue;
00409       }
00410 
00411       else
00412       {
00413         LOG4SENDPP_THROW(Exception(__LINE__, __FILE__, LOG4SENDPP_STD_NS::string("select() failed: ")
00414                              + getErrorString(getLastError())));
00415 #ifdef LOG4SENDPP_NO_EXCEPTIONS
00416        pimpl->net_error = true;
00417        return;
00418 #endif
00419       }
00420     }
00421 
00422     if(ready == 0)
00423     {
00424       LOG4SENDPP_THROW(Exception(__LINE__, __FILE__, "timeout while attempting to write."));
00425 #ifdef LOG4SENDPP_NO_EXCEPTIONS
00426       pimpl->net_error = true;
00427       return;
00428 #endif
00429     }
00430 
00431     if(FD_ISSET(pimpl->fd_handle, &wfd))
00432     {
00433       if ( (written = low_level_write(buff, len)) < 0)
00434       {
00435         switch(getLastError())
00436         {
00437           case EAGAIN:
00438           case EINTR:
00439 #ifdef __unix__
00440             errno = 0;
00441 #endif
00442           continue;
00443 
00444           case EPIPE:
00445             close();
00446             LOG4SENDPP_THROW(Exception(__LINE__, __FILE__, LOG4SENDPP_STD_NS::string("attempt to write to a connection"
00447                                             " already closed by the peer")));
00448 #ifdef LOG4SENDPP_NO_EXCEPTIONS
00449             pimpl->net_error = true;
00450             return;
00451 #endif
00452           /*break; */
00453 
00454           default:
00455             LOG4SENDPP_THROW(Exception(__LINE__, __FILE__, LOG4SENDPP_STD_NS::string("low_level_write() failed: ")
00456                                            + getErrorString(getLastError())));
00457 #ifdef LOG4SENDPP_NO_EXCEPTIONS
00458             pimpl->net_error = true;
00459             return;
00460 #endif
00461 
00462         }
00463       }
00464 
00465       else
00466       {
00467         buff += written;
00468         len -= written;
00469       }
00470     }
00471   }
00472 }
00473 
00474 
00475 LOG4SENDPP_API_IMPL(void) TcpIpAppender::close()
00476 {
00477   pimpl->net_error = false;
00478 
00479   if (pimpl->fd_handle < 0)
00480      return;
00481 
00482   int ret;
00483 
00484 #ifndef __unix__
00485 
00486   ret = closesocket(pimpl->fd_handle);
00487 
00488 #else
00489 
00490   do
00491     ret = ::close(pimpl->fd_handle);
00492   while(ret < 0 && (errno == EINTR || errno == EAGAIN));
00493 
00494 #endif
00495 
00496   pimpl->fd_handle = -1;
00497 
00498   if(ret < 0)
00499   {
00500     LOG4SENDPP_THROW(Exception(__LINE__, __FILE__, LOG4SENDPP_STD_NS::string("close() failed: ")
00501                                + getErrorString(getLastError())));
00502 #ifdef LOG4SENDPP_NO_EXCEPTIONS
00503     pimpl->net_error = true;
00504     return;
00505 #endif
00506   }
00507 }
00508 
00509 
00510 LOG4SENDPP_NS_END
00511 

Generated on Sat Nov 24 14:41:22 2007 for log4sendpp by  doxygen 1.5.3