summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuo Jinghua <sunmoon1997@gmail.com>2011-11-26 18:42:34 +0800
committerLuo Jinghua <sunmoon1997@gmail.com>2011-11-26 18:42:34 +0800
commitc024c6692c68b4e22041c04bb256ae44161a3d29 (patch)
tree7de951d84d3de813c838815a3d008bc0b8e82ee9
parent837cb69583d8be07d5d9bed3739ef75bcfa15da3 (diff)
SexyAppFramework: Added a tcp log listener
-rw-r--r--osframework/source/SexyAppFramework/SConscript3
-rw-r--r--osframework/source/SexyAppFramework/SexyLogManager.cpp6
-rw-r--r--osframework/source/SexyAppFramework/TcpLogListener.cpp290
-rw-r--r--osframework/source/SexyAppFramework/TcpLogListener.h86
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