diff --git a/cpp/tglog.cc b/cpp/tglog.cc new file mode 100644 index 0000000..e9b30f5 --- /dev/null +++ b/cpp/tglog.cc @@ -0,0 +1,192 @@ +#include +#include +#include +#include + +#include +#include +#include "tglog.h" + +static const char* const TGLOG_ROOT = "/data/logs/%s/upload"; +static const char* const POLY_TGLOG_ROOT = "/data/logs/%s/%s/upload"; +static const char* const TGLOG_FILENAME = "log_$pid_%Y%m%d%H.log"; + +struct TGLogMsgNode +{ + int game_id = 0; + std::string jsonstr; + TGLogMsgNode* next = nullptr; +}; + +struct TGLogImpl +{ + std::string filename_fmt; + std::string project_name; + bool is_poly_log = false; + + std::thread* save_thread = nullptr; + bool save_thread_shutdown = false; + + std::mutex msg_mutex; + TGLogMsgNode* top_node = nullptr; + TGLogMsgNode* bot_node = nullptr; + TGLogMsgNode* work_node = nullptr; + std::mutex *save_cond_mutex = nullptr; + std::condition_variable *save_cond = nullptr; + + TGLogImpl() + { + save_cond_mutex = new std::mutex(); + save_cond = new std::condition_variable(); + } + + virtual ~TGLogImpl() + { + delete save_cond_mutex; + save_cond_mutex = nullptr; + delete save_cond; + save_cond = nullptr; + } + + std::string GetCurrentFileName() + { + time_t nowtime = time(nullptr); + struct tm tm_nowtime = {0}; + localtime_r(&nowtime, &tm_nowtime); + + char szfilename[256]; + szfilename[0] = '\0'; + strftime(szfilename, a8::ArraySize(szfilename), filename_fmt.c_str(), &tm_nowtime); + std::string filename((char*)szfilename); + return filename; + } +}; + +void TGLog::Init() +{ + impl_ = new TGLogImpl(); + impl_->filename_fmt = TGLOG_FILENAME; + a8::ReplaceString(impl_->filename_fmt, "$pid", a8::XValue(getpid())); + + impl_->save_thread_shutdown = false; + impl_->save_thread = new std::thread(&TGLog::SaveToFileThreadProc, this); +} + +void TGLog::UnInit() +{ + delete impl_; + impl_ = nullptr; +} + +void TGLog::SetProjectInfo(const std::string& project_name, bool is_poly_log) +{ + project_name_ = project_name; + is_poly_log_ = is_poly_log; +} + +void TGLog::AddTrackLog(int game_id, const std::string& accountid, unsigned long ip_saddr, + int logclass1, int logclass2, a8::XObject* prop) +{ + std::string logtime_str; + { + time_t nowtime = time(nullptr); + struct tm tm_time = {0}; + localtime_r(&nowtime, &tm_time); + + char buff[256]; + strftime(buff, a8::ArraySize(buff), "%F %T", &tm_time); + logtime_str.append((char*)buff); + } + a8::MutableXObject* xobj = a8::MutableXObject::NewObject(); + + xobj->SetVal("#account_id", accountid); + xobj->SetVal("#type", "track"); + xobj->SetVal("#time", logtime_str); + xobj->SetVal("#ip", a8::GetIpAddress(ip_saddr)); + xobj->SetVal("#event_name", a8::Format("event_%d_%d", {logclass1, logclass2})); + + xobj->SetVal("properties", *prop); + { + TGLogMsgNode *p = new TGLogMsgNode(); + p->game_id = game_id; + xobj->ToJsonStr(p->jsonstr); + impl_->msg_mutex.lock(); + if (impl_->bot_node) { + impl_->bot_node->next = p; + impl_->bot_node = p; + } else { + impl_->top_node = p; + impl_->bot_node = p; + } + impl_->msg_mutex.unlock(); + { + std::unique_lock lk(*impl_->save_cond_mutex); + impl_->save_cond->notify_all(); + } + } +} + +void TGLog::SaveToFileThreadProc() +{ + auto GetLogFile = [this] (std::map opened_log_files_hash, int game_id, const std::string& filename) -> FILE* + { + auto itr = opened_log_files_hash.find(game_id); + if (itr == opened_log_files_hash.end()) { + std::string log_dir; + if (impl_->is_poly_log) { + log_dir = a8::Format(POLY_TGLOG_ROOT, {impl_->project_name, impl_->work_node->game_id}); + } else { + log_dir = a8::Format(TGLOG_ROOT, {impl_->project_name}); + } + a8::ForceCreateDir(log_dir); + FILE* logfile = fopen((log_dir + filename).c_str(), "a+"); + if (logfile) { + opened_log_files_hash[game_id] = logfile; + } + return logfile; + } else { + return itr->second; + } + }; + + while (!impl_->save_thread_shutdown) { + if (!impl_->work_node && impl_->top_node) { + impl_->msg_mutex.lock(); + impl_->work_node = impl_->top_node; + impl_->top_node = nullptr; + impl_->bot_node = nullptr; + impl_->msg_mutex.unlock(); + } + + if (impl_->work_node) { + std::string filename = impl_->GetCurrentFileName(); + std::map opened_log_files_hash; + + while (impl_->work_node) { + TGLogMsgNode* nextnode = impl_->work_node->next; + TGLogMsgNode* work_node = impl_->work_node; + + FILE* logfile = GetLogFile(opened_log_files_hash, work_node->game_id, filename); + if (logfile) { + work_node->jsonstr.push_back('\n'); + fwrite(work_node->jsonstr.c_str(), 1, work_node->jsonstr.size(), logfile); + } + delete work_node; + + impl_->work_node = nextnode; + } + + for (auto& pair : opened_log_files_hash) { + if (pair.second) { + fclose(pair.second); + } + } + } + + { + std::unique_lock lk(*impl_->save_cond_mutex); + impl_->save_cond->wait_for(lk, std::chrono::seconds(10)); + } + } + +} diff --git a/cpp/tglog.h b/cpp/tglog.h new file mode 100644 index 0000000..e0e3d06 --- /dev/null +++ b/cpp/tglog.h @@ -0,0 +1,25 @@ +#pragma once + +struct TGLogImpl; +class TGLog : public a8::Singleton +{ + private: + TGLog() {}; + friend class a8::Singleton; + +public: + void Init(); + void UnInit(); + + void SetProjectInfo(const std::string& project_name, bool is_poly_log); + void AddTrackLog(int game_id, const std::string& accountid, unsigned long ip_saddr, + int logclass1, int logclass2, a8::XObject* prop); + + private: + void SaveToFileThreadProc(); + + private: + std::string project_name_; + bool is_poly_log_ = false; + TGLogImpl* impl_ = nullptr; +}; diff --git a/cpp/utils.cc b/cpp/utils.cc index 0862657..84319c6 100644 --- a/cpp/utils.cc +++ b/cpp/utils.cc @@ -117,3 +117,61 @@ void InitMysqlConnection(a8::mysql::Query* query) query->Next(); } } + +bool CheckRegisterTimeInSessionId(const std::string& accountid, const std::string& sessionid) +{ + std::vector strings; + a8::Split(sessionid, strings, '_'); + if (strings.size() < 4) { + return false; + } + return true; +} + +time_t ExtractRegisterTimeFromSessionId(const std::string& sessionid) +{ + std::vector strings; + a8::Split(sessionid, strings, '_'); + if (strings.size() < 4) { + return false; + } + std::string session_createtime = strings[0]; + std::string account_registertime = strings[1]; + std::string md51 = strings[2]; + std::string md52 = strings[3]; + return a8::XValue(account_registertime); +} + +bool IsValidSessionId(const std::string& accountid, const std::string& sessionid) +{ + std::vector strings; + a8::Split(sessionid, strings, '_'); + if (strings.size() < 4) { + return false; + } + return true; +} + +int ExtractGameIdFromAccountId(const std::string& accountid) +{ + std::vector strings; + a8::Split(accountid, strings, '_'); + if (strings.size() < 2) { + return false; + } + std::string gameid = strings[0]; + std::string channelid = strings[1]; + return a8::XValue(gameid); +} + +int ExtractChannelIdFromAccountId(const std::string& accountid) +{ + std::vector strings; + a8::Split(accountid, strings, '_'); + if (strings.size() < 2) { + return false; + } + std::string gameid = strings[0]; + std::string channelid = strings[1]; + return a8::XValue(channelid); +} diff --git a/cpp/utils.h b/cpp/utils.h index 55b5caf..3372c96 100644 --- a/cpp/utils.h +++ b/cpp/utils.h @@ -76,3 +76,9 @@ static void SetToRepeatedField(const T1& t1, T2& t2) } void InitMysqlConnection(a8::mysql::Query* query); + +bool CheckRegisterTimeInSessionId(const std::string& accountid, const std::string& sessionid); +time_t ExtractRegisterTimeFromSessionId(const std::string& sessionid); +bool IsValidSessionId(const std::string& accountid, const std::string& sessionid); +int ExtractGameIdFromAccountId(const std::string& accountid); +int ExtractChannelIdFromAccountId(const std::string& accountid);