大云海山数据库(He3DB)前沿探索:HAProxy日志时间打印优化实践

21 阅读7分钟

HAProxy日志时间打印优化实践

HAProxy 日志是排查负载均衡故障、分析流量特征、优化性能的核心依据,不同日志格式(默认 / 自定义)包含的信息维度不同,下面从日志配置、日志格式日志输出等方面分析。

HAProxy 日志格式

HAProxy 日志默认有两种输出格式,需先确认你的配置(haproxy.cfg 中 log-format 字段)

  • 默认格式(HTTP 模式)

HAProxy 对 HTTP 流量的默认日志格式,包含请求方法、URL、状态码、客户端 / 后端信息等核心字段。

  • TCP 模式格式

若 HAProxy 代理 TCP 流量(如数据库、SSH),日志会简化为「连接层面」的信息(无 HTTP 相关字段)。

日志输出位置

HAProxy 日志默认发送到系统日志(/var/log/messages/、/var/log/syslog),也可配置为独立文件(如 /var/log/haproxy.log),需确保 rsyslog/syslog-ng 已正确配置。

下面主要通过分析TCP 模式日志格式(适配数据库),使用三种方式【syslog、stdout和源码改造】研究HAProxy标准日志输出。

HAProxy 日志输出方式

  • 输出到本地 syslog 服务

HAProxy 最经典的日志输出方式,依赖系统 rsyslog/syslog-ng 服务,将日志发送到本地 syslog 后落地到文件

缺点:需配置 syslog 服务,容器化场景适配性差。

  • 输出到本地 Unix 套接字(比 syslog 更高效)

用本地 Unix 套接字(/dev/log)替代网络套接字(127.0.0.1:514),减少网络开销,局限性:仅适用于 HAProxy 与 syslog 同机的场景。

配套配置

无需额外监听 514 端口,仅需 rsyslog 配置 local2.* /var/log/haproxy.log 即可。

适用场景

物理机 / 虚拟机部署,HAProxy 与 syslog 同机,不适用容器化场景;

追求日志传输效率(无 UDP 网络开销)。

  • 输出到标准输出(stdout)(容器化首选,HAProxy 2.4+)

HAProxy 2.4 新增的输出方式,直接将日志打印到进程 stdout,适配 Docker/K8s 容器化场景(容器日志默认采集 stdout)

**适用场景:**Docker/K8s 容器化部署的 HAProxy;无 syslog 服务的轻量环境(如极简容器);调试场景(直接看控制台输出)

  • 输出到远程 syslog 服务器(集中化日志)

将日志发送到远程 syslog 服务器(如 rsyslog 服务端、ELK、Graylog),实现多台 HAProxy 日志集中管理。

**适用场景:**多台 HAProxy 集群部署;集中化日志分析、存储的场景;

1、HAProxy默认日志配置

通过配置log 127.0.0.1:514 local2和log global执行日志打印,将 HAProxy 日志发送到本机 514 端口的 syslog 服务,并标记为 local2 日志设施,defaults继承 global 段定义的日志输出规则。

global

log 127.0.0.1:514 local2

maxconn 1000

ulimit-n 1048576

daemon

stats socket /usr/local/stats.sock mode 666 level admin

pidfile /var/run/haproxy.pid

nbthread 8

defaults

mode tcp

retries 6

timeout http-request 10s

timeout queue 1m

timeout connect 10s

timeout client 3601s

timeout server 3601s

timeout http-keep-alive 3601s

timeout check 10s

log global

option tcplog

option dontlognull

option http-server-close

option redispatch

resolvers mysql-resolver

parse-resolv-conf

resolve_retries 6

timeout retry 3s

hold valid 10s

hold nx 10s

hold timeout 10s

hold refused 10s

hold other 10s

listen inter_listen

bind :::3306 v4v6

mode tcp

option tcpka

balance roundrobin

server mysql1 a4243a5ca8a-6b4370-headless.200cec7c.svc.cluster.local:3306 check inter 30000 fall 3 rise 3 resolvers mysql-resolver

日志输出:

**存在问题:**打印日志无时间信息,不方便运维人员定位异常时间点。

2、log-format配置日志打印格式

精简并定制日志输出字段,只保留连接、时间、前后端、流量、连接数等核心维度,去掉了 HTTP 请求行、Cookie 等冗余字段,适合对日志体积敏感、仅关注基础流量监控的场景。

格式定义:log-format "%ci:%cp [%t] %ft %b/%s %Tw/%Tc/%Tt %B %ts %ac/%fc/%bc/%sc/%rc %sq/%bq"

占位符

完整名称

中文含义

示例

%ci

client_ip

客户端 IP 地址(发起请求的客户端 / 上游设备 IP)

192.168.1.100

%cp

client_port

客户端源端口(客户端发起请求的端口)

54321

%t

time_local

HAProxy 接收请求的时间(精确到毫秒,格式:[dd/MMM/YYYY:HH:MM:SS.ms])

[20/Nov/2025:10:23:44.123]

%ft

frontend_name

接收请求的前端名称(haproxy.cfg 中 frontend 配置的名称)

http_front

%b

backend_name

选中的后端名称(haproxy.cfg 中 backend 配置的名称)

http_back

%s

server_name

选中的后端服务器名称(backend 中 server 配置的名称)

server1

%Tw

time_queue_wait

