본문 바로가기
-----ETC-----/C++ 네트워크 프로그래밍 시리즈

[C++ 네트워크 프로그래밍] Day 21: 네트워크 모니터링과 로깅

by cogito21_cpp 2024. 8. 1.
반응형

네트워크 모니터링과 로깅

네트워크 애플리케이션의 성능과 상태를 지속적으로 모니터링하고, 문제 발생 시 신속하게 대응하기 위해 로깅은 필수적입니다. 네트워크 모니터링과 로깅은 시스템의 안정성과 성능을 유지하는 데 중요한 역할을 합니다.

네트워크 모니터링

네트워크 모니터링은 네트워크 트래픽, 성능, 오류 등을 지속적으로 관찰하고 분석하는 과정입니다. 이를 통해 네트워크의 상태를 실시간으로 파악하고, 잠재적인 문제를 조기에 발견할 수 있습니다.

네트워크 모니터링 도구

  1. Prometheus: 오픈 소스 모니터링 시스템으로, 메트릭을 수집하고 저장하며, 알림을 설정할 수 있습니다.
  2. Grafana: 시각화 도구로, Prometheus와 같은 모니터링 시스템에서 데이터를 가져와 대시보드 형태로 시각화합니다.
  3. Nagios: 시스템 및 네트워크 모니터링 도구로, 서비스 및 호스트의 상태를 모니터링하고 알림을 설정할 수 있습니다.

로깅

로깅은 애플리케이션의 실행 상태, 오류, 성능 등을 기록하는 과정입니다. 이를 통해 문제 발생 시 디버깅이 용이해지고, 애플리케이션의 동작을 추적할 수 있습니다.

로깅 라이브러리

  1. spdlog: 매우 빠르고 간단한 C++ 로깅 라이브러리로, 다양한 싱크를 지원합니다.
  2. Boost.Log: Boost 라이브러리의 일부로, 강력한 로깅 기능을 제공합니다.
  3. glog: Google에서 개발한 C++ 로깅 라이브러리로, 간편하게 사용할 수 있습니다.

Boost.Beast를 사용한 네트워크 모니터링과 로깅 예제

Boost.Beast와 spdlog를 사용하여 간단한 HTTP 서버를 구현하고, 네트워크 요청을 로깅하는 예제를 만들어 보겠습니다.

설치

spdlog를 설치하려면 다음 명령어를 사용합니다.

  • Debian/Ubuntu:
  • sudo apt-get install libspdlog-dev
  • CentOS/RHEL:
  • sudo yum install spdlog

HTTP 서버 구현 및 로깅

LoggingHttpServer.h

#ifndef LOGGINGHTTPSERVER_H
#define LOGGINGHTTPSERVER_H

#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/strand.hpp>
#include <spdlog/spdlog.h>
#include <memory>
#include <string>
#include <iostream>

namespace beast = boost::beast; // Boost.Beast 네임스페이스
namespace http = beast::http;   // HTTP 관련 네임스페이스
namespace net = boost::asio;    // Boost.Asio 네임스페이스
using tcp = net::ip::tcp;       // TCP 네임스페이스

class Session : public std::enable_shared_from_this<Session> {
public:
    explicit Session(tcp::socket socket) : socket_(std::move(socket)) {}

    void run() {
        do_read();
    }

private:
    void do_read() {
        auto self(shared_from_this());
        http::async_read(socket_, buffer_, req_,
            [this, self](beast::error_code ec, std::size_t bytes_transferred) {
                boost::ignore_unused(bytes_transferred);
                if (!ec) {
                    handle_request();
                }
            });
    }

    void handle_request() {
        // 요청을 로깅합니다.
        spdlog::info("Received request: {} {}", req_.method_string().to_string(), req_.target().to_string());

        http::response<http::string_body> res{http::status::ok, req_.version()};
        res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
        res.set(http::field::content_type, "text/html");
        res.keep_alive(req_.keep_alive());
        res.body() = "Hello, world!";
        res.prepare_payload();

        do_write(std::move(res));
    }

