feat: enhance observability metrics collection and reporting

- Updated BuildSnapshot function to include OpenResty metrics.
- Enhanced BuildTrafficReport to utilize managed OpenResty metrics.
- Introduced new functions for parsing access logs in both JSON and combined formats.
- Added tests for traffic report generation from combined access logs.
- Implemented local observability metrics collection from OpenResty.
- Created Lua scripts for OpenResty to gather observability data.
- Updated configuration documentation to include new observability port and settings.
- Added support for OpenResty observability in the server configuration.
This commit is contained in:
ryan
2026-03-14 17:07:47 +08:00
parent 8cc839669c
commit bdfa80f214
19 changed files with 800 additions and 184 deletions
+61 -31
View File
@@ -20,6 +20,8 @@ import (
const CertDirPlaceholder = "__ATSF_CERT_DIR__"
const RouteConfigPlaceholder = "__ATSF_ROUTE_CONFIG__"
const AccessLogPlaceholder = "__ATSF_ACCESS_LOG__"
const LuaDirPlaceholder = "__ATSF_LUA_DIR__"
const ObservabilityPortPlaceholder = "__ATSF_OBSERVABILITY_PORT__"
const DockerMainConfigPath = "/usr/local/openresty/nginx/conf/nginx.conf"
const DockerRouteConfigPath = "/etc/nginx/conf.d/atsflare_routes.conf"
const DockerAccessLogPath = "/etc/nginx/conf.d/atsflare_access.log"
@@ -97,14 +99,15 @@ func (e *PathExecutor) Restart(ctx context.Context) error {
}
type DockerExecutor struct {
DockerBinary string
ContainerName string
Image string
MainConfigPath string
RouteConfigDir string
CertDir string
NginxCertDir string
Runner CommandRunner
DockerBinary string
ContainerName string
Image string
MainConfigPath string
RouteConfigDir string
CertDir string
NginxCertDir string
OpenrestyObservabilityPort int
Runner CommandRunner
}
func (e *DockerExecutor) Test(ctx context.Context) error {
@@ -180,6 +183,7 @@ func (e *DockerExecutor) runContainer(ctx context.Context) error {
"--name", e.ContainerName,
"-p", "80:80",
"-p", "443:443",
"-p", fmt.Sprintf("127.0.0.1:%d:%d", e.OpenrestyObservabilityPort, e.OpenrestyObservabilityPort),
"-v", fmt.Sprintf("%s:%s", e.MainConfigPath, DockerMainConfigPath),
"-v", fmt.Sprintf("%s:/etc/nginx/conf.d", e.RouteConfigDir),
"-v", fmt.Sprintf("%s:%s", e.CertDir, e.NginxCertDir),
@@ -194,12 +198,13 @@ func (e *DockerExecutor) runContainer(ctx context.Context) error {
}
type Manager struct {
MainConfigPath string
RouteConfigPath string
RuntimeRouteConfigPath string
CertDir string
NginxCertDir string
Executor Executor
MainConfigPath string
RouteConfigPath string
RuntimeRouteConfigPath string
CertDir string
NginxCertDir string
OpenrestyObservabilityPort int
Executor Executor
}
func (m *Manager) Apply(ctx context.Context, mainConfig string, routeConfig string, supportFiles []protocol.SupportFile) error {
@@ -290,6 +295,12 @@ func (m *Manager) CurrentChecksum() (string, error) {
if accessLogPath := m.accessLogRuntimePath(); accessLogPath != "" {
normalizedMain = strings.ReplaceAll(normalizedMain, accessLogPath, AccessLogPlaceholder)
}
if luaDir := m.luaRuntimePath(); luaDir != "" {
normalizedMain = strings.ReplaceAll(normalizedMain, luaDir, LuaDirPlaceholder)
}
if m.OpenrestyObservabilityPort > 0 {
normalizedMain = strings.ReplaceAll(normalizedMain, fmt.Sprintf("%d", m.OpenrestyObservabilityPort), ObservabilityPortPlaceholder)
}
normalizedRoute := string(data)
if m.NginxCertDir != "" {
normalizedRoute = strings.ReplaceAll(normalizedRoute, m.NginxCertDir, CertDirPlaceholder)
@@ -304,14 +315,15 @@ func (m *Manager) CurrentChecksum() (string, error) {
}
type ExecutorOptions struct {
NginxPath string
DockerBinary string
ContainerName string
Image string
MainConfigPath string
RouteConfigPath string
CertDir string
NginxCertDir string
NginxPath string
DockerBinary string
ContainerName string
Image string
MainConfigPath string
RouteConfigPath string
CertDir string
NginxCertDir string
OpenrestyObservabilityPort int
}
func NewExecutor(options ExecutorOptions) Executor {
@@ -335,14 +347,15 @@ func NewExecutor(options ExecutorOptions) Executor {
certDir = absDir
}
return &DockerExecutor{
DockerBinary: options.DockerBinary,
ContainerName: options.ContainerName,
Image: options.Image,
MainConfigPath: mainConfigPath,
RouteConfigDir: routeConfigDir,
CertDir: certDir,
NginxCertDir: options.NginxCertDir,
Runner: runner,
DockerBinary: options.DockerBinary,
ContainerName: options.ContainerName,
Image: options.Image,
MainConfigPath: mainConfigPath,
RouteConfigDir: routeConfigDir,
CertDir: certDir,
NginxCertDir: options.NginxCertDir,
OpenrestyObservabilityPort: options.OpenrestyObservabilityPort,
Runner: runner,
}
}
@@ -588,7 +601,11 @@ func (m *Manager) supportFileTargetPath(relativePath string) (string, error) {
if strings.TrimSpace(m.CertDir) == "" {
return "", errors.New("cert dir 不能为空")
}
normalizedPath := filepath.Clean(filepath.FromSlash(strings.TrimSpace(relativePath)))
candidate := strings.TrimSpace(relativePath)
if strings.Contains(candidate, `\`) {
candidate = strings.ReplaceAll(candidate, `\`, "/")
}
normalizedPath := filepath.Clean(filepath.FromSlash(candidate))
if normalizedPath == "." || normalizedPath == "" {
return "", errors.New("support file path 不能为空")
}
@@ -621,6 +638,12 @@ func (m *Manager) renderMainConfig(content string) string {
if accessLogPath := m.accessLogRuntimePath(); accessLogPath != "" {
rendered = strings.ReplaceAll(rendered, AccessLogPlaceholder, accessLogPath)
}
if luaDir := m.luaRuntimePath(); luaDir != "" {
rendered = strings.ReplaceAll(rendered, LuaDirPlaceholder, luaDir)
}
if m.OpenrestyObservabilityPort > 0 {
rendered = strings.ReplaceAll(rendered, ObservabilityPortPlaceholder, fmt.Sprintf("%d", m.OpenrestyObservabilityPort))
}
return rendered
}
@@ -639,6 +662,13 @@ func (m *Manager) accessLogRuntimePath() string {
return filepath.ToSlash(filepath.Join(filepath.Dir(includePath), "atsflare_access.log"))
}
func (m *Manager) luaRuntimePath() string {
if strings.TrimSpace(m.NginxCertDir) == "" {
return ""
}
return filepath.ToSlash(m.NginxCertDir)
}
func checksum(content string) string {
sum := sha256.Sum256([]byte(content))
return hex.EncodeToString(sum[:])
+27 -23
View File
@@ -210,14 +210,15 @@ func TestDockerExecutorRunContainerMountsManagedFiles(t *testing.T) {
certDir := filepath.Clean("/tmp/managed/certs")
runner := &fakeRunner{}
executor := &DockerExecutor{
DockerBinary: "docker",
ContainerName: "atsflare-openresty",
Image: "openresty/openresty:alpine",
MainConfigPath: mainConfigPath,
RouteConfigDir: routeConfigDir,
CertDir: certDir,
NginxCertDir: "/etc/nginx/atsflare-certs",
Runner: runner,
DockerBinary: "docker",
ContainerName: "atsflare-openresty",
Image: "openresty/openresty:alpine",
MainConfigPath: mainConfigPath,
RouteConfigDir: routeConfigDir,
CertDir: certDir,
NginxCertDir: "/etc/nginx/atsflare-certs",
OpenrestyObservabilityPort: 18081,
Runner: runner,
}
if err := executor.runContainer(context.Background()); err != nil {
@@ -233,6 +234,7 @@ func TestDockerExecutorRunContainerMountsManagedFiles(t *testing.T) {
"--name", "atsflare-openresty",
"-p", "80:80",
"-p", "443:443",
"-p", "127.0.0.1:18081:18081",
"-v", mainConfigPath + ":" + DockerMainConfigPath,
"-v", routeConfigDir + ":/etc/nginx/conf.d",
"-v", certDir + ":/etc/nginx/atsflare-certs",
@@ -253,14 +255,15 @@ func TestDockerExecutorRecreatesContainerOnStartup(t *testing.T) {
},
}
executor := &DockerExecutor{
DockerBinary: "docker",
ContainerName: "atsflare-openresty",
Image: "openresty/openresty:alpine",
MainConfigPath: filepath.Clean("/tmp/nginx.conf"),
RouteConfigDir: filepath.Clean("/tmp/routes"),
CertDir: filepath.Clean("/tmp/certs"),
NginxCertDir: "/etc/nginx/atsflare-certs",
Runner: runner,
DockerBinary: "docker",
ContainerName: "atsflare-openresty",
Image: "openresty/openresty:alpine",
MainConfigPath: filepath.Clean("/tmp/nginx.conf"),
RouteConfigDir: filepath.Clean("/tmp/routes"),
CertDir: filepath.Clean("/tmp/certs"),
NginxCertDir: "/etc/nginx/atsflare-certs",
OpenrestyObservabilityPort: 18081,
Runner: runner,
}
if err := executor.EnsureRuntime(context.Background(), true); err != nil {
@@ -279,13 +282,14 @@ func TestDockerExecutorRecreatesContainerOnStartup(t *testing.T) {
func TestNewExecutorUsesAbsoluteDockerMountPath(t *testing.T) {
executor := NewExecutor(ExecutorOptions{
DockerBinary: "docker",
ContainerName: "atsflare-openresty",
Image: "openresty/openresty:alpine",
MainConfigPath: "./data/etc/nginx/nginx.conf",
RouteConfigPath: "./data/etc/nginx/conf.d/atsflare_routes.conf",
CertDir: "./data/etc/nginx/certs",
NginxCertDir: "/etc/nginx/atsflare-certs",
DockerBinary: "docker",
ContainerName: "atsflare-openresty",
Image: "openresty/openresty:alpine",
MainConfigPath: "./data/etc/nginx/nginx.conf",
RouteConfigPath: "./data/etc/nginx/conf.d/atsflare_routes.conf",
CertDir: "./data/etc/nginx/certs",
NginxCertDir: "/etc/nginx/atsflare-certs",
OpenrestyObservabilityPort: 18081,
})
dockerExecutor, ok := executor.(*DockerExecutor)