|
@@ -0,0 +1,222 @@
|
|
|
|
+package main
|
|
|
|
+
|
|
|
|
+import (
|
|
|
|
+ "net/http"
|
|
|
|
+
|
|
|
|
+ log "github.com/Sirupsen/logrus"
|
|
|
|
+ "github.com/prometheus/client_golang/prometheus/promhttp"
|
|
|
|
+ "github.com/prometheus/client_golang/prometheus"
|
|
|
|
+
|
|
|
|
+ "flag"
|
|
|
|
+ "fmt"
|
|
|
|
+ "os"
|
|
|
|
+ "io/ioutil"
|
|
|
|
+ "strings"
|
|
|
|
+ "regexp"
|
|
|
|
+
|
|
|
|
+ "database/sql"
|
|
|
|
+ _ "github.com/go-sql-driver/mysql"
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
+//This is my collector metrics
|
|
|
|
+type wpCollector struct {
|
|
|
|
+ numPostsMetric *prometheus.Desc
|
|
|
|
+ numCommentsMetric *prometheus.Desc
|
|
|
|
+ numUsersMetric *prometheus.Desc
|
|
|
|
+
|
|
|
|
+ db_host string
|
|
|
|
+ db_name string
|
|
|
|
+ db_user string
|
|
|
|
+ db_pass string
|
|
|
|
+ db_table_prefix string
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+//This is a constructor for my wpCollector struct
|
|
|
|
+func newWordPressCollector(host string, dbname string, username string, pass string, table_prefix string) *wpCollector {
|
|
|
|
+ return &wpCollector{
|
|
|
|
+ numPostsMetric: prometheus.NewDesc("wp_num_posts_metric",
|
|
|
|
+ "Shows the number of total posts in the WordPress site",
|
|
|
|
+ nil, nil,
|
|
|
|
+ ),
|
|
|
|
+ numCommentsMetric: prometheus.NewDesc("wp_num_comments_metric",
|
|
|
|
+ "Shows the number of total comments in the WordPress site",
|
|
|
|
+ nil, nil,
|
|
|
|
+ ),
|
|
|
|
+ numUsersMetric: prometheus.NewDesc("wp_num_users_metric",
|
|
|
|
+ "Shows the number of registered users in the WordPress site",
|
|
|
|
+ nil, nil,
|
|
|
|
+ ),
|
|
|
|
+
|
|
|
|
+ db_host: host,
|
|
|
|
+ db_name: dbname,
|
|
|
|
+ db_user: username,
|
|
|
|
+ db_pass: pass,
|
|
|
|
+ db_table_prefix: table_prefix,
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+//Describe method is required for a prometheus.Collector type
|
|
|
|
+func (collector *wpCollector) Describe(ch chan<- *prometheus.Desc) {
|
|
|
|
+
|
|
|
|
+ //We set the metrics
|
|
|
|
+ ch <- collector.numPostsMetric
|
|
|
|
+ ch <- collector.numCommentsMetric
|
|
|
|
+ ch <- collector.numUsersMetric
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+//Collect method is required for a prometheus.Collector type
|
|
|
|
+func (collector *wpCollector) Collect(ch chan<- prometheus.Metric) {
|
|
|
|
+
|
|
|
|
+ //We run DB queries here to retrieve the metrics we care about
|
|
|
|
+ dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s", collector.db_user, collector.db_pass, collector.db_host, collector.db_name)
|
|
|
|
+
|
|
|
|
+ db, err := sql.Open("mysql", dsn)
|
|
|
|
+ if(err != nil){
|
|
|
|
+ fmt.Fprintf(os.Stderr, "Error connecting to database: %s ...\n", err)
|
|
|
|
+ os.Exit(1)
|
|
|
|
+ }
|
|
|
|
+ defer db.Close()
|
|
|
|
+
|
|
|
|
+ //select count(*) as num_users from wp_users;
|
|
|
|
+ var num_users float64
|
|
|
|
+ q1 := fmt.Sprintf("select count(*) as num_users from %susers;", collector.db_table_prefix)
|
|
|
|
+ err = db.QueryRow(q1).Scan(&num_users)
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Fatal(err)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ //select count(*) as num_comments from wp_comments;
|
|
|
|
+ var num_comments float64
|
|
|
|
+ q2 := fmt.Sprintf("select count(*) as num_comments from %scomments;", collector.db_table_prefix)
|
|
|
|
+ err = db.QueryRow(q2).Scan(&num_comments)
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Fatal(err)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ //select count(*) as num_posts from wp_posts WHERE post_type='post' AND post_status!='auto-draft';
|
|
|
|
+ var num_posts float64
|
|
|
|
+ q3 := fmt.Sprintf("select count(*) as num_posts from %sposts WHERE post_type='post' AND post_status!='auto-draft';", collector.db_table_prefix)
|
|
|
|
+ err = db.QueryRow(q3).Scan(&num_posts)
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Fatal(err)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ //Write latest value for each metric in the prometheus metric channel.
|
|
|
|
+ //Note that you can pass CounterValue, GaugeValue, or UntypedValue types here.
|
|
|
|
+ ch <- prometheus.MustNewConstMetric(collector.numPostsMetric, prometheus.CounterValue, num_posts)
|
|
|
|
+ ch <- prometheus.MustNewConstMetric(collector.numCommentsMetric, prometheus.CounterValue, num_comments)
|
|
|
|
+ ch <- prometheus.MustNewConstMetric(collector.numUsersMetric, prometheus.CounterValue, num_users)
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func main() {
|
|
|
|
+
|
|
|
|
+ wpConfPtr := flag.String("wpconfig", "", "Path for wp-config.php file of the WordPress site you wish to monitor")
|
|
|
|
+ wpHostPtr := flag.String("host", "127.0.0.1", "Hostname or Address for DB server")
|
|
|
|
+ wpPortPtr := flag.String("port", "3306", "DB server port")
|
|
|
|
+ wpNamePtr := flag.String("db", "", "DB name")
|
|
|
|
+ wpUserPtr := flag.String("user", "", "DB user for connection")
|
|
|
|
+ wpPassPtr := flag.String("pass", "", "DB password for connection")
|
|
|
|
+ wpTablePrefixPtr := flag.String("tableprefix", "wp_", "Table prefix for WordPress tables")
|
|
|
|
+
|
|
|
|
+ flag.Parse()
|
|
|
|
+
|
|
|
|
+ if *wpConfPtr == "" {
|
|
|
|
+ db_host := fmt.Sprintf("%s:%s", *wpHostPtr, *wpPortPtr)
|
|
|
|
+ db_name := *wpNamePtr
|
|
|
|
+ db_user := *wpUserPtr
|
|
|
|
+ db_password := *wpPassPtr
|
|
|
|
+ table_prefix := *wpTablePrefixPtr
|
|
|
|
+
|
|
|
|
+ if db_name == "" {
|
|
|
|
+ fmt.Fprintf(os.Stderr, "flag -db=dbname required!\n")
|
|
|
|
+ os.Exit(1)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if db_user == "" {
|
|
|
|
+ fmt.Fprintf(os.Stderr, "flag -user=username required!\n")
|
|
|
|
+ os.Exit(1)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ //We create the collector
|
|
|
|
+ collector := newWordPressCollector(db_host, db_name, db_user, db_password, table_prefix)
|
|
|
|
+ prometheus.MustRegister(collector)
|
|
|
|
+
|
|
|
|
+ //no path supplied error
|
|
|
|
+ //fmt.Fprintf(os.Stderr, "flag -wpconfig=/path/to/wp-config/ required!\n")
|
|
|
|
+ //os.Exit(1)
|
|
|
|
+ } else{
|
|
|
|
+ var wpconfig_file strings.Builder
|
|
|
|
+ wpconfig_file.WriteString(*wpConfPtr)
|
|
|
|
+
|
|
|
|
+ if strings.HasSuffix(*wpConfPtr, "/") {
|
|
|
|
+ wpconfig_file.WriteString("wp-config.php")
|
|
|
|
+ }else{
|
|
|
|
+ wpconfig_file.WriteString("/wp-config.php")
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ //try to read wp-config.php file from path
|
|
|
|
+ dat, err := ioutil.ReadFile(wpconfig_file.String())
|
|
|
|
+ if(err != nil){
|
|
|
|
+ panic(err)
|
|
|
|
+ }
|
|
|
|
+ fmt.Printf("Read :%v bytes\n", len(dat))
|
|
|
|
+
|
|
|
|
+ //We must locate with regular expressions the MySQL connection credentials and the table prefix
|
|
|
|
+ //define('DB_HOST', 'xxxxxxx');
|
|
|
|
+ r, _ := regexp.Compile(`define\(['"]DB_HOST['"].*?,.*?['"](.*?)['"].*?\);`)
|
|
|
|
+ res := r.FindStringSubmatch(string(dat[:len(dat)]))
|
|
|
|
+ if(res == nil){
|
|
|
|
+ fmt.Fprintf(os.Stderr, "Error could not find DB_HOST in wp-config.php ...\n")
|
|
|
|
+ os.Exit(1)
|
|
|
|
+ }
|
|
|
|
+ db_host := res[1]
|
|
|
|
+
|
|
|
|
+ //define('DB_NAME', 'xxxxxxx');
|
|
|
|
+ r, _ = regexp.Compile(`define\(['"]DB_NAME['"].*?,.*?['"](.*?)['"].*?\);`)
|
|
|
|
+ res = r.FindStringSubmatch(string(dat[:len(dat)]))
|
|
|
|
+ if(res == nil){
|
|
|
|
+ fmt.Fprintf(os.Stderr, "Error could not find DB_NAME in wp-config.php ...\n")
|
|
|
|
+ os.Exit(1)
|
|
|
|
+ }
|
|
|
|
+ db_name := res[1]
|
|
|
|
+
|
|
|
|
+ //define('DB_USER', 'xxxxxxx');
|
|
|
|
+ r, _ = regexp.Compile(`define\(['"]DB_USER['"].*?,.*?['"](.*?)['"].*?\);`)
|
|
|
|
+ res = r.FindStringSubmatch(string(dat[:len(dat)]))
|
|
|
|
+ if(res == nil){
|
|
|
|
+ fmt.Fprintf(os.Stderr, "Error could not find DB_USER in wp-config.php ...\n")
|
|
|
|
+ os.Exit(1)
|
|
|
|
+ }
|
|
|
|
+ db_user := res[1]
|
|
|
|
+
|
|
|
|
+ //define('DB_PASSWORD', 'xxxxxxx');
|
|
|
|
+ r, _ = regexp.Compile(`define\(['"]DB_PASSWORD['"].*?,.*?['"](.*?)['"].*?\);`)
|
|
|
|
+ res = r.FindStringSubmatch(string(dat[:len(dat)]))
|
|
|
|
+ if(res == nil){
|
|
|
|
+ fmt.Fprintf(os.Stderr, "Error could not find DB_PASSWORD in wp-config.php ...\n")
|
|
|
|
+ os.Exit(1)
|
|
|
|
+ }
|
|
|
|
+ db_password := res[1]
|
|
|
|
+
|
|
|
|
+ //$table_prefix = 'wp_';
|
|
|
|
+ r, _ = regexp.Compile(`\$table_prefix.*?=.*?['"](.*?)['"];`)
|
|
|
|
+ res = r.FindStringSubmatch(string(dat[:len(dat)]))
|
|
|
|
+ if(res == nil){
|
|
|
|
+ fmt.Fprintf(os.Stderr, "Error could not find $table_prefix in wp-config.php ...\n")
|
|
|
|
+ os.Exit(1)
|
|
|
|
+ }
|
|
|
|
+ table_prefix := res[1]
|
|
|
|
+
|
|
|
|
+ //We create the collector
|
|
|
|
+ collector := newWordPressCollector(db_host, db_name, db_user, db_password, table_prefix)
|
|
|
|
+ prometheus.MustRegister(collector)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ //This section will start the HTTP server and expose
|
|
|
|
+ //any metrics on the /metrics endpoint.
|
|
|
|
+ http.Handle("/metrics", promhttp.Handler())
|
|
|
|
+ log.Info("Beginning to serve on port :8888")
|
|
|
|
+ log.Fatal(http.ListenAndServe(":8888", nil))
|
|
|
|
+}
|
|
|
|
+
|