353 lines
7.8 KiB
Go
353 lines
7.8 KiB
Go
package main
|
|
|
|
import (
|
|
"database/sql"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"text/template"
|
|
"time"
|
|
|
|
"github.com/go-echarts/go-echarts/v2/charts"
|
|
"github.com/go-echarts/go-echarts/v2/opts"
|
|
_ "github.com/go-sql-driver/mysql"
|
|
ini "gopkg.in/ini.v1"
|
|
)
|
|
|
|
var verb bool = false
|
|
var file string = ""
|
|
var cfg *ini.File
|
|
|
|
type DataSet struct {
|
|
Alias string
|
|
Item string
|
|
Result string
|
|
UpdateTime string
|
|
}
|
|
|
|
func main() {
|
|
flag.BoolVar(&verb, "v", false, "Show Verbose Debug Messages")
|
|
flag.StringVar(&file, "f", "", "Config File Path")
|
|
flag.Parse()
|
|
|
|
log.SetOutput(os.Stdout)
|
|
log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)
|
|
|
|
if file == "" {
|
|
file = strings.TrimSuffix(os.Args[0], ".exe") + ".ini"
|
|
}
|
|
|
|
var err error
|
|
cfg, err = ini.Load(file)
|
|
if err != nil {
|
|
log.Printf("[ERR] %v\r\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
addr := cfg.Section("HTTP").Key("ListenIP").String()
|
|
port := cfg.Section("HTTP").Key("ListenPort").String()
|
|
http.HandleFunc("/", handleIndex)
|
|
http.HandleFunc("/echart/", eChart)
|
|
http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("./assets"))))
|
|
http.ListenAndServe(addr+":"+port, nil)
|
|
}
|
|
|
|
func handleIndex(w http.ResponseWriter, r *http.Request) {
|
|
if r.RequestURI == "/favicon.ico" {
|
|
return
|
|
}
|
|
log.Printf("[MSG] Received Request: %s, %s\r\n", r.RemoteAddr, r.RequestURI)
|
|
defer r.Body.Close()
|
|
|
|
type Data struct {
|
|
Title string
|
|
Host string
|
|
}
|
|
data := Data{
|
|
Title: cfg.Section("HTTP").Key("Title").String(),
|
|
Host: cfg.Section("HTTP").Key("Host").String(),
|
|
}
|
|
tpl, err := template.ParseFiles("root.html")
|
|
if err != nil {
|
|
log.Printf("[ERR] %v\r\n", err)
|
|
return
|
|
}
|
|
err = tpl.Execute(w, data)
|
|
if err != nil {
|
|
log.Printf("[ERR] %v\r\n", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func eChart(w http.ResponseWriter, r *http.Request) {
|
|
if r.RequestURI == "/favicon.ico" {
|
|
return
|
|
}
|
|
fmt.Println()
|
|
log.Printf("[MSG] Received Request: %s, %s\r\n", r.RemoteAddr, r.RequestURI)
|
|
defer r.Body.Close()
|
|
|
|
if err := r.ParseForm(); err != nil {
|
|
log.Printf("[ERR] %v\r\n", err)
|
|
}
|
|
|
|
type Params struct {
|
|
Range string ""
|
|
Alias string ""
|
|
Class string ""
|
|
Scope string ""
|
|
}
|
|
var params Params
|
|
params.Range = r.Form.Get("range")
|
|
params.Scope = r.Form.Get("scope")
|
|
params.Alias = r.Form.Get("alias")
|
|
params.Class = r.Form.Get("class")
|
|
log.Printf("[MSG] Request Params: %#v\r\n", params)
|
|
|
|
timeAxis := make([]string, 0)
|
|
endTime := time.Now()
|
|
beginTime := endTime.AddDate(0, 0, -30)
|
|
bt := beginTime
|
|
for i := 1; i <= 60*24*30; i++ {
|
|
bt = bt.Add(time.Minute)
|
|
timeAxis = append(timeAxis, bt.Format("01-02 15:04"))
|
|
}
|
|
|
|
// Parsing Config - Database
|
|
cfg.Reload()
|
|
dbHost := cfg.Section("Database").Key("Host").MustString("127.0.0.1")
|
|
dbPort := cfg.Section("Database").Key("Port").MustString("3306")
|
|
dbSchema := cfg.Section("Database").Key("Database").MustString("monitoring")
|
|
dbTable := cfg.Section("Database").Key("Table").MustString("server")
|
|
dbColumns := cfg.Section("Database").Key("Columns").MustString("alias,item,result,update_time")
|
|
dbUsername := cfg.Section("Database").Key("Username").String()
|
|
dbPassword := cfg.Section("Database").Key("Password").String()
|
|
|
|
if dbUsername == "" || dbPassword == "" {
|
|
log.Println("[ERR] Invalid Database Configuration.")
|
|
return
|
|
}
|
|
|
|
qry := fmt.Sprintf(
|
|
"SELECT %s FROM %s WHERE alias='%s' AND item='%s' AND update_stmp>=%d ORDER BY seqid ASC;",
|
|
dbColumns,
|
|
dbTable,
|
|
params.Alias,
|
|
params.Class,
|
|
beginTime.Unix(),
|
|
)
|
|
log.Printf("[MSG] Query SQL: \r\n%s\r\n", qry)
|
|
|
|
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", dbUsername, dbPassword, dbHost, dbPort, dbSchema)
|
|
conn, err := sql.Open("mysql", dsn)
|
|
if err != nil {
|
|
log.Printf("[ERR] %v\r\n", err)
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
|
|
if err := conn.Ping(); err != nil {
|
|
log.Printf("[ERR] %v\r\n", err)
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
|
|
rows, err := conn.Query(qry)
|
|
if err != nil {
|
|
log.Printf("[ERR] %v\r\n", err)
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
log.Println("[MSG] Query Successed.")
|
|
|
|
baseData := make(map[string]string, 0)
|
|
for rows.Next() {
|
|
var data DataSet
|
|
err := rows.Scan(&data.Alias, &data.Item, &data.Result, &data.UpdateTime)
|
|
if err != nil {
|
|
log.Printf("[ERR] %v\r\n", err)
|
|
return
|
|
}
|
|
baseData[data.UpdateTime] = data.Result
|
|
}
|
|
log.Printf("[MSG] Total Scanned: %d\r\n", len(baseData))
|
|
|
|
var dots []opts.LineData = make([]opts.LineData, 0)
|
|
for _, t := range timeAxis {
|
|
tmp, ok := baseData[t]
|
|
if ok {
|
|
dots = append(dots, opts.LineData{Value: tmp})
|
|
} else {
|
|
dots = append(dots, opts.LineData{Value: "null"})
|
|
}
|
|
}
|
|
log.Println("[MSG] Finished Sort Metadata.")
|
|
|
|
// Parsing Config - Chart Titles
|
|
chartTitle := cfg.Section("Chart").Key("Title").String()
|
|
chartSubtitle := cfg.Section("Chart").Key("Subtitle").String()
|
|
chartSeries := cfg.Section("Chart").Key("Series").String()
|
|
assetsURL := cfg.Section("Chart").Key("AssetsURL").String()
|
|
|
|
var xPercent float32 = 0
|
|
if params.Range == "all" {
|
|
xPercent = 0
|
|
}
|
|
if params.Range == "7days" {
|
|
xPercent = 76.66
|
|
}
|
|
if params.Range == "3days" {
|
|
xPercent = 90
|
|
}
|
|
if params.Range == "2days" {
|
|
xPercent = 93.33
|
|
}
|
|
if params.Range == "1day" {
|
|
xPercent = 96.66
|
|
}
|
|
if params.Range == "12hrs" {
|
|
xPercent = 98.33
|
|
}
|
|
if params.Range == "6hrs" {
|
|
xPercent = 99.16
|
|
}
|
|
if params.Alias != "" {
|
|
chartSubtitle = params.Alias
|
|
}
|
|
if params.Class != "" {
|
|
chartTitle = params.Class
|
|
}
|
|
|
|
chart := charts.NewLine()
|
|
// lang := make([]string, 0)
|
|
// lang = append(lang, "data view")
|
|
// lang = append(lang, "turn off")
|
|
// lang = append(lang, "refresh")
|
|
|
|
chart.SetGlobalOptions(
|
|
charts.WithTitleOpts(
|
|
opts.Title{
|
|
Left: "center",
|
|
Top: "0",
|
|
Title: chartTitle,
|
|
TitleStyle: &opts.TextStyle{
|
|
Color: "#000000",
|
|
FontSize: 18,
|
|
FontFamily: "Calibri",
|
|
},
|
|
Subtitle: chartSubtitle,
|
|
SubtitleStyle: &opts.TextStyle{
|
|
Color: "#222222",
|
|
FontSize: 16,
|
|
FontFamily: "Calibri",
|
|
},
|
|
},
|
|
),
|
|
charts.WithInitializationOpts(
|
|
opts.Initialization{
|
|
PageTitle: "eChart",
|
|
BackgroundColor: "#FFFFFF",
|
|
Width: "800px",
|
|
Height: "600px",
|
|
AssetsHost: assetsURL,
|
|
},
|
|
),
|
|
charts.WithDataZoomOpts(
|
|
opts.DataZoom{
|
|
Type: "inside",
|
|
Start: xPercent,
|
|
End: 100,
|
|
},
|
|
),
|
|
charts.WithXAxisOpts(
|
|
opts.XAxis{
|
|
Min: "dataMin",
|
|
Max: "dataMax",
|
|
AxisLabel: &opts.AxisLabel{
|
|
Show: true,
|
|
ShowMinLabel: true,
|
|
ShowMaxLabel: true,
|
|
Rotate: 45,
|
|
FontSize: "14",
|
|
FontWeight: "600",
|
|
FontFamily: "Calibri",
|
|
LineHeight: "250",
|
|
},
|
|
},
|
|
),
|
|
charts.WithTooltipOpts(
|
|
opts.Tooltip{
|
|
Show: true,
|
|
Trigger: "axis",
|
|
},
|
|
),
|
|
charts.WithToolboxOpts(
|
|
opts.Toolbox{
|
|
Show: true,
|
|
Feature: &opts.ToolBoxFeature{
|
|
SaveAsImage: &opts.ToolBoxFeatureSaveAsImage{
|
|
Show: true,
|
|
Type: "png",
|
|
},
|
|
DataView: &opts.ToolBoxFeatureDataView{
|
|
Show: true,
|
|
},
|
|
Restore: &opts.ToolBoxFeatureRestore{
|
|
Show: true,
|
|
},
|
|
},
|
|
},
|
|
),
|
|
charts.WithLegendOpts(
|
|
opts.Legend{
|
|
Show: false,
|
|
},
|
|
),
|
|
)
|
|
|
|
chart.SetXAxis(timeAxis).AddSeries(chartSeries, dots).
|
|
SetSeriesOptions(
|
|
charts.WithLineChartOpts(
|
|
opts.LineChart{
|
|
ConnectNulls: true,
|
|
},
|
|
),
|
|
charts.WithMarkPointNameTypeItemOpts(
|
|
opts.MarkPointNameTypeItem{
|
|
Name: "Max",
|
|
Type: "max",
|
|
},
|
|
opts.MarkPointNameTypeItem{
|
|
Name: "Min",
|
|
Type: "min",
|
|
},
|
|
),
|
|
charts.WithMarkPointStyleOpts(
|
|
opts.MarkPointStyle{
|
|
SymbolSize: 50,
|
|
Label: &opts.Label{
|
|
Show: true,
|
|
Position: "inside",
|
|
},
|
|
},
|
|
),
|
|
charts.WithMarkLineNameTypeItemOpts(
|
|
opts.MarkLineNameTypeItem{
|
|
Name: "Avrg",
|
|
Type: "average",
|
|
},
|
|
),
|
|
// charts.WithLabelOpts(
|
|
// opts.Label{
|
|
// Show: true,
|
|
// Position: "top",
|
|
// },
|
|
// ),
|
|
)
|
|
chart.Render(w)
|
|
log.Println("[MSG] Finished Render Web Page.")
|
|
}
|