diff options
author | Luo Jinghua <sunmoon1997@gmail.com> | 2011-11-26 18:42:34 +0800 |
---|---|---|
committer | Luo Jinghua <sunmoon1997@gmail.com> | 2011-11-26 18:42:34 +0800 |
commit | c024c6692c68b4e22041c04bb256ae44161a3d29 (patch) | |
tree | 7de951d84d3de813c838815a3d008bc0b8e82ee9 | |
parent | 837cb69583d8be07d5d9bed3739ef75bcfa15da3 (diff) |
SexyAppFramework: Added a tcp log listener
4 files changed, 384 insertions, 1 deletions
diff --git a/osframework/source/SexyAppFramework/SConscript b/osframework/source/SexyAppFramework/SConscript index b93ff49..b7c27dc 100644 --- a/osframework/source/SexyAppFramework/SConscript +++ b/osframework/source/SexyAppFramework/SConscript @@ -35,7 +35,8 @@ srcs = ["Buffer.cpp", "ButtonWidget.cpp", "Checkbox.cpp", 'SexyLang.cpp', 'Find.cpp', 'TextLayout.cpp', 'SexyString.cpp', 'AndroidLogListener.cpp', 'DefaultLogListener.cpp', 'SexyLog.cpp', 'SexyLogListener.cpp', 'SexyLogManager.cpp', 'SelectableWidget.cpp', - 'SexySocket.cpp', 'SimpleUdpLogListener.cpp', 'SexyServiceManager.cpp'] + 'SexySocket.cpp', 'SimpleUdpLogListener.cpp', 'TcpLogListener.cpp', + 'SexyServiceManager.cpp'] ### uniconv srcs += map(lambda f:os.path.join ('uniconv', f), diff --git a/osframework/source/SexyAppFramework/SexyLogManager.cpp b/osframework/source/SexyAppFramework/SexyLogManager.cpp index 7594294..867c5f8 100644 --- a/osframework/source/SexyAppFramework/SexyLogManager.cpp +++ b/osframework/source/SexyAppFramework/SexyLogManager.cpp @@ -2,6 +2,7 @@ #include "DefaultLogListener.h" #include "Common.h" #include "SimpleUdpLogListener.h" +#include "TcpLogListener.h" #if defined(ANDROID) || defined(__ANDROID__) #include "AndroidLogListener.h" @@ -45,6 +46,11 @@ void LogManager::setupDefaultListener() { mDefaultListener = new SimpleUdpLogListener(mDefaultTarget); } + else if (mDefaultTarget == "tcp" || + mDefaultTarget.substr(0, 6) == "tcp://") + { + mDefaultListener = new TcpLogListener(mDefaultTarget); + } else { #if defined(ANDROID) || defined(__ANDROID__) diff --git a/osframework/source/SexyAppFramework/TcpLogListener.cpp b/osframework/source/SexyAppFramework/TcpLogListener.cpp new file mode 100644 index 0000000..9fc9763 --- /dev/null +++ b/osframework/source/SexyAppFramework/TcpLogListener.cpp @@ -0,0 +1,290 @@ +#include "TcpLogListener.h" +#include "SexyLogManager.h" +#include "SexyTimer.h" +#include "Common.h" + +#if defined(WIN32) || defined(_WIN32) +#include <winsock.h> +#else +#include <sys/select.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#endif + +#if defined(WIN32) || defined(_WIN32) +#define getpid() ((int)GetCurrentProcessId()) +#endif + +#include <stdio.h> +#include <errno.h> +#include <assert.h> + +using namespace Sexy; + +TcpLogListener::TcpLogListener(const std::string& target) : + mHost(""), mPort("11035") +{ + mMaxLogRecordSize = GetEnvIntOption("SEXY_TCP_LOG_BUFFER_SIZE", 1024 * 1024); + mLogRecordSeq = 0; + mLogRecordSize = 0; + + std::string s = target; + + if (s.substr(0, 6) == "tcp://") + { + s = s.substr(6); + std::vector<std::string> tuple; + Split(s, ":", tuple); + + if (tuple.size() > 1) + mPort = tuple[1]; + if (tuple.size() > 0) + mHost = tuple[0]; + } + + mDone = true; + mSock = new TCPServerSocket(mHost, atoi(mPort.c_str())); + if (mSock->hasError()) + { + // printf("TcpLogListener: Listening on %s:%s\n", mHost.c_str(), mPort.c_str()); + delete mSock; + mSock = 0; + } + else + { + mServiceInfo.mName = "sexytcplog"; + mServiceInfo.mDesc = "The log service for SexyAppFramework"; + mServiceInfo.mType = "tcp"; + mServiceInfo.mAddr = mSock->getLocalAddress(); + mServiceInfo.mPort = mPort; + + ServiceManager& mgr = ServiceManager::getInstance(); + mgr.registerService(mServiceInfo); + + mDone = false; + mThread = Thread::Create(serverProc, this); + } +} + +TcpLogListener::~TcpLogListener() +{ + if (mSock) + { + mDone = true; + mThread.Join(); + + ServiceManager& mgr = ServiceManager::getInstance(); + mgr.unregisterService(mServiceInfo); + } + + ClientMap::iterator it; + for (it = mClientMap.begin(); it != mClientMap.end();) + { + it->second.close(); + mClientMap.erase(it++); + } +} + +void TcpLogListener::serverProc(void* arg) +{ + TcpLogListener* tcpLogListener = (TcpLogListener*)arg; + tcpLogListener->server(); +} + +void TcpLogListener::addClient(TCPSocket* sock) +{ + mClientMap.insert(ClientMap::value_type(sock->getSocket(), TcpLogClient())); + TcpLogClient& client = mClientMap[sock->getSocket()]; + client.setSocket(sock); + client.mSeq = 0; + client.mWouldBlock = false; +} + +static inline char* writel(char *buf, unsigned int l) +{ + buf[0] = l >> 24; + buf[1] = (l >> 16) & 0xff; + buf[2] = (l >> 8) & 0xff; + buf[3] = (l >> 0) & 0xff; + + return buf + 4; +} + +static inline char* writes(char *buf, unsigned short s) +{ + buf[0] = (s >> 8) & 0xff; + buf[1] = (s >> 0) & 0xff; + + return buf + 2; +} + +bool TcpLogListener::sendRecord(TcpLogRecord* record, TcpLogClient& client) +{ + assert (client.mSock != 0); + + // 4b tag 'LGBD' + // 4b package len + // 4b pid + // 4b timestamp + // 2b log level + // 2b tag length + // 4b msg length + char header[4 + 4 + 4 + 4 + 2 + 2 + 4]; + char* ptr = header; + + ptr[0] = 'L'; + ptr[1] = 'G'; + ptr[2] = 'B'; + ptr[3] = 'D'; + ptr += 4; + + ptr = writel(ptr, sizeof(header) + record->tag.length() + record->msg.length()); + ptr = writel(ptr, record->pid); + ptr = writel(ptr, record->timestamp); + ptr = writes(ptr, record->lvl); + ptr = writes(ptr, record->tag.length()); + ptr = writel(ptr, record->msg.length()); + if (!client.mSock->send(header, sizeof(header))) + return false; + if (!client.mSock->send(record->tag.data(), record->tag.length())) + return false; + if (!client.mSock->send(record->msg.data(), record->msg.length())) + return false; + return true; +} + +void TcpLogListener::server() +{ + while (!mDone && mSock) + { + int sock = mSock->getSocket(); + fd_set rfds; + fd_set wfds; + fd_set efds; + + FD_ZERO(&rfds); + FD_SET(sock, &rfds); + FD_ZERO(&efds); + FD_SET(sock, &efds); + FD_ZERO(&wfds); + + int maxsock = sock; + ClientMap::iterator it; + for (it = mClientMap.begin(); it != mClientMap.end(); ++it) + { + TcpLogClient& client = it->second; + + if (it->first > maxsock) + maxsock = it->first; + + if (client.mWouldBlock && client.mSeq < mLogRecordSeq) + FD_SET(it->first, &wfds); + FD_SET(it->first, &efds); + } + + struct timeval tv; + tv.tv_sec = 0; + if (mClientMap.empty()) + tv.tv_usec = 10000; + else + tv.tv_usec = 1000; + + int ret = select(maxsock + 1, &rfds, &wfds, &efds, &tv); + if (ret < 0 && errno == EINTR) + continue; + if (ret < 0) + break; + + if (ret && FD_ISSET(sock, &efds)) + break; + + if (ret && FD_ISSET(sock, &rfds)) + { + TCPSocket* clientSock = mSock->accept(); + if (clientSock) + { + addClient(clientSock); + // printf("TcpLogListener: new client: %d\n", clientSock->getSocket()); + } + } + + TcpLogRecord* record = 0; + for (it = mClientMap.begin(); it != mClientMap.end();) + { + TcpLogClient& client = it->second; + + if (ret && FD_ISSET(it->first, &efds)) + { + // printf("TcpLogListener: removing client: %d\n", + // client.mSock->getSocket()); + client.close(); + mClientMap.erase(it++); + } + + if (ret && FD_ISSET(it->first, &rfds)) + { + // verify password ? + } + + if (!client.mWouldBlock || (ret && FD_ISSET(it->first, &wfds))) + { + AutoCrit anAutoCrit(mCritSect); + + TcpLogRecordMap::iterator logIt; + logIt = mLogRecords.upper_bound(client.mSeq); + if (logIt == mLogRecords.end()) + { + ++it; + continue; + } + + record = &logIt->second; + if (!sendRecord(record, client)) + { + // printf("TcpLogListener: removing client: %d\n", + // client.mSock->getSocket()); + client.close(); + mClientMap.erase(it++); + continue; + } + + client.mSeq = logIt->first; + } + + ++it; + } + } +} + +void TcpLogListener::log(LogLevel lvl, const std::string& tag, const std::string& s) +{ + if (mPort.empty() || !mSock || s.empty()) + return; + + AutoCrit anAutoCrit(mCritSect); + int64 seq = mLogRecordSeq++; + + mLogRecords.insert(TcpLogRecordMap::value_type(seq, TcpLogRecord())); + TcpLogRecord& record = mLogRecords[seq]; + record.lvl = lvl; + record.tag = tag; + record.msg = s; + record.pid = getpid(); + record.timestamp = GetTickCount(); + inlineRTrim(record.msg); + + size_t size = record.size(); + while (mMaxLogRecordSize && mLogRecordSize + size > mMaxLogRecordSize && + !mLogRecords.empty()) + { + TcpLogRecordMap::iterator it = mLogRecords.begin(); + + assert (mLogRecordSize > it->second.size()); + + mLogRecordSize -= it->second.size(); + mLogRecords.erase(it); + } + mLogRecordSize += size; + //printf("TcpLogListener: logsize %zd\n", mLogRecordSize); +} diff --git a/osframework/source/SexyAppFramework/TcpLogListener.h b/osframework/source/SexyAppFramework/TcpLogListener.h new file mode 100644 index 0000000..a8fb82e --- /dev/null +++ b/osframework/source/SexyAppFramework/TcpLogListener.h @@ -0,0 +1,86 @@ +#ifndef __TCP_LOG_LISTENER_H__ +#define __TCP_LOG_LISTENER_H__ + +#include "SexyLogListener.h" +#include "SexyServiceManager.h" +#include "SexySocket.h" +#include "SexyThread.h" +#include "AutoCrit.h" + +#include <string> + +namespace Sexy { + + struct TcpLogRecord { + LogLevel lvl; + std::string tag; + std::string msg; + int pid; + DWORD timestamp; + + size_t size() + { + return sizeof(TcpLogRecord) + tag.length() + msg.length(); + } + }; + + struct TcpLogClient { + TCPSocket* mSock; + int64 mSeq; + bool mWouldBlock; + + void setSeq(int64 seq) + { + mSeq = seq; + } + + void setSocket(TCPSocket* sock) + { + mSock = sock; + } + + void close() + { + delete mSock; + mSock = 0; + } + }; + + class TcpLogListener : public LogListener { + public: + TcpLogListener(const std::string& target = ""); + ~TcpLogListener(); + + virtual void log(LogLevel lvl, const std::string& tag, const std::string& s); + + private: + static void serverProc(void* arg); + + void server(); + void addClient(TCPSocket* socket); + bool sendRecord(TcpLogRecord* record, TcpLogClient& client); + + private: + TCPServerSocket *mSock; + Thread mThread; + CritSect mCritSect; + bool mDone; + + typedef std::map<int, TcpLogClient> ClientMap; + ClientMap mClientMap; + + typedef std::map<int64, TcpLogRecord> TcpLogRecordMap; + TcpLogRecordMap mLogRecords; + int64 mLogRecordSeq; + size_t mMaxLogRecordSize; + size_t mLogRecordSize; + + std::string mHost; + std::string mPort; + + ServiceInfo mServiceInfo; + }; + +} + +#endif |