package main import ( "context" "log/slog" "net/http" "os" "xugu_exporter/collector" _ "gitee.com/XuguDB/go-xugu-driver" kingpin "github.com/alecthomas/kingpin/v2" "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/collectors" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/common/promlog" "github.com/prometheus/common/promlog/flag" "github.com/prometheus/common/version" "github.com/prometheus/exporter-toolkit/web" webflag "github.com/prometheus/exporter-toolkit/web/kingpinflag" // Required for debugging // _ "net/http/pprof" ) var ( // Version will be set at build time. Version = "0.0.1.dev" // 版本号,通常在构建时设置。这里初始化为一个开发版本号。 // 使用 Kingpin 库定义命令行标志,并为每个标志提供帮助信息和默认值。 // metricPath 定义了暴露指标的路径。默认值来自环境变量 "TELEMETRY_PATH",若未设置则默认为 "/metrics"。 metricPath = kingpin.Flag("web.telemetry-path", "Path under which to expose metrics. (env: TELEMETRY_PATH)").Default(getEnv("TELEMETRY_PATH", "/metrics")).String() // dsn 定义了数据库的连接字符串。默认值来自环境变量 "DATA_SOURCE_NAME",若未设置则默认为空字符串。 dsn = kingpin.Flag("database.dsn", "Connection string to a data source. (env: DATA_SOURCE_NAME)", ).Default(getEnv("DATA_SOURCE_NAME", "")).String() // dsnFile 定义了从文件读取数据库连接字符串的路径。默认值来自环境变量 "DATA_SOURCE_NAME_FILE",若未设置则默认为空字符串。 dsnFile = kingpin.Flag("database.dsnFile", "File to read a string to a data source from. (env: DATA_SOURCE_NAME_FILE)", ).Default(getEnv("DATA_SOURCE_NAME_FILE", "")).String() // defaultFileMetrics 定义了存储默认指标的文件路径。该文件支持 TOML 或 YAML 格式。默认值来自环境变量 "DEFAULT_METRICS",若未设置则默认为 "default-metrics.toml"。 defaultFileMetrics = kingpin.Flag( "default.metrics", "File with default metrics in a toml format. (env: DEFAULT_METRICS)", ).Default(getEnv("DEFAULT_METRICS", "default-metrics.toml")).String() // customMetrics 定义了包含自定义指标的文件路径。该文件支持 TOML 或 YAML 格式。默认值来自环境变量 "CUSTOM_METRICS",若未设置则默认为空字符串。 customMetrics = kingpin.Flag( "custom.metrics", "File that may contain various custom metrics in a toml format. (env: CUSTOM_METRICS)", ).Default(getEnv("CUSTOM_METRICS", "")).String() // queryTimeout 定义了查询的超时时间,单位为秒。默认值来自环境变量 "QUERY_TIMEOUT",若未设置则默认为 5 秒。 queryTimeout = kingpin.Flag( "query.timeout", "Query timeout (in seconds). (env: QUERY_TIMEOUT)", ).Default(getEnv("QUERY_TIMEOUT", "5")).Int() // maxIdleConns 定义了连接池中最大空闲连接数。默认值来自环境变量 "DATABASE_MAXIDLECONNS",若未设置则默认为 0。 maxIdleConns = kingpin.Flag( "database.maxIdleConns", "Number of maximum idle connections in the connection pool. (env: DATABASE_MAXIDLECONNS)", ).Default(getEnv("DATABASE_MAXIDLECONNS", "0")).Int() // maxOpenConns 定义了连接池中最大打开连接数。默认值来自环境变量 "DATABASE_MAXOPENCONNS",若未设置则默认为 10。 maxOpenConns = kingpin.Flag( "database.maxOpenConns", "Number of maximum open connections in the connection pool. (env: DATABASE_MAXOPENCONNS)", ).Default(getEnv("DATABASE_MAXOPENCONNS", "10")).Int() // scrapeInterval 定义了每次抓取数据的间隔时间。默认值为 0,表示在每次收集请求时进行抓取。 scrapeInterval = kingpin.Flag( "scrape.interval", "Interval between each scrape. Default is to scrape on collect requests", ).Default("0s").Duration() // toolkitFlags 是与 Web 相关的附加标志,添加到 `kingpin.CommandLine` 上,默认端口为 ":9161"。 toolkitFlags = webflag.AddFlags(kingpin.CommandLine, ":9161") ) func main() { // 创建 Prometheus 日志配置对象 promLogConfig := &promlog.Config{} // 添加命令行标志以配置 Prometheus 日志 flag.AddFlags(kingpin.CommandLine, promLogConfig) // 设置帮助标志的快捷键 kingpin.HelpFlag.Short('\n') // 设置程序版本信息 kingpin.Version(version.Print("xugudb_exporter")) // 解析命令行输入的参数 kingpin.Parse() // 创建新的日志记录器 logger := promlog.New(promLogConfig) // 如果提供了 dsnFile 参数,读取文件中的数据库连接信息 if dsnFile != nil && *dsnFile != "" { dsnFileContent, err := os.ReadFile(*dsnFile) if err != nil { // 如果文件读取失败,记录错误并退出程序 level.Error(logger).Log("msg", "Unable to read DATA_SOURCE_NAME_FILE", "file", dsnFile, "error", err) os.Exit(1) } // 将文件内容作为数据库连接字符串 *dsn = string(dsnFileContent) } // 初始化配置对象,将从命令行解析的参数赋值给配置 config := &collector.Config{ DSN: *dsn, // 数据库连接字符串 MaxOpenConns: *maxOpenConns, // 最大打开连接数 MaxIdleConns: *maxIdleConns, // 最大空闲连接数 CustomMetrics: *customMetrics, // 自定义指标文件路径 QueryTimeout: *queryTimeout, // 查询超时时间 DefaultMetricsFile: *defaultFileMetrics, // 默认指标文件路径 } // 创建一个新的 Exporter 实例,用于收集 xugu 数据库的指标 exporter, err := collector.NewExporter(logger, config) if err != nil { // 如果创建 Exporter 失败,记录错误并退出程序 level.Error(logger).Log("unable to connect to DB", err) } // 如果设置了抓取间隔(scrapeInterval),则启动定时抓取指标的任务 if *scrapeInterval != 0 { ctx, cancel := context.WithCancel(context.Background()) defer cancel() // 启动一个 goroutine,定期从数据库抓取指标 go exporter.RunScheduledScrapes(ctx, *scrapeInterval) } // 将 Exporter 注册到 Prometheus 中,供 Prometheus 进行指标抓取 prometheus.MustRegister(exporter) // 注册构建信息收集器,用于报告程序的版本信息等元数据 prometheus.MustRegister(collectors.NewBuildInfoCollector()) // 记录启动信息 level.Info(logger).Log("msg", "Starting xugudb_exporter", "version", version.Info()) level.Info(logger).Log("msg", "Build context", "build", version.BuildContext()) level.Info(logger).Log("msg", "Collect from: ", "metricPath", *metricPath) // 设置 Prometheus HTTP handler 的选项 opts := promhttp.HandlerOpts{ ErrorHandling: promhttp.ContinueOnError, // 遇到错误时继续处理其他请求 } // 配置 /metrics 路径用于暴露 Prometheus 格式的指标 http.Handle(*metricPath, promhttp.HandlerFor(prometheus.DefaultGatherer, opts)) // 配置根路径(/),返回一个简单的 HTML 页面,包含版本信息和指标链接 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("<html><head><title>xugu DB Exporter " + Version + "</title></head><body><h1>xugu DB Exporter " + Version + "</h1><p><a href='" + *metricPath + "'>Metrics</a></p></body></html>")) }) // 创建一个日志处理器,输出到标准输出(os.Stdout) handlerS := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ Level: slog.LevelInfo, // 设置最低日志级别为 Info }) // 创建一个新的 logger loggerS := slog.New(handlerS) // 创建一个新的 logger //loggerS := slog.New(slog.NewTextHandler(slog.Default())) // 使用 logger 记录日志 loggerS.Info("This is an info message") loggerS.Warn("This is a warning message") loggerS.Error("This is an error message") // 如果需要,可以添加更多的上下文信息 loggerS.With("context", "example").Info("This is a context-aware info message") // 创建一个 HTTP 服务器实例 server := &http.Server{} // 启动 HTTP 服务器,监听并提供 Prometheus 指标 if err := web.ListenAndServe(server, toolkitFlags, loggerS); err != nil { // 如果服务器启动失败,记录错误并退出程序 level.Error(logger).Log("msg", "Listening error", "reason", err) os.Exit(1) } } // getEnv 返回指定环境变量的值,如果环境变量未设置,则返回提供的备用值。 func getEnv(key, fallback string) string { // os.LookupEnv 用于查找环境变量的值。如果环境变量存在,返回值和一个布尔值(true/false); // 如果环境变量不存在,返回空字符串和 false。 if value, ok := os.LookupEnv(key); ok { // 如果环境变量存在,返回其值 return value } // 如果环境变量不存在,返回备用值 return fallback }