debug.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. package api
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/http"
  6. "os"
  7. "time"
  8. "git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/api/errs"
  9. "git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/api/sse"
  10. context "github.com/kataras/iris/v12/context"
  11. "go.mongodb.org/mongo-driver/bson/primitive"
  12. )
  13. type DebugStage struct {
  14. DebugEvent `json:",inline"`
  15. Events []*DebugEvent `json:"events"`
  16. }
  17. type DebugEvent struct {
  18. ID string `json:"id"`
  19. Type string `json:"type"`
  20. Status string `json:"status"`
  21. Created int64 `json:"created"`
  22. Error *errs.Error `json:"error"`
  23. Data interface{} `json:"data"`
  24. }
  25. type Request struct {
  26. ID string `json:"id"`
  27. Status string `json:"status"`
  28. Created int64 `json:"created"`
  29. }
  30. type DebugTaks struct {
  31. ID string `json:"id"`
  32. Status string `json:"status"`
  33. Created int64 `json:"created"`
  34. Stages []*DebugStage `json:"stages"`
  35. CurrentStage *DebugStage `json:"-"`
  36. Debug *Debugger `json:"-"`
  37. Request interface{} `json:"request"`
  38. }
  39. func NewDebugTaks() *DebugTaks {
  40. return &DebugTaks{
  41. Stages: []*DebugStage{},
  42. CurrentStage: &DebugStage{},
  43. }
  44. }
  45. func (debug *DebugTaks) Stage(id string) *DebugStage {
  46. stage := &DebugStage{}
  47. stage.ID = id
  48. stage.Events = []*DebugEvent{}
  49. debug.Stages = append(debug.Stages, stage)
  50. debug.CurrentStage = stage
  51. return stage
  52. }
  53. func (stage *DebugStage) PushEvent(event *DebugEvent) {
  54. stage.Events = append(stage.Events, event)
  55. }
  56. func (debug *DebugTaks) Event(eventType, eventId string) *DebugEvent {
  57. event := &DebugEvent{
  58. ID: eventId,
  59. Type: eventType,
  60. Created: time.Now().Unix(),
  61. }
  62. debug.CurrentStage.PushEvent(event)
  63. return event
  64. }
  65. func (task *DebugTaks) Finalize() {
  66. var (
  67. debug = task.Debug
  68. out []byte
  69. err error
  70. )
  71. for _, stage := range task.Stages {
  72. for _, event := range stage.Events {
  73. if event.Error != nil {
  74. event.Status = "error"
  75. stage.Status = "error"
  76. task.Status = "error"
  77. goto end
  78. }
  79. }
  80. }
  81. // task.Event()
  82. end:
  83. if out, err = json.Marshal(task); err != nil {
  84. debug.Emit(&sse.Event{
  85. Kind: "debugger.error",
  86. Payload: err.Error(),
  87. })
  88. } else {
  89. debug.Emit(&sse.Event{
  90. Kind: "request",
  91. Payload: string(out),
  92. })
  93. }
  94. }
  95. type Debugger struct {
  96. ChannelID string
  97. Tasks []*DebugTaks
  98. Hub *sse.SSEHub
  99. }
  100. func (debug *Debugger) Handler() func(context.Context) {
  101. return func(ctx context.Context) {
  102. var err error
  103. task := debug.CreateTask()
  104. if task.Request, err = parseRequest(ctx.Request()); err != nil {
  105. fmt.Println("Request debug error", err.Error())
  106. }
  107. ctx.Values().Set("#debug", task)
  108. ctx.Next()
  109. }
  110. }
  111. func (debug *Debugger) CreateTask() *DebugTaks {
  112. task := &DebugTaks{
  113. ID: primitive.NewObjectID().Hex(),
  114. Status: "",
  115. Created: time.Now().Unix(),
  116. Debug: debug,
  117. }
  118. debug.Tasks = append([]*DebugTaks{task}, debug.Tasks...)
  119. return task
  120. }
  121. func (debug *Debugger) Emit(events ...*sse.Event) {
  122. channel := debug.Hub.GetChannel(debug.ChannelID)
  123. for _, event := range events {
  124. channel.Emit(event)
  125. }
  126. }
  127. func (debug *Debugger) EventStream() func(context.Context) (interface{}, *errs.Error) {
  128. return func(ctx context.Context) (resp interface{}, err *errs.Error) {
  129. go func() {
  130. if len(debug.Tasks) > 0 {
  131. time.Sleep(time.Second)
  132. fmt.Println("initialize debug with ", len(debug.Tasks))
  133. for _, task := range debug.Tasks[:10] {
  134. task.Finalize()
  135. }
  136. debug.Tasks = debug.Tasks[:10]
  137. }
  138. }()
  139. if err = debug.Hub.UpgradeConnection(
  140. ctx,
  141. debug.ChannelID,
  142. ); err != nil {
  143. return
  144. }
  145. resp = true
  146. return
  147. }
  148. }
  149. func NewDebug() *Debugger {
  150. return &Debugger{
  151. ChannelID: "debug",
  152. Tasks: []*DebugTaks{},
  153. Hub: sse.NewSSEHub(&sse.SSEOptions{
  154. URI: os.Getenv("REDIS_URI"),
  155. Password: os.Getenv("REDIS_PASSWD"),
  156. ChannelCollection: "debugger",
  157. }),
  158. }
  159. }
  160. func parseRequest(req *http.Request) (result map[string]interface{}, err error) {
  161. save := req.Body
  162. defer func() {
  163. req.Body = save
  164. }()
  165. result = map[string]interface{}{}
  166. // if !body || req.Body == nil {
  167. // req.Body = nil
  168. // } else {
  169. // save, req.Body, err = drainBody(req.Body)
  170. // if err != nil {
  171. // return nil, err
  172. // }
  173. // }
  174. var (
  175. reqURI string
  176. reqMethod string
  177. )
  178. if reqURI = req.RequestURI; reqURI == "" {
  179. reqURI = req.URL.RequestURI()
  180. }
  181. if reqMethod = req.Method; req.Method == "" {
  182. reqMethod = "GET"
  183. }
  184. result["URI"] = reqURI
  185. result["URL"] = map[string]interface{}{
  186. "Scheme": req.URL.Scheme,
  187. "Host": req.URL.Hostname(),
  188. "IsAbs": req.URL.IsAbs(),
  189. "EscapedPath": req.URL.EscapedPath(),
  190. "Port": req.URL.Port(),
  191. "Uri": req.URL.String(),
  192. "query": req.URL.Query(),
  193. }
  194. result["Method"] = reqMethod
  195. result["ProtoMajor"] = req.ProtoMajor
  196. result["ProtoMinor"] = req.ProtoMinor
  197. result["Proto"] = req.Proto
  198. result["RemoteAddr"] = req.RemoteAddr
  199. result["Header"] = req.Header
  200. result["Host"] = req.Host
  201. result["Close"] = req.Close
  202. result["ContentLength"] = req.ContentLength
  203. // for name, value := range req.Header {
  204. // fmt.Printf("%v: %v\n", name, value)
  205. // }
  206. // fmt.Fprintf(&b, "%s %s HTTP/%d.%d\r\n", valueOrDefault(req.Method, "GET"),
  207. // reqURI, req.ProtoMajor, req.ProtoMinor)
  208. // absRequestURI := strings.HasPrefix(req.RequestURI, "http://") || strings.HasPrefix(req.RequestURI, "https://")
  209. // if !absRequestURI {
  210. // host := req.Host
  211. // if host == "" && req.URL != nil {
  212. // host = req.URL.Host
  213. // }
  214. // if host != "" {
  215. // fmt.Fprintf(&b, "Host: %s\r\n", host)
  216. // }
  217. // }
  218. // chunked := len(req.TransferEncoding) > 0 && req.TransferEncoding[0] == "chunked"
  219. // if len(req.TransferEncoding) > 0 {
  220. // fmt.Fprintf(&b, "Transfer-Encoding: %s\r\n", strings.Join(req.TransferEncoding, ","))
  221. // }
  222. // if req.Close {
  223. // fmt.Fprintf(&b, "Connection: close\r\n")
  224. // }
  225. // err = req.Header.WriteSubset(&b, reqWriteExcludeHeaderDump)
  226. // if err != nil {
  227. // return nil, err
  228. // }
  229. // io.WriteString(&b, "\r\n")
  230. // if req.Body != nil {
  231. // var dest io.Writer = &b
  232. // if chunked {
  233. // dest = NewChunkedWriter(dest)
  234. // }
  235. // _, err = io.Copy(dest, req.Body)
  236. // if chunked {
  237. // dest.(io.Closer).Close()
  238. // io.WriteString(&b, "\r\n")
  239. // }
  240. // }
  241. // req.Body = save
  242. // if err != nil {
  243. // return nil, err
  244. // }
  245. // return b.Bytes(), nil
  246. return
  247. }