// -*- c++ -*- //------------------------------------------------------------------------------ // Logger.cpp //------------------------------------------------------------------------------ // $Id: Logger.cpp,v 1.7 2006/07/20 02:30:54 vlg Exp $ //------------------------------------------------------------------------------ // Copyright (c) 2001,2005 by Vladislav Grinchenko // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. //------------------------------------------------------------------------------ #include #include // for WIN32 hacking #include "assa/Connector.h" #include "assa/INETAddress.h" #include "assa/IPv4Socket.h" #include "assa/FileLogger.h" #include "assa/StdOutLogger.h" #include "assa/RemoteLogger.h" #include "assa/AutoPtr.h" #include "assa/Logger.h" using namespace ASSA; ASSA_DECL_SINGLETON(Logger); // Internal storage static const int TMPBUF_SZ = 4096; // Logger's member functions int Logger:: log_close (void) { int ret = 0; if (m_impl) { ret = m_impl->log_close (); delete m_impl; m_impl = 0; } return ret; } int Logger:: log_open (u_long groups_) { if (m_impl != NULL) { std::cerr << "Logger::log_open - Implementation already exist" << std::endl; return -1; } m_impl = new StdOutLogger; return m_impl->log_open (groups_); } int Logger:: log_open (const char* logfname_, u_long groups_, u_long maxsize_) { if (m_impl != NULL) { return -1; } m_impl = new FileLogger; return m_impl->log_open (logfname_, groups_, maxsize_); } int Logger:: log_open (const std::string& logsvraddr_, const char* logfname_, u_long groups_, u_long maxsize_, Reactor* reactor_) { { TimeVal tv (10.0); INETAddress addr (logsvraddr_.c_str ()); if (addr.bad ()) { return -1; } Connector log_connector; AutoPtr lsp (new RemoteLogger); log_connector.open (tv); if (log_connector.connect (lsp.get (), addr) < 0) { delete m_impl; m_impl = NULL; return -1; } m_impl = lsp.release (); } int ret = m_impl->log_open (m_app_name.c_str (), logfname_, groups_, maxsize_, reactor_); return ret; } /** Here is an interesting twist introduced by remote logging server: Setting errno bits puts us in an unexpected situation. Asynchronous connection establishment reliest on examening errno to see if it was set by the system call, ::connect(), to EINPROGRESS. If it was, and thus ::connect() failed, IPv4Socket::connect() would log it as an error with EL() macro. When client is configured to log its messages to the server, call to EL() would result into the call to log_msg(), and because connection to the server has not yet been established, m_impl is set to 0 and errno is reset to EPERM. When stack unwinds itself, Connector::connect() fails because Connector::connectServiceHandler() returned errno different from expected EINPROGRESS (it is EPERM)! */ /** From vprintf(3S) manpage: * * int vsnprintf (char* buf, size_t cnt, const char* fmt, va_list argptr); * * "The vsnprintf() function returns the number of characters * formatted, that is, then number of characters that would have * been written to the buffer if it were large enough. It returns * a negative value if an output error was encountered." * * However, mingw relies on WIN32 implementation of the function * which is inherently broken (not C99 compliant). * From MSD reference: * * "vsnprint returns the number of characters written if the * the number of characters to write is less than or equal to 'cnt'; * if the number of characters to write is greater than 'cnt', * RETURN -1 indicating that output has been truncated. The return * value does not include the terminating null, if one is written. */ int Logger:: log_msg (u_long g_, const char* fmt_, ...) { va_list ap; va_list ap2; string empty_str; size_t expected_sz = 0; static char tmpbuf[TMPBUF_SZ]; char* bufp = tmpbuf; int len = TMPBUF_SZ; int ret; if (m_impl == NULL) { return -1; } /** Estimate message size */ #if defined(WIN32) va_copy (ap2, ap); ret = vsnprintf (bufp, len-1, fmt_, ap2); va_end (ap2); if (ret == -1 || ret >= len) { len *= 2; bufp = new char [len]; while (1) { va_copy (ap2, ap); ret = vsnprintf (bufp, len-1, fmt_, ap2); va_end (ap2); if (ret > -1 && ret < len) { delete [] bufp; break; } len *= 2; delete [] bufp; bufp = new char [len]; // std::cout << "Logger::log_func : malloc(" << len << ")\n" // << std::flush; } } #else /* POSIX, C99 compliant */ char c; va_start (ap, fmt_); ret = ::vsnprintf (&c, 1, fmt_, ap); va_end (ap); #endif expected_sz = ret + 1; // std::cout << "Logger::log_func : expected_sz = " // << expected_sz << "\n" << std::flush; /** Fromat and write message to the log */ va_start (ap, fmt_); ret = m_impl->log_msg (static_cast (g_), m_context.size (), m_context.size () ? m_context.top () : empty_str, expected_sz, fmt_, ap); va_end (ap); /** * For extra debuggin * if (ret < 0) { va_start (ap, fmt_); vsnprintf (tmpbuf, TMPBUF_SZ -1, fmt_, ap); std::cout << "m_impl->log_msg()=-1 message:\n" << tmpbuf << std::flush; va_end (ap); } */ return ret; } int Logger:: log_func (u_long g_, marker_t type_) { std::string empty_str; if (m_impl == NULL) { return -1; } return m_impl->log_func (static_cast (g_), m_context.size (), m_context.size () ? m_context.top () : empty_str, type_); }