Skip to content

Commit

Permalink
Add logging to file
Browse files Browse the repository at this point in the history
  • Loading branch information
qprostu committed Jan 31, 2018
1 parent 3c3a19d commit 4d2ae76
Show file tree
Hide file tree
Showing 7 changed files with 310 additions and 39 deletions.
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ cmake_minimum_required(VERSION 2.8)

set(Boost_INCLUDE_DIR /usr/local/include)
set(Boost_LIBRARY_DIR /usr/local/lib)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m64 -pipe -O3 -g -Wall -W -std=c++11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m64 -pipe -O3 -g -Wall -W -std=c++11 -Isrc")
set(CMAKE_LINK_FLAGS "${CMAKE_LINK_FLAGS} -m64 -g -Wl,-O3")
find_package(Boost COMPONENTS system program_options thread filesystem regex REQUIRED)
include_directories(${Boost_INCLUDE_DIR})
link_directories(${Boost_LIBRARY_DIR})

add_executable(sqlproxy sqlproxy.cpp)
add_executable(sqlproxy src/sqlproxy.cpp)
target_link_libraries(sqlproxy ${Boost_LIBRARIES})
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,23 @@ with cmake:
$ mkdir .build && cd .build
$ cmake ..
$ make

### Example of configuration

logger {
filename "sqlproxy.log"
}
proxy {
proxy_ip "127.0.0.1"
proxy_port 3333
postgresql_ip "127.0.0.1"
postgresql_port 5432
}

### Execute command

$ sqlproxy --config sqlproxy.conf

### Testing

$ pgbench -h localhost -U test -d testdb -p 3333 -c 32 -j 4 -T 360
6 changes: 3 additions & 3 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ SQLProxy server build configuration
env = Environment(
CPPFLAGS='-m64 -pipe -O3 -g -Wall -W -std=c++11',
LINKFLAGS='-m64 -g -Wl,-O3',
CPPPATH=['/usr/local/include'],
CPPPATH=['src', '/usr/local/include'],
LIBPATH=['/usr/local/lib'],
LIBS=Split('pthread boost_system boost_program_options boost_thread '
'boost_filesystem boost_regex')
'boost_filesystem')
)

env['CPPPATH'].append(filter(None, ARGUMENTS.get("cpppath", "").split(",")))
env['LIBPATH'].append(filter(None, ARGUMENTS.get("libpath", "").split(",")))

