Select Git revision
api-logs.go
-
Francé Wilke authoredFrancé Wilke authored
api-logs.go 5.67 KiB
package api_logs
import (
"net/url"
"strconv"
"strings"
"time"
"github.com/aws/aws-lambda-go/events"
)
func GenerateIncomingAPILog(startTime time.Time, requestID *string, claim map[string]interface{}, req events.APIGatewayProxyRequest, res events.APIGatewayProxyResponse) ApiLog {
endTime := time.Now()
currentRequestID := ""
if requestID != nil {
currentRequestID = *requestID
}
var authType string
var authUsername string
if req.RequestContext.Identity.CognitoAuthenticationType != "" {
authType = "cognito"
split := strings.Split(req.RequestContext.Identity.CognitoAuthenticationProvider, ":")
if len(split) > 0 {
authUsername = split[len(split)-1] //= part after last ':'
}
} else {
authType = "iam"
split := strings.Split(req.RequestContext.Identity.UserArn, ":user/")
if len(split) > 0 {
authUsername = split[len(split)-1] //= part after ':user/'
}
}
userID, _ := claim["UserID"].(int64)
username, _ := claim["Username"].(string)
accountID, _ := claim["AccountID"].(int64)
if accountID == 0 {
if accountIDParam, ok := req.QueryStringParameters["account_id"]; ok {
if i64, err := strconv.ParseInt(accountIDParam, 10, 64); err == nil && i64 > 0 {
accountID = i64
}
}
}
apiLog := ApiLog{
StartTime: startTime,
EndTime: endTime,
DurMs: endTime.Sub(startTime).Milliseconds(),
Type: "api-incoming",
Method: req.HTTPMethod,
Address: req.RequestContext.DomainName,
Path: req.Path,
ResponseCode: res.StatusCode,
RequestID: currentRequestID,
InitialAuthType: authType,
InitialAuthUsername: authUsername,
SourceIP: req.RequestContext.Identity.SourceIP,
UserAgent: req.RequestContext.Identity.UserAgent,
UserID: userID,
Username: username,
AccountID: accountID,
Request: ApiLogRequest{
Headers: req.Headers,
QueryParameters: req.QueryStringParameters,
BodySize: len(req.Body),
Body: req.Body,
},
Response: ApiLogResponse{
Headers: res.Headers,
BodySize: len(res.Body),
Body: res.Body,
},
}
//also copy multi-value query parameters to the log as CSV array values
for n, as := range req.MultiValueQueryStringParameters {
apiLog.Request.QueryParameters[n] = "[" + strings.Join(as, ",") + "]"
}
return apiLog
}
func GenerateOutgoingAPILog(startTime time.Time, requestID *string, claim map[string]interface{}, urlString string, method string, requestBody string, requestHeaders map[string]string, responseBody string, responseCode int) ApiLog {
endTime := time.Now()
currentRequestID := ""
if requestID != nil {
currentRequestID = *requestID
}
userID, _ := claim["UserID"].(int64)
username, _ := claim["Username"].(string)
accountID, _ := claim["AccountID"].(int64)
params := map[string]string{}
parsedURL, err := url.Parse(urlString)
if err == nil {
for n, v := range parsedURL.Query() {
params[n] = strings.Join(v, ",")
}
}
apiLog := ApiLog{
StartTime: startTime,
EndTime: endTime,
DurMs: endTime.Sub(startTime).Milliseconds(),
Type: "api-outgoing",
Method: method,
Path: parsedURL.Path,
Address: parsedURL.Host,
ResponseCode: responseCode,
RequestID: currentRequestID,
UserID: userID,
Username: username,
AccountID: accountID,
Request: ApiLogRequest{
Headers: requestHeaders,
QueryParameters: params,
BodySize: len(requestBody),
Body: requestBody,
},
Response: ApiLogResponse{
Headers: requestHeaders,
BodySize: len(responseBody),
Body: responseBody,
},
}
return apiLog
}
//ApiLog is the SQS event details struct encoded as JSON document, sent to SQS, to be logged for each API handler executed.
type ApiLog struct {
StartTime time.Time `json:"start_time"`
EndTime time.Time `json:"end_time"`
DurMs int64 `json:"duration_ms"` //duration in milliseconds
Type string `json:"type"` //incoming-api or outgoing-api
Method string `json:"method"`
Address string `json:"address"` //server address for incoming and outgoing
Path string `json:"path"`
ResponseCode int `json:"response_code"`
RequestID string `json:"request_id"`
InitialAuthUsername string `json:"initial_auth_username,omitempty"`
InitialAuthType string `json:"initial_auth_type,omitempty"`
AccountID int64 `json:"account_id,omitempty"`
UserID int64 `json:"user_id,omitempty"`
Username string `json:"username,omitempty"`
SourceIP string `json:"source_ip,omitempty"` //only logged for incoming API
UserAgent string `json:"user_agent,omitempty"` //only for incoming, indicate type of browser when UI
Request ApiLogRequest `json:"request"`
Response ApiLogResponse `json:"response"`
}
type ApiLogRequest struct {
Headers map[string]string `json:"headers,omitempty"`
QueryParameters map[string]string `json:"query_parameters,omitempty"`
BodySize int `json:"body_size" search:"long"` //set even when body is truncated/omitted
Body string `json:"body,omitempty"` //json body as a string
}
type ApiLogResponse struct {
Headers map[string]string `json:"headers,omitempty"`
BodySize int `json:"body_size"` //set even when body is truncated/omitted
Body string `json:"body,omitempty"` //json content as a string
}