客户端请求在 HAProxy 队列中的等待时间(毫秒,无等待则为 0)

0

%Tc

time_connect

HAProxy 与后端服务器建立连接的时间(毫秒)

10

%Tt

time_total

从请求入队到响应完成的总耗时(毫秒,= % Tw + % Tc + 后端处理时间)

30

%B

bytes_out

HAProxy 发送给客户端的响应字节数(不含 TCP/IP 头部)

1234

%ts

termination_state

会话终止状态

...

stdout输出方式:容器化友好、无需配置 syslog、调试直观。Syslog,需配置 rsyslog/syslog-ng,使用成本高。

指定stdout输出格式,必须指定log global,否则日志不输出到stdout。log stdout local3 info 是 HAProxy 2.4+ 版本新增的日志输出到标准输出(stdout) 的配置,核心作用是「将 HAProxy 日志直接打印到进程的标准输出,标记为 local3 设施,日志级别为 info」,特别适合容器化(Docker/K8s)部署的 HAProxy(容器日志默认采集 stdout)。

版本要求:

HAProxy 版本 >= 2.4,HAProxy 版本 < 2.4,不支持 stdout 目标。

配置示例

日志输出:

**存在问题:**发现使用stdout输出方式日志出现重复打印情况,存在有时间和无时间两行日志,造成日志冗余,增加了日志采集负担和排查复杂度。

  1. 改造HAPROXY源码,修改默认日志打印格式

优化思路:通过修改v3.2.10版本HAProxy日志打印函数print_message,通过扩展前缀缓冲区空间,将格式化后的时间信息拼接至原有label前,实现日志默认打印时间信息目的,主要改造内容:

  • 前缀缓冲区扩展

扩展前缀缓冲区:原11字节 + 时间戳(21字节) + 预留空间,总计64字节(避免溢出)

char prefix[64] = {0};

  • 时间查询

struct timeval tv;

gettimeofday(&tv, NULL);

  • 时间解析

localtime_r(&tv.tv_sec, &tm_info);

线程安全的时间解析,避免localtime()竞态问题。

  • 格式化时间戳

格式:[YYYY-MM-DD HH:MM:SS]

char time_buf[32] = {0};

snprintf(time_buf, sizeof(time_buf),

"[%04d-%02d-%02d %02d:%02d:%02d]",

tm_info.tm_year + 1900, tm_info.tm_mon + 1, tm_info.tm_mday,

tm_info.tm_hour, tm_info.tm_min, tm_info.tm_sec);

  • 拼接时间戳

先写入时间戳

strncpy(prefix, time_buf, sizeof(prefix) - 1);

拼接原有label(格式:[时间] [label]), label最多7字符,兼容原逻辑。

snprintf(prefix + strlen(prefix), sizeof(prefix) - strlen(prefix) - 1, "[%-.7s] ", label ? label : "");

时间戳格式设定:

[2025-12-29 15:30:45] [label] (1234) : 消息内容

兼容性:

完全保留原有函数的入参、上下文逻辑、日志写入、控制台输出行为,仅新增时间戳前缀,无需修改调用该函数的其他代码。

**修改源码位置:**haproxy-3.2/src/errors.c文件

代码开头添加头文件:

#include <sys/time.h> // 用于gettimeofday
#include <time.h> // 用于localtime_r/tm
#include <string.h> // 用于strncpy/strlen

...

/* Generic function to display messages prefixed by a label + timestamp */

static void print_message(int use_usermsgs_ctx, const char *label, const char *fmt, va_list argp)

{

struct ist msg_ist = IST_NULL;

char *head, *parsing_str, *msg;

char prefix[64] = {0};

struct timeval tv;

struct tm tm_info;

char time_buf[32] = {0};

gettimeofday(&tv, NULL);

localtime_r(&tv.tv_sec, &tm_info);

snprintf(time_buf, sizeof(time_buf),

"[%04d-%02d-%02d %02d:%02d:%02d]",

tm_info.tm_year + 1900, tm_info.tm_mon + 1, tm_info.tm_mday,

tm_info.tm_hour, tm_info.tm_min, tm_info.tm_sec);

strncpy(prefix, time_buf, sizeof(prefix) - 1);

snprintf(prefix + strlen(prefix), sizeof(prefix) - strlen(prefix) - 1,

"[%-.7s] ", label ? label : "");

...

日志配置:

global

log 127.0.0.1 local3 info

maxconn 700

ulimit-n 1048576

daemon

stats socket /usr/local/stats.sock mode 666 level admin

pidfile /var/run/haproxy.pid

nbthread 8

defaults

mode tcp

retries 6

timeout queue 1m

timeout connect 10s

timeout client 3601s

timeout server 3601s

timeout check 10s

log global

option tcplog

option dontlognull

option redispatch

resolvers mysql-resolver

parse-resolv-conf

resolve_retries 6

timeout retry 3s

hold valid 10s

hold nx 10s

hold timeout 10s

hold refused 10s

hold other 10s

listen inter_listen

bind :::3306 v4v6

mode tcp

option tcpka

balance roundrobin

server mysql1 abe601669c2-8665a0-headless.971a0e06.svc.cluster.local:3306 check inter 30000 fall 3 rise 3 resolvers mysql-resolver

改造后日志输出:

连通性测试验证:

优化源码后的HAPrxoy可以达到日志打印时间信息目的,并避免修改引入缓冲区溢出和线程安全等问题。