在实际生产中,很多时候需要测试配合查找问题,为了方便测试抓取log,基于glog使用UDP往以太网发送log,只需要wireshark或者一些通用工具就能把log记录下来。本文glog版本是v0.6.0.
在glog上,我们可以通过AddLogSink函数添加自定义的日志输出目标。
GLOG_EXPORT void AddLogSink(LogSink *destination);
该方法需要一个输出参数LogSink,通过继承google::LogSink实现,新类需要实现send函数。send函数即为自定义的发送函数。
virtual void send(google::LogSeverity severity, const char* full_filename,
const char* base_filename, int line,
const google::LogMessageTime& logmsgtime,
const char* message, size_t message_len)
我在send函数里实现了通过UDP发送log的代码。
#ifndef ETHERNET_LOG_UDP_LOG_SINK_H
#define ETHERNET_LOG_UDP_LOG_SINK_H
#include "glog/logging.h"
#include "udp_log.h"
#include <memory>
class UDPLogSink : public google::LogSink {
public:
UDPLogSink() {
udp_log = std::make_unique<UDPLog>();
udp_log->socket_init();
}
UDPLogSink(const std::string& address, uint32_t port) {
udp_log = std::make_unique<UDPLog>(address, port);
udp_log->socket_init();
}
private:
virtual void send(google::LogSeverity severity, const char* full_filename,
const char* base_filename, int line,
const google::LogMessageTime& logmsgtime,
const char* message, size_t message_len) {
udp_log->write_log(google::LogSink::ToString(
severity,
base_filename,
line,
logmsgtime,
message,
message_len));
}
std::unique_ptr<UDPLog> udp_log{nullptr};
};
#endif //ETHERNET_LOG_UDP_LOG_SINK_H
UDPLog是发送log的实现
udp_log.h
#ifndef ETHERNET_LOG_UDP_LOG_H
#define ETHERNET_LOG_UDP_LOG_H
#include <string>
#include <arpa/inet.h>
#include "mutex"
class UDPLog {
public:
explicit UDPLog() = default;
explicit UDPLog(const std::string& address, uint32_t port);
~UDPLog();
int socket_init();
void write_log(const std::string& log);
private:
const std::string send_address = "255.255.255.255";
const uint32_t send_port = 8000;
int sockfd{0};
sockaddr_in socket_addr{0};
std::mutex send_lock;
};
#endif //ETHERNET_LOG_UDP_LOG_H
udp_log.cpp
对于sendto是否需要加锁比较疑惑,查资料有人说要加,有人说不需要,最后为了保险,还是加了锁。
#include "udp_log.h"
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
UDPLog::~UDPLog() {
if (sockfd) {
close(sockfd);
}
}
void UDPLog::write_log(const std::string &log) {
std::lock_guard<std::mutex> lock(send_lock);
sendto(sockfd, log.c_str(), log.size(), 0, (struct sockaddr *)&socket_addr, sizeof(sockaddr));
}
int UDPLog::socket_init() {
// 创建UDP套接字
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
return sockfd;
}
socket_addr.sin_family = AF_INET;
socket_addr.sin_addr.s_addr = inet_addr(send_address.c_str());
socket_addr.sin_port = htons(send_port);
return 0;
}
UDPLog::UDPLog(const std::string &address, uint32_t port) :
send_address(address),
send_port(port) {
}
测试代码main.cpp
#include <iostream>
#include "udp_log/udp_log_sink.h"
#include "glog/logging.h"
#include <thread>
#include <signal.h>
bool loop = true;
void signal_handler(int num) {
LOG(INFO) << "Program exit, receive signal " << num;
loop = false;
}
void log_thread(bool& running) {
while (running) {
LOG(INFO) << "log thread tid " << gettid();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
int main(int argc, char* argv[]) {
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
FLAGS_log_dir = ".";
google::InitGoogleLogging(argv[0]);
UDPLogSink udp_log_sink("192.168.244.1", 8000);
google::AddLogSink(&udp_log_sink);
std::thread t1([&]{ log_thread(loop);});
std::thread t2([&]{ log_thread(loop);});
uint32_t i = 0;
while (loop) {
LOG(INFO) << "hello,world! " << i;
++i;
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
t1.join();
t2.join();
return 0;
}
wireshark测试结果
网络调试助手