123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- package service
- import (
- "bytes"
- "context"
- "encoding/json"
- "fmt"
- "os/exec"
- "regexp"
- "strconv"
- "strings"
- "time"
- "go-common/app/admin/main/apm/conf"
- "go-common/app/admin/main/apm/model/pprof"
- "go-common/library/log"
- xtime "go-common/library/time"
- )
- var (
- port = "2333"
- cpuURL = "%s/x/admin/apm/pprof/svg?name=%s&uri=%s&hostname=%s"
- msg = "<a href=\"%s\">点击查看详情</a>(注:生成时间有延迟,如未显示,稍后重试)"
- )
- // kind .
- const (
- CPUPerformace = 1 // CPU性能图
- CPUFrame = 2 // CPU火焰图
- HeapPerformance = 3 // 内存性能图
- HeapFrame = 4 // 内存火焰图
- )
- // Pprof ...
- func (s *Service) Pprof(url, uri, svgName, hostName string, time int64, sType int8) (err error) {
- var (
- out bytes.Buffer
- errOut bytes.Buffer
- )
- goPath := "go"
- if len(conf.Conf.Pprof.GoPath) > 0 {
- goPath = conf.Conf.Pprof.GoPath
- }
- f, err := exec.LookPath(goPath)
- if err != nil {
- log.Error("pprof go error(%v) goPath=(%v)", err, goPath)
- fmt.Printf("pprof=(%v)", err)
- return
- }
- cmd := exec.Command(f, "tool", "pprof", "--seconds="+strconv.FormatInt(time, 10), "--svg", "--output="+conf.Conf.Pprof.Dir+"/"+svgName+"_"+hostName+"_"+uri+".svg", url+"/debug/pprof/"+uri)
- cmd.Stdout = &out
- cmd.Stderr = &errOut
- // 执行命令
- if sType == 1 { //串行
- if err = cmd.Run(); err != nil {
- log.Error("pprof Run stdout=(%s) stderr=(%s) error(%v)", out.String(), errOut.String(), err)
- }
- } else { //阻塞
- if err = cmd.Start(); err != nil {
- log.Error("pprof Start stdout=(%s) stderr=(%s) error(%v)", out.String(), errOut.String(), err)
- }
- if err = cmd.Wait(); err != nil {
- log.Error("s.Pprof cmd.Wait() error(%v)", err)
- }
- }
- return
- }
- // Torch ...
- func (s *Service) Torch(c context.Context, url, uri, svgName, hostName string, time int64, sType int8) (err error) {
- goPath := "go-torch"
- // if len(conf.Conf.Pprof.GoPath) > 0 {
- // goPath = conf.Conf.Pprof.GoPath
- // }
- f, err := exec.LookPath(goPath)
- if err != nil {
- log.Error("go-torch error(%v) goPath=(%v)", err, goPath)
- fmt.Printf("go-torch=(%v)", err)
- return
- }
- cmd := exec.Command(f, "--url="+url, "--suffix=/debug/pprof/"+uri, "--seconds="+strconv.FormatInt(time, 10), "-f="+conf.Conf.Pprof.Dir+"/"+svgName+"_"+hostName+"_"+uri+"_flame.svg")
- var (
- out bytes.Buffer
- cmdErr bytes.Buffer
- )
- cmd.Stdout = &out
- cmd.Stderr = &cmdErr
- // 执行命令
- if sType == 1 { //串行
- if err = cmd.Run(); err != nil {
- log.Error("go-torch Run stdout=(%s) stderr=(%s) error(%v)", out.String(), cmdErr.String(), err)
- }
- } else { //阻塞
- if err = cmd.Start(); err != nil {
- log.Error("go-torch Start stdout=(%s) stderr=(%s) error(%v)", out.String(), cmdErr.String(), err)
- }
- if err = cmd.Wait(); err != nil {
- log.Error("cmd.Wait() error(%v)", err)
- }
- }
- return
- }
- // ActiveWarning active
- func (s *Service) ActiveWarning(c context.Context, text string) (err error) {
- var (
- ins *pprof.Ins
- title = "【%s】性能告警抓取通知"
- warn = &pprof.Warning{}
- pws = make([]*pprof.Warn, 0)
- times = time.Now().Unix()
- curTime = xtime.Time(times)
- reg = regexp.MustCompile(`[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}`)
- )
- if err = json.Unmarshal([]byte(text), warn); err != nil {
- log.Error("s.ActiveWarning json.Unmarshal data error(%v)", err)
- return
- }
- if warn.Tags.App == "" {
- log.Info("s.ActiveWarning skipped(AppName is empty)")
- return
- }
- title = fmt.Sprintf(title, warn.Tags.App)
- if ins, err = s.dao.Instances(c, warn.Tags.App); err != nil {
- log.Error("s.ActiveWaring get instances error(%v)", err)
- return
- }
- for _, instance := range ins.Instances {
- var (
- ip string
- addrs = instance.Addrs
- strs = strings.Split(instance.Hostname, "-")
- hostName = fmt.Sprintf("%s-%d", strs[len(strs)-1], times)
- pprofWarn = &pprof.Warn{}
- )
- if len(addrs) < 1 {
- log.Info("s.ActiveWarning not found adds")
- continue
- }
- ip = reg.FindString(addrs[0])
- host := fmt.Sprintf("http://%s:%s", ip, port)
- pprofWarn.IP = ip
- pprofWarn.AppID = warn.Tags.App
- pprofWarn.SvgName = hostName
- pprofWarn.Ctime = curTime
- pprofWarn.Mtime = curTime
- if err = s.Torch(c, host, "profile", warn.Tags.App, hostName, 30, 2); err == nil {
- pprofWarn.Kind = CPUPerformace
- pws = append(pws, packing(pprofWarn))
- }
- if err = s.Pprof(host, "profile", warn.Tags.App, hostName, 30, 2); err == nil {
- pprofWarn.Kind = CPUFrame
- pws = append(pws, packing(pprofWarn))
- }
- if err = s.Torch(c, host, "heap", warn.Tags.App, hostName, 30, 2); err == nil {
- pprofWarn.Kind = HeapPerformance
- pws = append(pws, packing(pprofWarn))
- }
- if err = s.Pprof(host, "heap", warn.Tags.App, hostName, 30, 2); err == nil {
- pprofWarn.Kind = HeapFrame
- pws = append(pws, packing(pprofWarn))
- }
- }
- if len(pws) == 0 {
- return
- }
- if err = s.AddPprofWarn(c, pws); err != nil {
- return
- }
- return s.SendWeChat(c, title, fmt.Sprintf(msg, s.c.Host.SVENCo), warn.Tags.App, strings.Join(s.c.WeChat.Users, ","))
- }
- // packing .
- func packing(pw *pprof.Warn) (pprofWarn *pprof.Warn) {
- pprofWarn = &pprof.Warn{
- AppID: pw.AppID,
- SvgName: pw.SvgName,
- IP: pw.IP,
- Kind: pw.Kind,
- Mtime: pw.Mtime,
- Ctime: pw.Ctime,
- }
- return
- }
- // AddPprofWarn .
- func (s *Service) AddPprofWarn(c context.Context, pws []*pprof.Warn) (err error) {
- var (
- sql = "INSERT INTO `pprof_warn`(`app_id`, `svg_name`, `ip`, `kind`, `ctime`, `mtime`) VALUES %s"
- key = make([]string, 0)
- value = make([]interface{}, 0)
- )
- for _, pw := range pws {
- key = append(key, "(?,?,?,?,?,?)")
- value = append(value, pw.AppID, pw.SvgName, pw.IP, pw.Kind, pw.Ctime, pw.Mtime)
- }
- if err = s.DB.Exec(fmt.Sprintf(sql, strings.Join(key, ",")), value...).Error; err != nil {
- log.Error("s.AddPprofWarn error(%v)", err)
- }
- return
- }
- // PprofWarn .
- func (s *Service) PprofWarn(c context.Context, req *pprof.Params) (pws []*pprof.Warn, err error) {
- var (
- query = s.DB.Where("1=1")
- )
- pws = make([]*pprof.Warn, 0)
- if req.AppID == "" && req.IP == "" && req.SvgName == "" && req.Kind == 0 && req.StartTime == 0 && req.EndTime == 0 {
- return
- }
- if req.AppID != "" {
- query = query.Where("app_id=?", req.AppID)
- }
- if req.SvgName != "" {
- query = query.Where("svg_name=?", req.SvgName)
- }
- if req.IP != "" {
- query = query.Where("ip=?", req.IP)
- }
- if req.Kind != 0 {
- query = query.Where("kind=?", req.Kind)
- }
- if req.StartTime != 0 && req.EndTime != 0 {
- query = query.Where("mtime between ? and ?", req.StartTime, req.EndTime)
- }
- if err = query.Order("mtime desc").Find(&pws).Error; err != nil {
- log.Error("s.PprofWarn query error(%v)", err)
- }
- s.setSvgURL(pws)
- return
- }
- // setSvgURL .
- func (s *Service) setSvgURL(pws []*pprof.Warn) {
- for _, pw := range pws {
- switch {
- case pw.Kind == CPUPerformace:
- pw.URL = fmt.Sprintf(cpuURL, s.c.Host.SVENCo, pw.AppID, "profile", pw.SvgName)
- case pw.Kind == CPUFrame:
- pw.URL = fmt.Sprintf(cpuURL, s.c.Host.SVENCo, pw.AppID, "profile_flame", pw.SvgName)
- case pw.Kind == HeapPerformance:
- pw.URL = fmt.Sprintf(cpuURL, s.c.Host.SVENCo, pw.AppID, "heap", pw.SvgName)
- case pw.Kind == HeapFrame:
- pw.URL = fmt.Sprintf(cpuURL, s.c.Host.SVENCo, pw.AppID, "heap_flame", pw.SvgName)
- default:
- }
- }
- }
|