    void do_write(http::response<http::string_body>&& res) {
        auto self(shared_from_this());
        http::async_write(socket_, res,
            [this, self](beast::error_code ec, std::size_t bytes_transferred) {
                boost::ignore_unused(bytes_transferred);
                socket_.shutdown(tcp::socket::shutdown_send, ec);
                socket_.close();
            });
    }

    tcp::socket socket_;
    beast::flat_buffer buffer_;
    http::request<http::string_body> req_;
};

class Listener : public std::enable_shared_from_this<Listener> {
public:
    Listener(net::io_context& ioc, tcp::endpoint endpoint)
        : acceptor_(net::make_strand(ioc)) {
        beast::error_code ec;

        acceptor_.open(endpoint.protocol(), ec);
        if (ec) {
            spdlog::error("Error opening socket: {}", ec.message());
            return;
        }

        acceptor_.set_option(net::socket_base::reuse_address(true), ec);
        if (ec) {
            spdlog::error("Error setting socket option: {}", ec.message());
            return;
        }

        acceptor_.bind(endpoint, ec);
        if (ec) {
            spdlog::error("Error binding socket: {}", ec.message());
            return;
        }

        acceptor_.listen(net::socket_base::max_listen_connections, ec);
        if (ec) {
            spdlog::error("Error listening on socket: {}", ec.message());
            return;
        }

        do_accept();
    }

private:
    void do_accept() {
        acceptor_.async_accept(
            net::make_strand(acceptor_.get_executor().context()),
            [this](beast::error_code ec, tcp::socket socket) {
                if (!ec) {
                    std::make_shared<Session>(std::move(socket))->run();
                } else {
                    spdlog::error("Error accepting connection: {}", ec.message());
                }

                do_accept();
            });
    }

    tcp::acceptor acceptor_;
};

#endif // LOGGINGHTTPSERVER_H

 

main.cpp

#include <boost/asio.hpp>
#include "LoggingHttpServer.h"

namespace net = boost::asio;
using tcp = net::ip::tcp;

int main() {
    try {
        spdlog::info("Starting server...");

        net::io_context ioc{1};
        auto const address = net::ip::make_address("0.0.0.0");
        auto const port = static_cast<unsigned short>(std::atoi("8080"));

        std::make_shared<Listener>(ioc, tcp::endpoint{address, port})->run();
        ioc.run();
    } catch (const std::exception& e) {
        spdlog::error("Exception: {}", e.what());
    }

    return 0;
}

 

설명

위의 코드는 Boost.Beast와 spdlog를 사용하여 간단한 HTTP 서버를 구현하고, 요청을 로깅하는 예제입니다.

  • Session 클래스:
    • 클라이언트와의 세션을 관리합니다. 클라이언트의 요청을 읽고, 응답을 보냅니다.
    • do_read() 함수는 클라이언트로부터 HTTP 요청을 비동기적으로 읽습니다.
    • handle_request() 함수는 클라이언트의 요청을 처리하고, 로깅합니다.
    • do_write() 함수는 클라이언트에게 HTTP 응답을 비동기적으로 씁니다.
  • Listener 클래스:
    • 클라이언트의 연결을 수락합니다. TCP 수신자를 사용하여 연결을 수락하고, 새로운 Session 객체를 생성합니다.
    • do_accept() 함수는 비동기적으로 클라이언트의 연결을 수락합니다.
  • main 함수:
    • main 함수는 io_context를 생성하고, HTTP 서버를 시작합니다. spdlog를 사용하여 서버의 시작과 예외를 로깅합니다.

 

이제 스물한 번째 날의 학습을 마쳤습니다. 네트워크 모니터링과 로깅의 기본 개념과 Boost.Beast와 spdlog를 사용하여 HTTP 서버에서 요청을 로깅하는 방법을 학습했습니다.

질문이나 피드백이 있으면 언제든지 댓글로 남겨 주세요. 내일은 "프로젝트 소개 및 설계 (실시간 채팅 애플리케이션)"에 대해 학습하겠습니다.

반응형