github.com/aldelo/common@v1.5.1/wrapper/gin/ginxray.go (about) 1 package gin 2 3 /* 4 * Copyright 2020-2023 Aldelo, LP 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 import ( 20 "bytes" 21 util "github.com/aldelo/common" 22 "github.com/aldelo/common/wrapper/xray" 23 awsxray "github.com/aws/aws-xray-sdk-go/xray" 24 "github.com/gin-gonic/gin" 25 "log" 26 "net/http" 27 "strings" 28 ) 29 30 const X_AMZN_TRACE_ID string = "X-Amzn-Trace-Id" 31 const X_AMZN_SEG_ID string = "X-Amzn-Seg-Id" 32 const X_AMZN_TR_ID string = "X-Amzn-Tr-Id" 33 34 // XRayMiddleware to trace gin actions with aws xray 35 // 36 // if the method call is related to a prior xray segment, 37 // use Headers "X-Amzn-Seg-Id" and "X-Amzn-Tr-Id" to deliver the parent SegmentID and TraceID to this call stack 38 func XRayMiddleware() gin.HandlerFunc { 39 return func(c *gin.Context) { 40 if c == nil { 41 log.Println("!!! XRay Middleware Failed: Gin Context Nil !!!") 42 return 43 } 44 45 if strings.ToLower(util.Right(c.Request.URL.Path, 7)) != "/health" { 46 if seg := xray.NewSegmentFromHeader(c.Request); seg != nil && seg.Ready() { 47 // if there were parent segment ID and trace ID, relate it to newly created segment here 48 parentSegID := c.GetHeader(X_AMZN_SEG_ID) 49 traceID := c.GetHeader(X_AMZN_TR_ID) 50 51 if util.LenTrim(parentSegID) > 0 && util.LenTrim(traceID) > 0 { 52 seg.SetParentSegment(parentSegID, traceID) 53 } 54 55 // close segment 56 defer seg.Close() 57 58 c.Request = c.Request.WithContext(seg.Ctx) 59 60 w := &ResponseBodyWriterInterceptor{ 61 ResponseWriter: c.Writer, 62 RespBody: &bytes.Buffer{}, 63 } 64 c.Writer = w 65 66 traceRequestData(c, seg.Seg) 67 c.Next() 68 traceResponseData(c, seg.Seg, w.RespBody) 69 } else { 70 c.Next() 71 } 72 } else { 73 c.Next() 74 } 75 } 76 } 77 78 func traceRequestData(c *gin.Context, seg *awsxray.Segment) { 79 if c == nil { 80 log.Println("!!! XRay Middleware Request Trace Failed: Gin Context Nil !!!") 81 return 82 } 83 84 if c.Request == nil { 85 log.Println("!!! XRay Middleware Request Trace Failed: Gin Context Http Request Nil !!!") 86 return 87 } 88 89 if c.Request.Header == nil { 90 log.Println("!!! XRay Middleware Request Trace Failed: Gin Context Http Request Header Nil !!!") 91 return 92 } 93 94 if c.Writer == nil { 95 log.Println("!!! XRay Middleware Request Trace Failed: Gin Context Http Response Writer Nil !!!") 96 return 97 } 98 99 if seg == nil { 100 log.Println("!!! XRay Middleware Request Trace Failed: XRay Segment Nil !!!") 101 return 102 } 103 104 req := c.Request 105 106 seg.Lock() 107 108 if segReq := getSegmentRequest(seg); segReq != nil { 109 segReq.Method = req.Method 110 111 if req.URL != nil { 112 segReq.URL = req.URL.String() 113 } 114 115 if xForwardedFor := req.Header.Get("X-Forwarded-For"); util.LenTrim(xForwardedFor) > 0 { 116 segReq.XForwardedFor = true 117 segReq.ClientIP = util.Trim(strings.Split(xForwardedFor, ",")[0]) 118 } else { 119 segReq.XForwardedFor = false 120 segReq.ClientIP = req.RemoteAddr 121 } 122 123 segReq.UserAgent = req.UserAgent() 124 125 c.Writer.Header().Set(X_AMZN_TRACE_ID, getAmznTraceHeader(req, seg)) 126 127 seg.Unlock() 128 129 reqHdr, _ := util.ReadHttpRequestHeaders(req) 130 if reqHdr != nil { 131 _ = seg.AddMetadata("Request_Headers", reqHdr) 132 } else { 133 _ = seg.AddMetadata("Request_Headers", "") 134 } 135 136 reqBdy, _ := util.ReadHttpRequestBody(req) 137 _ = seg.AddMetadata("Request_Body", string(reqBdy)) 138 } else { 139 seg.Unlock() 140 } 141 } 142 143 func getSegmentRequest(seg *awsxray.Segment) *awsxray.RequestData { 144 if seg != nil { 145 if h := seg.GetHTTP(); h != nil { 146 if r := h.GetRequest(); r != nil { 147 return r 148 } 149 } 150 } 151 152 return nil 153 } 154 155 func getSegmentResponse(seg *awsxray.Segment) *awsxray.ResponseData { 156 if seg != nil { 157 if h := seg.GetHTTP(); h != nil { 158 if r := h.GetResponse(); r != nil { 159 return r 160 } 161 } 162 } 163 164 return nil 165 } 166 167 func getAmznTraceHeader(req *http.Request, seg *awsxray.Segment) string { 168 if req == nil { 169 log.Println("!!! XRay Middleware Request Trace Failed: (getAmznTraceHeader) Gin Context Http Request Nil !!!") 170 return "" 171 } 172 173 if req.Header == nil { 174 log.Println("!!! XRay Middleware Request Trace Failed: (getAmznTraceHeader) Gin Context Http Request Header Nil !!!") 175 return "" 176 } 177 178 if seg == nil { 179 log.Println("!!! XRay Middleware Request Trace Failed: (getAmznTraceHeader) XRay Segment Nil !!!") 180 return "" 181 } 182 183 // parse x-amzn-trace-id header parts to trace map 184 trace := make(map[string]string) 185 hdr := req.Header.Get(X_AMZN_TRACE_ID) 186 187 for _, p := range strings.Split(hdr, ";") { 188 kv := strings.SplitN(p, "=", 2) 189 k := util.Trim(kv[0]) 190 v := "" 191 if len(kv) > 1 { 192 v = util.Trim(kv[1]) 193 } 194 trace[k] = v 195 } 196 197 if util.LenTrim(trace["Root"]) > 0 { 198 seg.TraceID = trace["Root"] 199 seg.RequestWasTraced = true 200 } 201 202 if util.LenTrim(trace["Parent"]) > 0 { 203 seg.ParentID = trace["Parent"] 204 } 205 206 buf := bytes.Buffer{} 207 buf.WriteString("Root=") 208 buf.WriteString(seg.TraceID) 209 210 return buf.String() 211 } 212 213 func traceResponseData(c *gin.Context, seg *awsxray.Segment, respBody *bytes.Buffer) { 214 if c == nil { 215 log.Println("!!! XRay Middleware Response Trace Failed: Gin Context Nil !!!") 216 return 217 } 218 219 if c.Writer == nil { 220 log.Println("!!! XRay Middleware Response Trace Failed: Gin Context Http Response Writer Nil !!!") 221 return 222 } 223 224 if seg == nil { 225 log.Println("!!! XRay Middleware Request Trace Failed: XRay Segment Nil !!!") 226 return 227 } 228 229 status := c.Writer.Status() 230 231 seg.Lock() 232 233 if segResp := getSegmentResponse(seg); segResp != nil { 234 segResp.Status = status 235 segResp.ContentLength = c.Writer.Size() 236 237 if status >= 400 && status < 500 { 238 seg.Error = true 239 } 240 241 if status == 429 { 242 seg.Throttle = true 243 } 244 245 if status >= 500 && status < 600 { 246 seg.Fault = true 247 } 248 249 seg.Unlock() 250 251 respHdr, _ := util.ParseHttpHeader(c.Writer.Header()) 252 if respHdr != nil { 253 _ = seg.AddMetadata("Response_Headers", respHdr) 254 } else { 255 _ = seg.AddMetadata("Response_Headers", "") 256 } 257 258 if respBody != nil { 259 _ = seg.AddMetadata("Response_Body", string(respBody.Bytes())) 260 } else { 261 _ = seg.AddMetadata("Response_Body", "") 262 } 263 } else { 264 seg.Unlock() 265 } 266 }