env.Program('sqlproxy', ['sqlproxy.cpp'])
env.Program('sqlproxy', ['src/sqlproxy.cpp'])
5 changes: 4 additions & 1 deletion sqlproxy.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
SQLProxy {
logger {
filename "sqlproxy.log"
}
proxy {
proxy_ip "127.0.0.1"
proxy_port 3333
postgresql_ip "127.0.0.1"
Expand Down
201 changes: 201 additions & 0 deletions src/logger.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
/**
* logger.hpp
*/

#pragma once

#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>
#include <fstream>
#include <sstream>
#include <string>
#include <ctime>

namespace services {

/// Class to provide simple logging functionality. Use the services::logger
/// typedef.
template <typename Service>
class basic_logger
: private boost::noncopyable
{
public:
/// The type of the service that will be used to provide timer operations.
typedef Service service_type;

/// The native implementation type of the timer.
typedef typename service_type::impl_type impl_type;

/// Constructor.
/**
* This constructor creates a logger.
*
* @param io_service The io_service object used to locate the logger service.
*
* @param identifier An identifier for this logger.
*/
explicit basic_logger(boost::asio::io_service& io_service,
const std::string& identifier)
: service_(boost::asio::use_service<Service>(io_service))
, impl_(service_.null())
{
service_.create(impl_, identifier);
}

/// Destructor.
~basic_logger() {
service_.destroy(impl_);
}

/// Get the io_service associated with the object.
boost::asio::io_service& get_io_service() {
return service_.get_io_service();
}

/// Set the output file for all logger instances.
void use_file(const std::string& file) {
service_.use_file(impl_, file);
}

/// Log a message.
void log(const std::string& message) {
service_.log(impl_, message);
}

private:
/// The backend service implementation.
service_type& service_;

/// The underlying native implementation.
impl_type impl_;
}; // class basic_logger


/// Service implementation for the logger.
class logger_service
: public boost::asio::io_service::service
{
public:
/// The unique service identifier.
static boost::asio::io_service::id id;

/// The backend implementation of a logger.
struct logger_impl {
explicit logger_impl(const std::string& ident) : identifier(ident) {}
std::string identifier;
};

/// The type for an implementation of the logger.
typedef logger_impl* impl_type;

/// Constructor creates a thread to run a private io_service.
logger_service(boost::asio::io_service& io_service)
: boost::asio::io_service::service(io_service)
, work_io_service_()
, work_(new boost::asio::io_service::work(work_io_service_))
, work_thread_(new boost::thread(
boost::bind(&boost::asio::io_service::run, &work_io_service_)))
{}

/// Destructor shuts down the private io_service.
~logger_service() {
/// Indicate that we have finished with the private io_service. Its
/// io_service::run() function will exit once all other work has completed.
work_.reset();
if (work_thread_)
work_thread_->join();
}

/// Destroy all user-defined handler objects owned by the service.
void shutdown_service() {}

/// Return a null logger implementation.
impl_type null() const {
return nullptr;
}

/// Create a new logger implementation.
void create(impl_type& impl, const std::string& identifier) {
impl = new logger_impl(identifier);
}

/// Destroy a logger implementation.
void destroy(impl_type& impl) {
delete impl;
impl = null();
}

/// Set the output file for the logger. The current implementation sets the
/// output file for all logger instances, and so the impl parameter is not
/// actually needed. It is retained here to illustrate how service functions
/// are typically defined.
void use_file(impl_type& /*impl*/, const std::string& file) {
// Pass the work of opening the file to the background thread.
work_io_service_.post(boost::bind(
&logger_service::use_file_impl, this, file));
}

std::string cur_time() const {
std::array<char, 24> buf;
timeval tv;
gettimeofday(&tv, 0);
std::tm *now = localtime(&tv.tv_sec);
snprintf(&buf[0], 24, "%d-%02d-%02d %02d:%02d:%02d.%03ld",
now->tm_year+1900, now->tm_mon+1, now->tm_mday, now->tm_hour,
now->tm_min, now->tm_sec, tv.tv_usec/1000);

return std::string(&buf[0], 23);
}

/// Log a message.
void log(impl_type& impl, const std::string& message) {
std::ostringstream os;
os << "[" << cur_time() << "] " << impl->identifier << ": " << message;

// Pass the work of opening the file to the background thread.
work_io_service_.post(boost::bind(
&logger_service::log_impl, this, os.str()));
}

private:
/// Helper function used to open the output file from within the private
/// io_service's thread.
/// Must be executed only once
void use_file_impl(const std::string& file) {
if (!ofstream_.is_open()) {
// std::cerr << "Opening log " << file << "\n";
ofstream_.open(file.c_str(), std::ios_base::app);
}
}

/// Helper function used to log a message from within the private io_service's
/// thread.
void log_impl(const std::string& text) {
ofstream_ << text << std::endl;
}

/// Private io_service used for performing logging operations.
boost::asio::io_service work_io_service_;

/// Work for the private io_service to perform. If we do not give the
/// io_service some work to do then the io_service::run() function will exit
/// immediately.
boost::scoped_ptr<boost::asio::io_service::work> work_;

/// Thread used for running the work io_service's run loop.
boost::scoped_ptr<boost::thread> work_thread_;

/// The file to which log messages will be written.
std::ofstream ofstream_;
};

boost::asio::io_service::id logger_service::id;

/// Typedef for typical logger usage.
typedef basic_logger<logger_service> logger;

} // namespace services
67 changes: 44 additions & 23 deletions server.hpp → src/server.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#pragma once

#include "logger.hpp"
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/enable_shared_from_this.hpp>
Expand Down Expand Up @@ -33,10 +34,14 @@ class session
typedef boost::shared_ptr<session> ptr_type;

/// Session constructor
session(boost::asio::io_service& ios)
: client_socket_(ios),
server_socket_(ios)
{}
session(boost::asio::io_service& ios, const std::string& log_name)
: client_socket_(ios)
, server_socket_(ios)
, logger_(ios, "SQL")
{
// Set the name of the file that all logger instances will use.
logger_.use_file(log_name);
}

/// Client socket property
tcp::socket& client_socket() {
Expand Down Expand Up @@ -106,19 +111,31 @@ class session
const size_t& bytes_transferred) {
if (!ec) {
if (client_data_[0] == 'Q') {
// Log sql request to console
std::cout << client_data_[0] << " ";
uint64_t p = (client_data_[1] << 24) +
(client_data_[2] << 16) +
(client_data_[3] << 8) +
client_data_[4];
std::cout << p << " ";
// Log sql request
// uint64_t p = (client_data_[1] << 24) +
// (client_data_[2] << 16) +
// (client_data_[3] << 8) +
// client_data_[4];
// std::cout << p << "] ";
std::string command(
// const_cast<const char*>(&client_data_[5]),
&client_data_[5],
bytes_transferred - 5);
std::cout << command << "\n";
bytes_transferred - 6);
logger_.log(command);
}
// Log connection user and database
// else {
// std::string value(&client_data_[0], bytes_transferred);
// std::size_t pos = value.find("user");
// if (pos != std::string::npos) {
// std::size_t pos1 = value.find("database");
// if (pos1 != std::string::npos) {
// std::stringstream ss;
// ss << "Connection user: " << value.substr(pos + 5, pos1 - pos - 6)
// << ", database: " << value.substr(pos1 + 9);
// logger_.log(ss.str());
// }
// }
// }

async_write(
server_socket_,
Expand Down Expand Up @@ -179,6 +196,7 @@ class session

tcp::socket client_socket_;
tcp::socket server_socket_;
services::logger logger_;

enum { max_length = 4096 };
std::array<char, max_length> client_data_;
Expand All @@ -193,21 +211,23 @@ class server
public:
server(boost::asio::io_service& io_service,
const std::string& local_host, uint16_t local_port,
const std::string& server_host, uint16_t server_port)
: io_service_(io_service),
localhost_address_(
boost::asio::ip::address_v4::from_string(local_host)),
acceptor_(io_service_,
tcp::endpoint(localhost_address_, local_port)),
server_port_(server_port),
server_host_(server_host)
const std::string& server_host, uint16_t server_port,
const std::string& log_name)
: io_service_(io_service)
, localhost_address_(
boost::asio::ip::address_v4::from_string(local_host))
, acceptor_(io_service_,
tcp::endpoint(localhost_address_, local_port))
, server_port_(server_port)
, server_host_(server_host)
, log_name_(log_name)
{}

/// Accept client connections
bool accept_connections() {
try {
// Create session with client/server sockets
session_ = boost::make_shared<session>(io_service_);
session_ = boost::make_shared<session>(io_service_, log_name_);

acceptor_.async_accept(
session_->client_socket(),
Expand Down Expand Up @@ -245,6 +265,7 @@ class server
session::ptr_type session_;
uint16_t server_port_;
std::string server_host_;
std::string log_name_;
}; // class server

} // namespace sqlproxy
Loading

0 comments on commit 4d2ae76

Please sign in to comment.