New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
#79 record polling history #90
Changes from all commits
ce1007f
f3fffdb
cd7058f
ec27487
1d491fc
ec61e3d
205d991
7eeeede
01f662a
432f862
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -96,11 +96,53 @@ db.createCollection( "view", { | |
} | ||
} } | ||
} ); | ||
|
||
db.createCollection( "polling_detail", { | ||
validator: { $jsonSchema: { | ||
bsonType: "object", | ||
required: [ "id","polling_date","ip","user_agent","url_path","response_body","response_header" ], | ||
properties: { | ||
id: { | ||
bsonType: "string", | ||
}, | ||
session_id: { | ||
bsonType: "string", | ||
}, | ||
domain: { | ||
bsonType: "string", | ||
}, | ||
polling_date: { | ||
bsonType: "string" | ||
}, | ||
ip: { | ||
bsonType: "string" | ||
}, | ||
user_agent: { | ||
bsonType: "string" | ||
}, | ||
url_path: { | ||
bsonType: "string" | ||
}, | ||
response_body: { | ||
bsonType: "string" | ||
}, | ||
response_header: { | ||
bsonType: "string" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 不应该是个string应该是个bson object |
||
}, | ||
response_code: { | ||
bsonType: "string" | ||
} | ||
} | ||
} } | ||
} ); | ||
|
||
//index | ||
db.kv.createIndex({"id": 1}, { unique: true } ); | ||
db.kv.createIndex({key: 1, label_id: 1,domain:1,project:1},{ unique: true }); | ||
db.label.createIndex({"id": 1}, { unique: true } ); | ||
db.label.createIndex({format: 1,domain:1,project:1},{ unique: true }); | ||
db.polling_detail.createIndex({"id": 1}, { unique: true } ); | ||
db.polling_detail.createIndex({session:1,domain:1}, { unique: true } ); | ||
db.view.createIndex({"id": 1}, { unique: true } ); | ||
db.view.createIndex({display:1,domain:1,project:1},{ unique: true }); | ||
//db config | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package iputil | ||
|
||
import ( | ||
"net" | ||
"net/http" | ||
"strings" | ||
) | ||
|
||
//ClientIP try to get ip from http header | ||
func ClientIP(r *http.Request) string { | ||
xForwardedFor := r.Header.Get("X-Forwarded-For") | ||
ip := strings.TrimSpace(strings.Split(xForwardedFor, ",")[0]) | ||
if ip != "" { | ||
return ip | ||
} | ||
ip = strings.TrimSpace(r.Header.Get("X-Real-Ip")) | ||
if ip != "" { | ||
return ip | ||
} | ||
if ip, _, err := net.SplitHostPort(strings.TrimSpace(r.RemoteAddr)); err == nil { | ||
return ip | ||
} | ||
return "" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,3 +54,17 @@ type ViewResponse struct { | |
Total int `json:"total,omitempty"` | ||
Data []*ViewDoc `json:"data,omitempty"` | ||
} | ||
|
||
//PollingDetail record operation history | ||
type PollingDetail struct { | ||
ID string `json:"id,omitempty" yaml:"id,omitempty"` | ||
SessionID string `json:"session_id,omitempty" yaml:"session_id,omitempty"` | ||
Domain string `json:"domain,omitempty" yaml:"domain,omitempty"` | ||
PollingData map[string]interface{} `json:"polling_data,omitempty" yaml:"polling_data,omitempty"` | ||
IP string `json:"ip,omitempty" yaml:"ip,omitempty"` | ||
UserAgent string `json:"user_agent,omitempty" yaml:"user_agent,omitempty"` | ||
URLPath string `json:"url_path,omitempty" yaml:"url_path,omitempty"` | ||
ResponseBody string `json:"response_body,omitempty" yaml:"response_body,omitempty"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ResponseBody 用 struct |
||
ResponseHeader string `json:"response_header,omitempty" yaml:"response_header,omitempty"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ResponseHeader 用map[string][]string |
||
ResponseCode int `json:"response_code,omitempty" yaml:"response_code,omitempty"` | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,7 @@ import ( | |
"context" | ||
"encoding/json" | ||
"errors" | ||
"github.com/apache/servicecomb-kie/pkg/model" | ||
"github.com/apache/servicecomb-kie/server/pubsub" | ||
"github.com/apache/servicecomb-kie/server/service" | ||
uuid "github.com/satori/go.uuid" | ||
|
@@ -86,6 +87,15 @@ func ReadLabelCombinations(req *goRestful.Request) ([]map[string]string, error) | |
return labelCombinations, nil | ||
} | ||
|
||
//WriteErrResponseWithRecord write error message to client and record | ||
func WriteErrResponseWithRecord(context *restful.Context, status int, msg, contentType string, record *model.PollingDetail) { | ||
WriteErrResponse(context, status, msg, contentType) | ||
_, recordErr := service.RecordService.RecordFailed(context.Ctx, record, status, msg) | ||
if recordErr != nil { | ||
openlogging.Error("record detail error error:" + recordErr.Error()) | ||
} | ||
} | ||
|
||
//WriteErrResponse write error message to client | ||
func WriteErrResponse(context *restful.Context, status int, msg, contentType string) { | ||
context.WriteHeader(status) | ||
|
@@ -209,7 +219,7 @@ func checkStatus(status string) (string, error) { | |
} | ||
|
||
func queryAndResponse(rctx *restful.Context, | ||
domain interface{}, project string, key string, labels map[string]string, limit, offset int64, status string) { | ||
domain interface{}, project string, key string, labels map[string]string, limit, offset int64, status string, record *model.PollingDetail) { | ||
m := getMatchPattern(rctx) | ||
opts := []service.FindOption{ | ||
service.WithKey(key), | ||
|
@@ -226,20 +236,26 @@ func queryAndResponse(rctx *restful.Context, | |
kv, err := service.KVService.List(rctx.Ctx, domain.(string), project, opts...) | ||
if err != nil { | ||
if err == service.ErrKeyNotExists { | ||
WriteErrResponse(rctx, http.StatusNotFound, err.Error(), common.ContentTypeText) | ||
WriteErrResponseWithRecord(rctx, http.StatusNotFound, err.Error(), common.ContentTypeText, record) | ||
return | ||
} | ||
WriteErrResponse(rctx, http.StatusInternalServerError, err.Error(), common.ContentTypeText) | ||
WriteErrResponseWithRecord(rctx, http.StatusInternalServerError, err.Error(), common.ContentTypeText, record) | ||
return | ||
} | ||
rev, err := service.RevisionService.GetRevision(rctx.Ctx, domain.(string)) | ||
if err != nil { | ||
WriteErrResponse(rctx, http.StatusInternalServerError, err.Error(), common.ContentTypeText) | ||
WriteErrResponseWithRecord(rctx, http.StatusInternalServerError, err.Error(), common.ContentTypeText, record) | ||
return | ||
} | ||
rctx.ReadResponseWriter().Header().Set(common.HeaderRevision, strconv.FormatInt(rev, 10)) | ||
err = writeResponse(rctx, kv) | ||
if err != nil { | ||
openlogging.Error(err.Error()) | ||
} | ||
respData, _ := json.Marshal(kv) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 不需要序列化,直接存数据 |
||
respHeader, _ := json.Marshal(rctx.Resp.Header()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 不要序列化直接存map |
||
_, recordErr := service.RecordService.RecordSuccess(rctx.Ctx, record, http.StatusOK, string(respData), string(respHeader)) | ||
if recordErr != nil { | ||
openlogging.Error("record detail error error:" + recordErr.Error()) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,12 +21,14 @@ package v1 | |
import ( | ||
"fmt" | ||
"github.com/apache/servicecomb-kie/pkg/common" | ||
"github.com/apache/servicecomb-kie/pkg/iputil" | ||
"github.com/apache/servicecomb-kie/pkg/model" | ||
"github.com/apache/servicecomb-kie/server/pubsub" | ||
"github.com/apache/servicecomb-kie/server/service" | ||
goRestful "github.com/emicklei/go-restful" | ||
"github.com/go-chassis/go-chassis/server/restful" | ||
"github.com/go-mesh/openlogging" | ||
uuid "github.com/satori/go.uuid" | ||
"net/http" | ||
) | ||
|
||
|
@@ -108,13 +110,14 @@ func (r *KVResource) GetByKey(rctx *restful.Context) { | |
WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText) | ||
return | ||
} | ||
insID := rctx.ReadHeader("insId") | ||
statusStr := rctx.ReadQueryParameter("status") | ||
status, err := checkStatus(statusStr) | ||
if err != nil { | ||
WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText) | ||
return | ||
} | ||
returnData(rctx, domain, project, labels, limit, offset, status) | ||
returnData(rctx, domain, project, labels, limit, offset, status, insID) | ||
} | ||
|
||
//List response kv list | ||
|
@@ -138,21 +141,24 @@ func (r *KVResource) List(rctx *restful.Context) { | |
WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText) | ||
return | ||
} | ||
insID := rctx.ReadHeader("insId") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 改参数名 |
||
statusStr := rctx.ReadQueryParameter("status") | ||
status, err := checkStatus(statusStr) | ||
if err != nil { | ||
WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText) | ||
return | ||
} | ||
returnData(rctx, domain, project, labels, limit, offset, status) | ||
returnData(rctx, domain, project, labels, limit, offset, status, insID) | ||
} | ||
|
||
func returnData(rctx *restful.Context, domain interface{}, project string, labels map[string]string, limit, offset int64, status string) { | ||
func returnData(rctx *restful.Context, domain interface{}, project string, labels map[string]string, limit, offset int64, status, insID string) { | ||
revStr := rctx.ReadQueryParameter(common.QueryParamRev) | ||
wait := rctx.ReadQueryParameter(common.QueryParamWait) | ||
var recordErr error | ||
recordData := GenPollingDetail(rctx, revStr, wait, domain.(string), project, labels, limit, offset, insID) | ||
if revStr == "" { | ||
if wait == "" { | ||
queryAndResponse(rctx, domain, project, "", labels, limit, offset, status) | ||
queryAndResponse(rctx, domain, project, "", labels, limit, offset, status, recordData) | ||
return | ||
} | ||
changed, err := eventHappened(rctx, wait, &pubsub.Topic{ | ||
|
@@ -162,26 +168,27 @@ func returnData(rctx *restful.Context, domain interface{}, project string, label | |
DomainID: domain.(string), | ||
}) | ||
if err != nil { | ||
WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText) | ||
WriteErrResponseWithRecord(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText, recordData) | ||
return | ||
} | ||
if changed { | ||
queryAndResponse(rctx, domain, project, "", labels, limit, offset, status) | ||
queryAndResponse(rctx, domain, project, "", labels, limit, offset, status, recordData) | ||
return | ||
} | ||
rctx.WriteHeader(http.StatusNotModified) | ||
_, recordErr = service.RecordService.RecordSuccess(rctx.Ctx, recordData, http.StatusNotModified, "", "") | ||
} else { | ||
revised, err := isRevised(rctx.Ctx, revStr, domain.(string)) | ||
if err != nil { | ||
if err == ErrInvalidRev { | ||
WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText) | ||
WriteErrResponseWithRecord(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText, recordData) | ||
return | ||
} | ||
WriteErrResponse(rctx, http.StatusInternalServerError, err.Error(), common.ContentTypeText) | ||
WriteErrResponseWithRecord(rctx, http.StatusInternalServerError, err.Error(), common.ContentTypeText, recordData) | ||
return | ||
} | ||
if revised { | ||
queryAndResponse(rctx, domain, project, "", labels, limit, offset, status) | ||
queryAndResponse(rctx, domain, project, "", labels, limit, offset, status, recordData) | ||
return | ||
} else if wait != "" { | ||
changed, err := eventHappened(rctx, wait, &pubsub.Topic{ | ||
|
@@ -191,21 +198,47 @@ func returnData(rctx *restful.Context, domain interface{}, project string, label | |
DomainID: domain.(string), | ||
}) | ||
if err != nil { | ||
WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText) | ||
WriteErrResponseWithRecord(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText, recordData) | ||
return | ||
} | ||
if changed { | ||
queryAndResponse(rctx, domain, project, "", labels, limit, offset, status) | ||
queryAndResponse(rctx, domain, project, "", labels, limit, offset, status, recordData) | ||
return | ||
} | ||
rctx.WriteHeader(http.StatusNotModified) | ||
return | ||
_, recordErr = service.RecordService.RecordSuccess(rctx.Ctx, recordData, http.StatusNotModified, "", "") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 太多地方需要考虑调用,不如用defer+一个func,虽然损失性能,但是优雅易维护 |
||
} else { | ||
rctx.WriteHeader(http.StatusNotModified) | ||
_, recordErr = service.RecordService.RecordSuccess(rctx.Ctx, recordData, http.StatusNotModified, "", "") | ||
} | ||
if recordErr != nil { | ||
openlogging.Error("record detail error error:" + recordErr.Error()) | ||
} | ||
} | ||
} | ||
|
||
//GenPollingDetail to generate data after get or list | ||
func GenPollingDetail(context *restful.Context, revStr, wait, domain, project string, labels map[string]string, limit, offset int64, insID string) *model.PollingDetail { | ||
data := &model.PollingDetail{} | ||
data.ID = uuid.NewV4().String() | ||
data.SessionID = insID | ||
data.Domain = domain | ||
data.IP = iputil.ClientIP(context.Req.Request) | ||
dataMap := map[string]interface{}{ | ||
"revStr": revStr, | ||
"wait": wait, | ||
"domain": domain, | ||
"project": project, | ||
"labels": labels, | ||
"limit": limit, | ||
"offset": offset, | ||
} | ||
data.PollingData = dataMap | ||
data.UserAgent = context.Req.HeaderParameter("User-Agent") | ||
data.URLPath = context.ReadRequest().Method + " " + context.ReadRequest().URL.Path | ||
return data | ||
} | ||
|
||
//Search search key only by label | ||
func (r *KVResource) Search(context *restful.Context) { | ||
var err error | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
不应该是个string应该是个bson object