pprof.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. package service
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/json"
  6. "fmt"
  7. "os/exec"
  8. "regexp"
  9. "strconv"
  10. "strings"
  11. "time"
  12. "go-common/app/admin/main/apm/conf"
  13. "go-common/app/admin/main/apm/model/pprof"
  14. "go-common/library/log"
  15. xtime "go-common/library/time"
  16. )
  17. var (
  18. port = "2333"
  19. cpuURL = "%s/x/admin/apm/pprof/svg?name=%s&uri=%s&hostname=%s"
  20. msg = "<a href=\"%s\">点击查看详情</a>(注:生成时间有延迟,如未显示,稍后重试)"
  21. )
  22. // kind .
  23. const (
  24. CPUPerformace = 1 // CPU性能图
  25. CPUFrame = 2 // CPU火焰图
  26. HeapPerformance = 3 // 内存性能图
  27. HeapFrame = 4 // 内存火焰图
  28. )
  29. // Pprof ...
  30. func (s *Service) Pprof(url, uri, svgName, hostName string, time int64, sType int8) (err error) {
  31. var (
  32. out bytes.Buffer
  33. errOut bytes.Buffer
  34. )
  35. goPath := "go"
  36. if len(conf.Conf.Pprof.GoPath) > 0 {
  37. goPath = conf.Conf.Pprof.GoPath
  38. }
  39. f, err := exec.LookPath(goPath)
  40. if err != nil {
  41. log.Error("pprof go error(%v) goPath=(%v)", err, goPath)
  42. fmt.Printf("pprof=(%v)", err)
  43. return
  44. }
  45. 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)
  46. cmd.Stdout = &out
  47. cmd.Stderr = &errOut
  48. // 执行命令
  49. if sType == 1 { //串行
  50. if err = cmd.Run(); err != nil {
  51. log.Error("pprof Run stdout=(%s) stderr=(%s) error(%v)", out.String(), errOut.String(), err)
  52. }
  53. } else { //阻塞
  54. if err = cmd.Start(); err != nil {
  55. log.Error("pprof Start stdout=(%s) stderr=(%s) error(%v)", out.String(), errOut.String(), err)
  56. }
  57. if err = cmd.Wait(); err != nil {
  58. log.Error("s.Pprof cmd.Wait() error(%v)", err)
  59. }
  60. }
  61. return
  62. }
  63. // Torch ...
  64. func (s *Service) Torch(c context.Context, url, uri, svgName, hostName string, time int64, sType int8) (err error) {
  65. goPath := "go-torch"
  66. // if len(conf.Conf.Pprof.GoPath) > 0 {
  67. // goPath = conf.Conf.Pprof.GoPath
  68. // }
  69. f, err := exec.LookPath(goPath)
  70. if err != nil {
  71. log.Error("go-torch error(%v) goPath=(%v)", err, goPath)
  72. fmt.Printf("go-torch=(%v)", err)
  73. return
  74. }
  75. 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")
  76. var (
  77. out bytes.Buffer
  78. cmdErr bytes.Buffer
  79. )
  80. cmd.Stdout = &out
  81. cmd.Stderr = &cmdErr
  82. // 执行命令
  83. if sType == 1 { //串行
  84. if err = cmd.Run(); err != nil {
  85. log.Error("go-torch Run stdout=(%s) stderr=(%s) error(%v)", out.String(), cmdErr.String(), err)
  86. }
  87. } else { //阻塞
  88. if err = cmd.Start(); err != nil {
  89. log.Error("go-torch Start stdout=(%s) stderr=(%s) error(%v)", out.String(), cmdErr.String(), err)
  90. }
  91. if err = cmd.Wait(); err != nil {
  92. log.Error("cmd.Wait() error(%v)", err)
  93. }
  94. }
  95. return
  96. }
  97. // ActiveWarning active
  98. func (s *Service) ActiveWarning(c context.Context, text string) (err error) {
  99. var (
  100. ins *pprof.Ins
  101. title = "【%s】性能告警抓取通知"
  102. warn = &pprof.Warning{}
  103. pws = make([]*pprof.Warn, 0)
  104. times = time.Now().Unix()
  105. curTime = xtime.Time(times)
  106. reg = regexp.MustCompile(`[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}`)
  107. )
  108. if err = json.Unmarshal([]byte(text), warn); err != nil {
  109. log.Error("s.ActiveWarning json.Unmarshal data error(%v)", err)
  110. return
  111. }
  112. if warn.Tags.App == "" {
  113. log.Info("s.ActiveWarning skipped(AppName is empty)")
  114. return
  115. }
  116. title = fmt.Sprintf(title, warn.Tags.App)
  117. if ins, err = s.dao.Instances(c, warn.Tags.App); err != nil {
  118. log.Error("s.ActiveWaring get instances error(%v)", err)
  119. return
  120. }
  121. for _, instance := range ins.Instances {
  122. var (
  123. ip string
  124. addrs = instance.Addrs
  125. strs = strings.Split(instance.Hostname, "-")
  126. hostName = fmt.Sprintf("%s-%d", strs[len(strs)-1], times)
  127. pprofWarn = &pprof.Warn{}
  128. )
  129. if len(addrs) < 1 {
  130. log.Info("s.ActiveWarning not found adds")
  131. continue
  132. }
  133. ip = reg.FindString(addrs[0])
  134. host := fmt.Sprintf("http://%s:%s", ip, port)
  135. pprofWarn.IP = ip
  136. pprofWarn.AppID = warn.Tags.App
  137. pprofWarn.SvgName = hostName
  138. pprofWarn.Ctime = curTime
  139. pprofWarn.Mtime = curTime
  140. if err = s.Torch(c, host, "profile", warn.Tags.App, hostName, 30, 2); err == nil {
  141. pprofWarn.Kind = CPUPerformace
  142. pws = append(pws, packing(pprofWarn))
  143. }
  144. if err = s.Pprof(host, "profile", warn.Tags.App, hostName, 30, 2); err == nil {
  145. pprofWarn.Kind = CPUFrame
  146. pws = append(pws, packing(pprofWarn))
  147. }
  148. if err = s.Torch(c, host, "heap", warn.Tags.App, hostName, 30, 2); err == nil {
  149. pprofWarn.Kind = HeapPerformance
  150. pws = append(pws, packing(pprofWarn))
  151. }
  152. if err = s.Pprof(host, "heap", warn.Tags.App, hostName, 30, 2); err == nil {
  153. pprofWarn.Kind = HeapFrame
  154. pws = append(pws, packing(pprofWarn))
  155. }
  156. }
  157. if len(pws) == 0 {
  158. return
  159. }
  160. if err = s.AddPprofWarn(c, pws); err != nil {
  161. return
  162. }
  163. return s.SendWeChat(c, title, fmt.Sprintf(msg, s.c.Host.SVENCo), warn.Tags.App, strings.Join(s.c.WeChat.Users, ","))
  164. }
  165. // packing .
  166. func packing(pw *pprof.Warn) (pprofWarn *pprof.Warn) {
  167. pprofWarn = &pprof.Warn{
  168. AppID: pw.AppID,
  169. SvgName: pw.SvgName,
  170. IP: pw.IP,
  171. Kind: pw.Kind,
  172. Mtime: pw.Mtime,
  173. Ctime: pw.Ctime,
  174. }
  175. return
  176. }
  177. // AddPprofWarn .
  178. func (s *Service) AddPprofWarn(c context.Context, pws []*pprof.Warn) (err error) {
  179. var (
  180. sql = "INSERT INTO `pprof_warn`(`app_id`, `svg_name`, `ip`, `kind`, `ctime`, `mtime`) VALUES %s"
  181. key = make([]string, 0)
  182. value = make([]interface{}, 0)
  183. )
  184. for _, pw := range pws {
  185. key = append(key, "(?,?,?,?,?,?)")
  186. value = append(value, pw.AppID, pw.SvgName, pw.IP, pw.Kind, pw.Ctime, pw.Mtime)
  187. }
  188. if err = s.DB.Exec(fmt.Sprintf(sql, strings.Join(key, ",")), value...).Error; err != nil {
  189. log.Error("s.AddPprofWarn error(%v)", err)
  190. }
  191. return
  192. }
  193. // PprofWarn .
  194. func (s *Service) PprofWarn(c context.Context, req *pprof.Params) (pws []*pprof.Warn, err error) {
  195. var (
  196. query = s.DB.Where("1=1")
  197. )
  198. pws = make([]*pprof.Warn, 0)
  199. if req.AppID == "" && req.IP == "" && req.SvgName == "" && req.Kind == 0 && req.StartTime == 0 && req.EndTime == 0 {
  200. return
  201. }
  202. if req.AppID != "" {
  203. query = query.Where("app_id=?", req.AppID)
  204. }
  205. if req.SvgName != "" {
  206. query = query.Where("svg_name=?", req.SvgName)
  207. }
  208. if req.IP != "" {
  209. query = query.Where("ip=?", req.IP)
  210. }
  211. if req.Kind != 0 {
  212. query = query.Where("kind=?", req.Kind)
  213. }
  214. if req.StartTime != 0 && req.EndTime != 0 {
  215. query = query.Where("mtime between ? and ?", req.StartTime, req.EndTime)
  216. }
  217. if err = query.Order("mtime desc").Find(&pws).Error; err != nil {
  218. log.Error("s.PprofWarn query error(%v)", err)
  219. }
  220. s.setSvgURL(pws)
  221. return
  222. }
  223. // setSvgURL .
  224. func (s *Service) setSvgURL(pws []*pprof.Warn) {
  225. for _, pw := range pws {
  226. switch {
  227. case pw.Kind == CPUPerformace:
  228. pw.URL = fmt.Sprintf(cpuURL, s.c.Host.SVENCo, pw.AppID, "profile", pw.SvgName)
  229. case pw.Kind == CPUFrame:
  230. pw.URL = fmt.Sprintf(cpuURL, s.c.Host.SVENCo, pw.AppID, "profile_flame", pw.SvgName)
  231. case pw.Kind == HeapPerformance:
  232. pw.URL = fmt.Sprintf(cpuURL, s.c.Host.SVENCo, pw.AppID, "heap", pw.SvgName)
  233. case pw.Kind == HeapFrame:
  234. pw.URL = fmt.Sprintf(cpuURL, s.c.Host.SVENCo, pw.AppID, "heap_flame", pw.SvgName)
  235. default:
  236. }
  237. }
  238. }