From 662b7c41123aa1ae17d71fd2f649ab2365bc464b Mon Sep 17 00:00:00 2001 From: Adam Gu Date: Tue, 24 Apr 2018 17:24:55 +0800 Subject: [PATCH] Add console logger to replace original debug output. --- CMakeLists.txt | 5 ++ example/rest_book_client/main.cc | 5 +- example/rest_book_server/main.cc | 5 ++ src/webcc/CMakeLists.txt | 8 +-- src/webcc/common.h | 10 ---- src/webcc/http_client.cc | 34 ++++------- src/webcc/http_request.cc | 12 +++- src/webcc/http_request.h | 3 + src/webcc/http_request_handler.cc | 33 +++-------- src/webcc/http_response.cc | 14 ++++- src/webcc/http_response.h | 3 + src/webcc/http_server.cc | 10 +--- src/webcc/http_session.cc | 18 +----- src/webcc/logger.cc | 74 ++++++++++++++++++++++++ src/webcc/logger.h | 94 +++++++++++++++++++++++++++++++ src/webcc/rest_server.cc | 13 +---- src/webcc/soap_server.cc | 13 +---- 17 files changed, 242 insertions(+), 112 deletions(-) create mode 100644 src/webcc/logger.cc create mode 100644 src/webcc/logger.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 926b8f7..afde4ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,16 @@ cmake_minimum_required(VERSION 3.1.0) project(webcc) +option(WEBCC_ENABLE_LOG "Enable console logger?" ON) option(WEBCC_ENABLE_SOAP "Enable SOAP support (need pugixml)?" ON) option(WEBCC_BUILD_UNITTEST "Build unit test?" ON) option(WEBCC_BUILD_REST_EXAMPLE "Build REST example?" ON) option(WEBCC_BUILD_SOAP_EXAMPLE "Build SOAP example?" ON) +if(WEBCC_ENABLE_LOG) + add_definitions(-DWEBCC_ENABLE_LOG) +endif() + if(WEBCC_ENABLE_SOAP) add_definitions(-DWEBCC_ENABLE_SOAP) endif() diff --git a/example/rest_book_client/main.cc b/example/rest_book_client/main.cc index c87165c..7805852 100644 --- a/example/rest_book_client/main.cc +++ b/example/rest_book_client/main.cc @@ -3,6 +3,7 @@ #include "boost/algorithm/string.hpp" #include "json/json.h" // jsoncpp +#include "webcc/logger.h" #include "webcc/http_client.h" #include "webcc/http_request.h" #include "webcc/http_response.h" @@ -54,7 +55,7 @@ public: return false; } - std::cout << http_response.content() << std::endl; + std::cout << "result:\n" << http_response.content() << std::endl; return true; } @@ -174,6 +175,8 @@ int main(int argc, char* argv[]) { return 1; } + LOG_INIT(webcc::VERB, 0); + g_host = argv[1]; g_port = argv[2]; diff --git a/example/rest_book_server/main.cc b/example/rest_book_server/main.cc index 937f1f7..2cb453e 100644 --- a/example/rest_book_server/main.cc +++ b/example/rest_book_server/main.cc @@ -1,5 +1,8 @@ #include + +#include "webcc/logger.h" #include "webcc/rest_server.h" + #include "book_services.h" static void Help(const char* argv0) { @@ -14,6 +17,8 @@ int main(int argc, char* argv[]) { return 1; } + LOG_INIT(webcc::VERB, 0); + unsigned short port = std::atoi(argv[1]); std::size_t workers = 2; diff --git a/src/webcc/CMakeLists.txt b/src/webcc/CMakeLists.txt index 5cde4bd..5d7659d 100644 --- a/src/webcc/CMakeLists.txt +++ b/src/webcc/CMakeLists.txt @@ -1,9 +1,3 @@ -option(WEBCC_DEBUG_OUTPUT "Enable debug output?" OFF) - -if(WEBCC_DEBUG_OUTPUT) - add_definitions(-DWEBCC_DEBUG_OUTPUT) -endif() - # Don't use any deprecated definitions (e.g., io_service). add_definitions(-DBOOST_ASIO_NO_DEPRECATED) @@ -30,6 +24,8 @@ set(SRCS http_server.h http_session.cc http_session.h + logger.cc + logger.h queue.h rest_server.cc rest_server.h diff --git a/src/webcc/common.h b/src/webcc/common.h index cee918a..714bb98 100644 --- a/src/webcc/common.h +++ b/src/webcc/common.h @@ -64,16 +64,6 @@ const std::string kHttpPatch = "PATCH"; const std::string kHttpPut = "PUT"; const std::string kHttpDelete = "DELETE"; -//enum class HttpMethod : int { -// kUnknown, -// kHead, -// kGet, -// kPost, -// kPatch, -// kPut, -// kDelete, -//}; - // HTTP response status. // This is not a full list. // Full list: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes diff --git a/src/webcc/http_client.cc b/src/webcc/http_client.cc index e6d53c4..4e1c4a1 100644 --- a/src/webcc/http_client.cc +++ b/src/webcc/http_client.cc @@ -1,9 +1,5 @@ #include "webcc/http_client.h" -#if WEBCC_DEBUG_OUTPUT -#include -#endif - #if 0 #include "boost/asio.hpp" #else @@ -13,6 +9,7 @@ #include "boost/asio/write.hpp" #endif +#include "webcc/logger.h" #include "webcc/http_response_parser.h" #include "webcc/http_request.h" #include "webcc/http_response.h" @@ -69,9 +66,14 @@ Error HttpClient::SendRequest(const HttpRequest& request, } boost::system::error_code ec; - tcp::resolver::results_type endpoints = - resolver.resolve(tcp::v4(), request.host(), port, ec); + // tcp::resolver::results_type + auto endpoints = resolver.resolve(tcp::v4(), request.host(), port, ec); + if (ec) { + LOG_ERRO("cannot resolve host: %s, %s", + request.host().c_str(), + port.c_str()); + return kHostResolveError; } @@ -84,9 +86,7 @@ Error HttpClient::SendRequest(const HttpRequest& request, // Send HTTP request. -#if WEBCC_DEBUG_OUTPUT - std::cout << "--- REQUEST ---" << std::endl << request << std::endl; -#endif + LOG_VERB("http request:\n{\n%s}", request.Dump().c_str()); try { boost::asio::write(socket, request.ToBuffers()); @@ -94,10 +94,6 @@ Error HttpClient::SendRequest(const HttpRequest& request, return kSocketWriteError; } -#if WEBCC_DEBUG_OUTPUT - std::cout << "--- RESPONSE ---" << std::endl; -#endif - // Read and parse HTTP response. HttpResponseParser parser(response); @@ -119,26 +115,18 @@ Error HttpClient::SendRequest(const HttpRequest& request, return kSocketReadError; } -#if WEBCC_DEBUG_OUTPUT - // NOTE: the content XML might not be well formated. - std::cout.write(buffer_.data(), length); -#endif - // Parse the response piece just read. // If the content has been fully received, next time flag "finished_" // will be set. Error error = parser.Parse(buffer_.data(), length); if (error != kNoError) { + LOG_ERRO("failed to parse http response."); return error; } } -#if WEBCC_DEBUG_OUTPUT - std::cout << std::endl; - std::cout << "--- RESPONSE (PARSED) ---" << std::endl; - std::cout << *response << std::endl; -#endif + LOG_VERB("http response:\n{\n%s}", response->Dump().c_str()); return kNoError; } diff --git a/src/webcc/http_request.cc b/src/webcc/http_request.cc index 75d5097..f621c61 100644 --- a/src/webcc/http_request.cc +++ b/src/webcc/http_request.cc @@ -1,5 +1,7 @@ #include "webcc/http_request.h" +#include + #include "boost/algorithm/string.hpp" namespace webcc { @@ -13,7 +15,9 @@ std::ostream& operator<<(std::ostream& os, const HttpRequest& request) { os << std::endl; - os << request.content() << std::endl; + if (!request.content().empty()) { + os << request.content() << std::endl; + } return os; } @@ -69,4 +73,10 @@ std::vector HttpRequest::ToBuffers() const { return buffers; } +std::string HttpRequest::Dump() const { + std::stringstream ss; + ss << *this; + return ss.str(); +} + } // namespace webcc diff --git a/src/webcc/http_request.h b/src/webcc/http_request.h index f7232a5..1891b35 100644 --- a/src/webcc/http_request.h +++ b/src/webcc/http_request.h @@ -60,6 +60,9 @@ public: // and not be changed until the write operation has completed. std::vector ToBuffers() const; + // Dump as string, only used by logger. + std::string Dump() const; + private: // HTTP method. std::string method_; diff --git a/src/webcc/http_request_handler.cc b/src/webcc/http_request_handler.cc index 5c61114..b07c78c 100644 --- a/src/webcc/http_request_handler.cc +++ b/src/webcc/http_request_handler.cc @@ -2,10 +2,7 @@ #include -#if WEBCC_DEBUG_OUTPUT -#include -#endif - +#include "webcc/logger.h" #include "webcc/common.h" #include "webcc/http_request.h" #include "webcc/http_response.h" @@ -20,27 +17,16 @@ void HttpRequestHandler::Start(std::size_t count) { assert(count > 0 && workers_.size() == 0); for (std::size_t i = 0; i < count; ++i) { -#if WEBCC_DEBUG_OUTPUT - boost::thread* worker = -#endif workers_.create_thread(std::bind(&HttpRequestHandler::WorkerRoutine, this)); - -#if WEBCC_DEBUG_OUTPUT - std::cout << "Worker is running (thread: " << worker->get_id() << ")\n"; -#endif } } void HttpRequestHandler::Stop() { -#if WEBCC_DEBUG_OUTPUT - std::cout << "Stopping workers...\n"; -#endif + LOG_VERB("Stopping workers..."); // Close pending sessions. for (HttpSessionPtr conn = queue_.Pop(); conn; conn = queue_.Pop()) { -#if WEBCC_DEBUG_OUTPUT - std::cout << "Closing pending session...\n"; -#endif + LOG_VERB("Closing pending session..."); conn->Stop(); } @@ -49,23 +35,18 @@ void HttpRequestHandler::Stop() { workers_.join_all(); -#if WEBCC_DEBUG_OUTPUT - std::cout << "All workers have been stopped.\n"; -#endif + LOG_VERB("All workers have been stopped."); } void HttpRequestHandler::WorkerRoutine() { -#if WEBCC_DEBUG_OUTPUT - boost::thread::id thread_id = boost::this_thread::get_id(); -#endif + LOG_VERB("Worker is running."); for (;;) { HttpSessionPtr session = queue_.PopOrWait(); if (!session) { -#if WEBCC_DEBUG_OUTPUT - std::cout << "Worker is going to stop (thread: " << thread_id << ")\n"; -#endif + LOG_VERB("Worker is going to stop."); + // For stopping next worker. queue_.Push(HttpSessionPtr()); diff --git a/src/webcc/http_response.cc b/src/webcc/http_response.cc index 71fd311..c74f321 100644 --- a/src/webcc/http_response.cc +++ b/src/webcc/http_response.cc @@ -1,5 +1,6 @@ #include "webcc/http_response.h" #include +#include namespace webcc { @@ -10,8 +11,11 @@ std::ostream& operator<<(std::ostream& os, const HttpResponse& response) { os << h.name << ": " << h.value << std::endl; } - os << std::endl << std::endl; - os << response.content(); + os << std::endl; + + if (!response.content().empty()) { + os << response.content() << std::endl; + } return os; } @@ -112,4 +116,10 @@ HttpResponse HttpResponse::Fault(HttpStatus::Enum status) { return response; } +std::string HttpResponse::Dump() const { + std::stringstream ss; + ss << *this; + return ss.str(); +} + } // namespace webcc diff --git a/src/webcc/http_response.h b/src/webcc/http_response.h index 8748876..fa9a7a0 100644 --- a/src/webcc/http_response.h +++ b/src/webcc/http_response.h @@ -32,6 +32,9 @@ public: // and not be changed until the write operation has completed. std::vector ToBuffers() const; + // Dump as string, only used by logger. + std::string Dump() const; + // Get a fault response when HTTP status is not OK. static HttpResponse Fault(HttpStatus::Enum status); diff --git a/src/webcc/http_server.cc b/src/webcc/http_server.cc index 86e50d9..7d29324 100644 --- a/src/webcc/http_server.cc +++ b/src/webcc/http_server.cc @@ -2,10 +2,7 @@ #include -#if WEBCC_DEBUG_OUTPUT -#include -#endif - +#include "webcc/logger.h" #include "webcc/http_request_handler.h" #include "webcc/soap_service.h" #include "webcc/utility.h" @@ -49,10 +46,7 @@ HttpServer::~HttpServer() { void HttpServer::Run() { assert(GetRequestHandler() != NULL); -#if WEBCC_DEBUG_OUTPUT - boost::thread::id thread_id = boost::this_thread::get_id(); - std::cout << "Server main thread: " << thread_id << std::endl; -#endif + LOG_VERB("Server is going to run..."); // Start worker threads. GetRequestHandler()->Start(workers_); diff --git a/src/webcc/http_session.cc b/src/webcc/http_session.cc index 7be746c..f8a1efd 100644 --- a/src/webcc/http_session.cc +++ b/src/webcc/http_session.cc @@ -1,13 +1,11 @@ #include "webcc/http_session.h" #include -#if WEBCC_DEBUG_OUTPUT -#include -#endif #include "boost/asio/write.hpp" #include "boost/date_time/posix_time/posix_time.hpp" +#include "webcc/logger.h" #include "webcc/http_request_handler.h" namespace webcc { @@ -94,25 +92,15 @@ void HttpSession::HandleRead(boost::system::error_code ec, // ensured by Asio. void HttpSession::HandleWrite(boost::system::error_code ec, std::size_t length) { -#if WEBCC_DEBUG_OUTPUT - boost::thread::id thread_id = boost::this_thread::get_id(); -#endif - if (!ec) { -#if WEBCC_DEBUG_OUTPUT - std::cout << "Response has been sent back (thread: " << thread_id << ")\n"; -#endif + LOG_VERB("Response has been sent back."); // Initiate graceful connection closure. boost::system::error_code ec; socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); } else { -#if WEBCC_DEBUG_OUTPUT - std::cout << "(thread: " << thread_id << ") Sending response error: " - << ec.message() - << std::endl; -#endif + LOG_ERRO("Sending response error: %s", ec.message().c_str()); if (ec != boost::asio::error::operation_aborted) { Stop(); diff --git a/src/webcc/logger.cc b/src/webcc/logger.cc new file mode 100644 index 0000000..fafb0d5 --- /dev/null +++ b/src/webcc/logger.cc @@ -0,0 +1,74 @@ +#include "webcc/logger.h" + +#if WEBCC_ENABLE_LOG + +#include +#include +#include + +#include "boost/thread.hpp" // For thread ID. + +namespace webcc { + +//////////////////////////////////////////////////////////////////////////////// + +static std::string GetThreadId() { + boost::thread::id thread_id = boost::this_thread::get_id(); + std::stringstream ss; + ss << thread_id; + return ss.str(); +} + +//////////////////////////////////////////////////////////////////////////////// + +static const char* kLevelNames[] = { + "VERB", "INFO", "WARN", "ERRO", "FATA" +}; + +struct Logger { + int level; + int modes; + std::mutex mutex; +}; + +// Global logger. +static Logger g_logger{ VERB }; + +//////////////////////////////////////////////////////////////////////////////// + +void LogInit(int level, int modes) { + g_logger.modes = modes; + g_logger.level = level; +} + +void LogWrite(int level, const char* file, int line, const char* format, ...) { + assert(format != nullptr); + + if (g_logger.level > level) { + return; + } + + std::lock_guard lock(g_logger.mutex); + + va_list va_ptr_console; + va_start(va_ptr_console, format); + + fprintf(stderr, + "%s, %5s, %24s, %4d, ", + kLevelNames[level], + GetThreadId().c_str(), + file, + line); + vfprintf(stderr, format, va_ptr_console); + fprintf(stderr, "\n"); + + if ((g_logger.modes & FLUSH) == FLUSH) { + fflush(stderr); + } + + va_end(va_ptr_console); +} + +} // namespace webcc + +#endif // WEBCC_ENABLE_LOG diff --git a/src/webcc/logger.h b/src/webcc/logger.h new file mode 100644 index 0000000..d4cc19f --- /dev/null +++ b/src/webcc/logger.h @@ -0,0 +1,94 @@ +#ifndef WEBCC_LOGGER_H_ +#define WEBCC_LOGGER_H_ + +// Simple console logger. + +namespace webcc { + +#if WEBCC_ENABLE_LOG + +enum LogLevel { + VERB = 0, + INFO, + WARN, + ERRO, + FATA, +}; + +enum LogMode { + FLUSH = 1, +}; + +void LogInit(int level, int modes); + +void LogWrite(int level, const char* file, int line, const char* format, ...); + +// Initialize the logger with a level. +// E.g., LOG_INIT(ERRO, FLUSH) +#define LOG_INIT(level, modes) LogInit(level, modes); + +#if (defined(WIN32) || defined(_WIN64)) + +// See: https://stackoverflow.com/a/8488201 +#define __FILENAME__ strrchr("\\" __FILE__, '\\') + 1 + +#define LOG_VERB(format, ...) \ + LogWrite(VERB, __FILENAME__, __LINE__, format, ##__VA_ARGS__); + +#define LOG_INFO(format, ...) \ + LogWrite(INFO, __FILENAME__, __LINE__, format, ##__VA_ARGS__); + +#define LOG_WARN(format, ...) \ + LogWrite(WARN, __FILENAME__, __LINE__, format, ##__VA_ARGS__); + +#define LOG_ERRO(format, ...) \ + LogWrite(ERRO, __FILENAME__, __LINE__, format, ##__VA_ARGS__); + +#define LOG_FATA(format, ...) \ + LogWrite(FATA, __FILENAME__, __LINE__, format, ##__VA_ARGS__); + +#else + +// See: https://stackoverflow.com/a/8488201 +#define __FILENAME__ strrchr("/" __FILE__, '/') + 1 + +#define LOG_VERB(format, args...) \ + LogWrite(VERB, __FILENAME__, __LINE__, format, ##args); + +#define LOG_INFO(format, args...) \ + LogWrite(INFO, __FILENAME__, __LINE__, format, ##args); + +#define LOG_WARN(format, args...) \ + LogWrite(WARN, __FILENAME__, __LINE__, format, ##args); + +#define LOG_ERRO(format, args...) \ + LogWrite(ERRO, __FILENAME__, __LINE__, format, ##args); + +#define LOG_FATA(format, args...) \ + LogWrite(FATA, __FILENAME__, __LINE__, format, ##args); + +#endif // defined(WIN32) || defined(_WIN64) + +#else // WEBCC_ENABLE_LOG == 0 + +#define LOG_INIT(level, modes) + +#if (defined(WIN32) || defined(_WIN64)) +#define LOG_VERB(format, ...) +#define LOG_INFO(format, ...) +#define LOG_WARN(format, ...) +#define LOG_ERRO(format, ...) +#define LOG_FATA(format, ...) +#else +#define LOG_VERB(format, args...) +#define LOG_INFO(format, args...) +#define LOG_WARN(format, args...) +#define LOG_ERRO(format, args...) +#define LOG_FATA(format, args...) +#endif // defined(WIN32) || defined(_WIN64) + +#endif // WEBCC_ENABLE_LOG + +} // namespace webcc + +#endif // WEBCC_LOGGER_H_ diff --git a/src/webcc/rest_server.cc b/src/webcc/rest_server.cc index 39199c9..dd5db6f 100644 --- a/src/webcc/rest_server.cc +++ b/src/webcc/rest_server.cc @@ -1,9 +1,6 @@ #include "webcc/rest_server.h" -#if WEBCC_DEBUG_OUTPUT -#include -#endif - +#include "webcc/logger.h" #include "webcc/url.h" namespace webcc { @@ -27,9 +24,7 @@ bool RestServiceManager::AddService(RestServicePtr service, return true; } catch (std::regex_error& e) { -#if WEBCC_DEBUG_OUTPUT - std::cout << e.what() << std::endl; -#endif + LOG_ERRO("URL is not a valid regular expression: %s", e.what()); } return false; @@ -76,9 +71,7 @@ HttpStatus::Enum RestRequestHandler::HandleSession(HttpSessionPtr session) { std::vector sub_matches; RestServicePtr service = service_manager_.GetService(url.path(), &sub_matches); if (!service) { -#if WEBCC_DEBUG_OUTPUT - std::cout << "No service matches the URL: " << url.path() << std::endl; -#endif + LOG_WARN("No service matches the URL: %s", url.path().c_str()); session->SetResponseStatus(HttpStatus::kBadRequest); session->SendResponse(); return HttpStatus::kBadRequest; diff --git a/src/webcc/soap_server.cc b/src/webcc/soap_server.cc index a46a6a2..d57eabe 100644 --- a/src/webcc/soap_server.cc +++ b/src/webcc/soap_server.cc @@ -1,9 +1,6 @@ #include "webcc/soap_server.h" -#if WEBCC_DEBUG_OUTPUT -#include -#endif - +#include "webcc/logger.h" #include "webcc/soap_request.h" #include "webcc/soap_response.h" @@ -55,15 +52,11 @@ SoapServicePtr SoapRequestHandler::GetServiceByUrl(const std::string& url) { UrlServiceMap::const_iterator it = url_service_map_.find(url); if (it != url_service_map_.end()) { -#if WEBCC_DEBUG_OUTPUT - std::cout << "Service matches the URL: " << url << std::endl; -#endif + LOG_VERB("Service matches the URL: %s", url.c_str()); return it->second; } -#if WEBCC_DEBUG_OUTPUT - std::cout << "No service matches the URL: " << url << std::endl; -#endif + LOG_WARN("No service matches the URL: %s", url.c_str()); return SoapServicePtr(); }