Added global profiler setting, options to apigw handlers
This commit is contained in:
parent
476084a3bf
commit
f33336b21d
@ -723,6 +723,7 @@ func updatePasswdSettings(opt options.AuthOpt, current *types.AppSettings) {
|
||||
|
||||
func updateApigwSettings(opt options.ApigwOpt, current *types.AppSettings) {
|
||||
current.Apigw.ProfilerEnabled = opt.ProfilerEnabled
|
||||
current.Apigw.ProfilerGlobal = opt.ProfilerGlobal
|
||||
}
|
||||
|
||||
// Checks if discovery is enabled in the options
|
||||
|
||||
@ -29,6 +29,10 @@ apigw: schema.#optionsGroup & {
|
||||
type: "bool"
|
||||
description: "Enable profiler"
|
||||
}
|
||||
profiler_global: {
|
||||
type: "bool"
|
||||
description: "Profiler enabled for all routes"
|
||||
}
|
||||
log_request_body: {
|
||||
type: "bool"
|
||||
description: "Enable incoming request body output in logs"
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
"github.com/cortezaproject/corteza-server/pkg/apigw/types"
|
||||
errors "github.com/cortezaproject/corteza-server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza-server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
)
|
||||
|
||||
type (
|
||||
@ -55,7 +56,7 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func NewRedirection() (e *redirection) {
|
||||
func NewRedirection(opts *options.ApigwOpt) (e *redirection) {
|
||||
e = &redirection{}
|
||||
|
||||
e.Name = "redirection"
|
||||
@ -78,8 +79,12 @@ func NewRedirection() (e *redirection) {
|
||||
return
|
||||
}
|
||||
|
||||
func (h redirection) New() types.Handler {
|
||||
return NewRedirection()
|
||||
func (h redirection) New(opts *options.ApigwOpt) types.Handler {
|
||||
return NewRedirection(opts)
|
||||
}
|
||||
|
||||
func (h redirection) Enabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (h redirection) String() string {
|
||||
@ -124,7 +129,7 @@ func (h redirection) Handler() types.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func NewDefaultJsonResponse() (e *defaultJsonResponse) {
|
||||
func NewDefaultJsonResponse(opts *options.ApigwOpt) (e *defaultJsonResponse) {
|
||||
e = &defaultJsonResponse{}
|
||||
|
||||
e.Name = "defaultJsonResponse"
|
||||
@ -134,8 +139,12 @@ func NewDefaultJsonResponse() (e *defaultJsonResponse) {
|
||||
return
|
||||
}
|
||||
|
||||
func (j defaultJsonResponse) New() types.Handler {
|
||||
return NewDefaultJsonResponse()
|
||||
func (j defaultJsonResponse) New(opts *options.ApigwOpt) types.Handler {
|
||||
return NewDefaultJsonResponse(opts)
|
||||
}
|
||||
|
||||
func (j defaultJsonResponse) Enabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (j defaultJsonResponse) String() string {
|
||||
@ -172,7 +181,7 @@ func checkStatus(typ string, status int) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func NewJsonResponse(reg typesRegistry) (e *jsonResponse) {
|
||||
func NewJsonResponse(opts *options.ApigwOpt, reg typesRegistry) (e *jsonResponse) {
|
||||
e = &jsonResponse{}
|
||||
|
||||
e.Name = "jsonResponse"
|
||||
@ -192,8 +201,12 @@ func NewJsonResponse(reg typesRegistry) (e *jsonResponse) {
|
||||
return
|
||||
}
|
||||
|
||||
func (j jsonResponse) New() types.Handler {
|
||||
return NewJsonResponse(j.reg)
|
||||
func (j jsonResponse) New(opts *options.ApigwOpt) types.Handler {
|
||||
return NewJsonResponse(opts, j.reg)
|
||||
}
|
||||
|
||||
func (j jsonResponse) Enabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (j jsonResponse) String() string {
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
agctx "github.com/cortezaproject/corteza-server/pkg/apigw/ctx"
|
||||
"github.com/cortezaproject/corteza-server/pkg/apigw/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@ -34,7 +35,7 @@ func Test_redirectionMerge(t *testing.T) {
|
||||
)
|
||||
|
||||
for _, tc := range tcc {
|
||||
t.Run(tc.name, testMerge(NewRedirection(), tc))
|
||||
t.Run(tc.name, testMerge(NewRedirection(&options.ApigwOpt{}), tc))
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,7 +75,7 @@ func Test_redirection(t *testing.T) {
|
||||
rc = httptest.NewRecorder()
|
||||
)
|
||||
|
||||
h := getHandler(NewRedirection())
|
||||
h := getHandler(NewRedirection(&options.ApigwOpt{}))
|
||||
h, err := h.Merge([]byte(tc.expr))
|
||||
|
||||
req.NoError(err)
|
||||
@ -144,7 +145,7 @@ func Test_jsonResponse(t *testing.T) {
|
||||
|
||||
r = r.WithContext(agctx.ScopeToContext(context.Background(), scope))
|
||||
|
||||
h := getHandler(NewJsonResponse(&mockHandlerRegistry{}))
|
||||
h := getHandler(NewJsonResponse(&options.ApigwOpt{}, &mockHandlerRegistry{}))
|
||||
h, err := h.Merge([]byte(tc.expr))
|
||||
|
||||
req.NoError(err)
|
||||
|
||||
@ -13,6 +13,7 @@ import (
|
||||
"github.com/cortezaproject/corteza-server/pkg/apigw/types"
|
||||
pe "github.com/cortezaproject/corteza-server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza-server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
)
|
||||
|
||||
type (
|
||||
@ -41,11 +42,12 @@ type (
|
||||
}
|
||||
|
||||
profiler struct {
|
||||
opts *options.ApigwOpt
|
||||
types.FilterMeta
|
||||
}
|
||||
)
|
||||
|
||||
func NewHeader() (v *header) {
|
||||
func NewHeader(opts *options.ApigwOpt) (v *header) {
|
||||
v = &header{}
|
||||
|
||||
v.Name = "header"
|
||||
@ -63,8 +65,12 @@ func NewHeader() (v *header) {
|
||||
return
|
||||
}
|
||||
|
||||
func (h header) New() types.Handler {
|
||||
return NewHeader()
|
||||
func (h header) New(opts *options.ApigwOpt) types.Handler {
|
||||
return NewHeader(opts)
|
||||
}
|
||||
|
||||
func (h header) Enabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (h header) String() string {
|
||||
@ -129,7 +135,7 @@ func (h header) Handler() types.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func NewQueryParam() (v *queryParam) {
|
||||
func NewQueryParam(opts *options.ApigwOpt) (v *queryParam) {
|
||||
v = &queryParam{}
|
||||
|
||||
v.Name = "queryParam"
|
||||
@ -147,8 +153,12 @@ func NewQueryParam() (v *queryParam) {
|
||||
return
|
||||
}
|
||||
|
||||
func (qp queryParam) New() types.Handler {
|
||||
return NewQueryParam()
|
||||
func (qp queryParam) New(opts *options.ApigwOpt) types.Handler {
|
||||
return NewQueryParam(opts)
|
||||
}
|
||||
|
||||
func (qp queryParam) Enabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (qp queryParam) String() string {
|
||||
@ -213,9 +223,10 @@ func (qp *queryParam) Handler() types.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func NewProfiler() (pp *profiler) {
|
||||
func NewProfiler(opts *options.ApigwOpt) (pp *profiler) {
|
||||
pp = &profiler{}
|
||||
|
||||
pp.opts = opts
|
||||
pp.Name = "profiler"
|
||||
pp.Label = "Profiler"
|
||||
pp.Kind = types.PreFilter
|
||||
@ -223,8 +234,12 @@ func NewProfiler() (pp *profiler) {
|
||||
return
|
||||
}
|
||||
|
||||
func (pr profiler) New() types.Handler {
|
||||
return NewProfiler()
|
||||
func (pr profiler) New(opts *options.ApigwOpt) types.Handler {
|
||||
return NewProfiler(opts)
|
||||
}
|
||||
|
||||
func (pr profiler) Enabled() bool {
|
||||
return pr.opts.ProfilerEnabled
|
||||
}
|
||||
|
||||
func (pr profiler) String() string {
|
||||
@ -246,8 +261,8 @@ func (pr *profiler) Handler() types.HandlerFunc {
|
||||
scope = agctx.ScopeFromContext(ctx)
|
||||
)
|
||||
|
||||
if scope.Opts().ProfilerEnabled {
|
||||
// profiler enabled overrides any profiling prefilter
|
||||
if pr.opts.ProfilerEnabled && pr.opts.ProfilerGlobal {
|
||||
// profiler global overrides any profiling prefilter
|
||||
// the hit is registered on lower level
|
||||
return
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/apigw/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@ -33,7 +34,7 @@ func Test_headerMerge(t *testing.T) {
|
||||
)
|
||||
|
||||
for _, tc := range tcc {
|
||||
t.Run(tc.name, testMerge(NewHeader(), tc))
|
||||
t.Run(tc.name, testMerge(NewHeader(&options.ApigwOpt{}), tc))
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,7 +80,7 @@ func Test_headerHandle(t *testing.T) {
|
||||
r := httptest.NewRequest(http.MethodGet, "/foo", http.NoBody)
|
||||
r.Header = tc.headers
|
||||
|
||||
t.Run(tc.name, testHandle(NewHeader(), r, tc))
|
||||
t.Run(tc.name, testHandle(NewHeader(&options.ApigwOpt{}), r, tc))
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,7 +112,7 @@ func Test_queryParamMerge(t *testing.T) {
|
||||
)
|
||||
|
||||
for _, tc := range tcc {
|
||||
t.Run(tc.name, testMerge(NewQueryParam(), tc))
|
||||
t.Run(tc.name, testMerge(NewQueryParam(&options.ApigwOpt{}), tc))
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,7 +145,7 @@ func Test_queryParamHandle(t *testing.T) {
|
||||
|
||||
for _, tc := range tcc {
|
||||
r := httptest.NewRequest(http.MethodGet, tc.url, http.NoBody)
|
||||
t.Run(tc.name, testHandle(NewQueryParam(), r, tc))
|
||||
t.Run(tc.name, testHandle(NewQueryParam(&options.ApigwOpt{}), r, tc))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ import (
|
||||
pe "github.com/cortezaproject/corteza-server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza-server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza-server/pkg/jsenv"
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@ -47,7 +48,7 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func NewWorkflow(wf WfExecer) (p *workflow) {
|
||||
func NewWorkflow(opts *options.ApigwOpt, wf WfExecer) (p *workflow) {
|
||||
p = &workflow{}
|
||||
|
||||
p.d = wf
|
||||
@ -67,8 +68,12 @@ func NewWorkflow(wf WfExecer) (p *workflow) {
|
||||
return
|
||||
}
|
||||
|
||||
func (h workflow) New() types.Handler {
|
||||
return NewWorkflow(h.d)
|
||||
func (h workflow) New(opts *options.ApigwOpt) types.Handler {
|
||||
return NewWorkflow(opts, h.d)
|
||||
}
|
||||
|
||||
func (h workflow) Enabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (h workflow) String() string {
|
||||
@ -147,7 +152,7 @@ func (h workflow) Handler() types.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func NewPayload(l *zap.Logger) (p *processerPayload) {
|
||||
func NewPayload(opts *options.ApigwOpt, l *zap.Logger) (p *processerPayload) {
|
||||
p = &processerPayload{}
|
||||
|
||||
p.vm = jsenv.New(jsenv.NewTransformer(jsenv.LoaderJS, jsenv.TargetES2016))
|
||||
@ -171,8 +176,12 @@ func NewPayload(l *zap.Logger) (p *processerPayload) {
|
||||
return
|
||||
}
|
||||
|
||||
func (h processerPayload) New() types.Handler {
|
||||
return NewPayload(h.log)
|
||||
func (h processerPayload) New(opts *options.ApigwOpt) types.Handler {
|
||||
return NewPayload(opts, h.log)
|
||||
}
|
||||
|
||||
func (h processerPayload) Enabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (h processerPayload) String() string {
|
||||
|
||||
@ -78,7 +78,7 @@ func Test_processerWorkflow(t *testing.T) {
|
||||
rc = httptest.NewRecorder()
|
||||
rq, _ = http.NewRequest("POST", "/foo", http.NoBody)
|
||||
ar, err = h.NewRequest(rq)
|
||||
pp = NewWorkflow(tc.wfs)
|
||||
pp = NewWorkflow(&options.ApigwOpt{}, tc.wfs)
|
||||
)
|
||||
|
||||
_, err = pp.Merge([]byte(tc.params))
|
||||
@ -173,7 +173,7 @@ func Test_processerPayload(t *testing.T) {
|
||||
ar, err = h.NewRequest(tc.rq)
|
||||
)
|
||||
|
||||
pp := NewPayload(zap.NewNop())
|
||||
pp := NewPayload(&options.ApigwOpt{}, zap.NewNop())
|
||||
_, err = pp.Merge([]byte(tc.params))
|
||||
|
||||
if tc.errv != "" {
|
||||
|
||||
@ -14,6 +14,7 @@ import (
|
||||
actx "github.com/cortezaproject/corteza-server/pkg/apigw/ctx"
|
||||
"github.com/cortezaproject/corteza-server/pkg/apigw/types"
|
||||
pe "github.com/cortezaproject/corteza-server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@ -45,7 +46,7 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func New(l *zap.Logger, c *http.Client, s types.SecureStorager) (p *proxy) {
|
||||
func New(opts *options.ApigwOpt, l *zap.Logger, c *http.Client, s types.SecureStorager) (p *proxy) {
|
||||
p = &proxy{}
|
||||
|
||||
p.c = c
|
||||
@ -67,8 +68,12 @@ func New(l *zap.Logger, c *http.Client, s types.SecureStorager) (p *proxy) {
|
||||
return
|
||||
}
|
||||
|
||||
func (h proxy) New() types.Handler {
|
||||
return New(h.log, h.c, h.s)
|
||||
func (h proxy) New(opts *options.ApigwOpt) types.Handler {
|
||||
return New(opts, h.log, h.c, h.s)
|
||||
}
|
||||
|
||||
func (h proxy) Enabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (h proxy) String() string {
|
||||
|
||||
@ -174,7 +174,7 @@ func Test_proxy(t *testing.T) {
|
||||
rq = httptest.NewRequest("POST", "/foo", strings.NewReader(`custom request body`))
|
||||
}
|
||||
|
||||
proxy := New(zap.NewNop(), c, struct{}{})
|
||||
proxy := New(&options.ApigwOpt{}, zap.NewNop(), c, struct{}{})
|
||||
_, err := proxy.Merge([]byte(tc.params))
|
||||
req.NoError(err)
|
||||
|
||||
|
||||
@ -41,7 +41,7 @@ func helperMethodNotAllowed(opt *options.ApigwOpt, pr *profiler.Profiler) http.H
|
||||
}
|
||||
|
||||
func addToProfiler(opt *options.ApigwOpt, pr *profiler.Profiler, r *http.Request, status int) {
|
||||
if !opt.ProfilerEnabled {
|
||||
if !(opt.ProfilerEnabled && opt.ProfilerGlobal) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
79
pkg/apigw/profiler/filter.go
Normal file
79
pkg/apigw/profiler/filter.go
Normal file
@ -0,0 +1,79 @@
|
||||
package profiler
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
|
||||
func FilterAggregation(list *types.ApigwProfilerAggregationSet, filter *types.ApigwProfilerFilter) {
|
||||
var (
|
||||
dec string = ""
|
||||
i uint = 0
|
||||
b = filter.Before == ""
|
||||
)
|
||||
|
||||
if filter.Limit == 0 {
|
||||
filter.Limit = FILTER_NUM_AGG_ITEMS
|
||||
}
|
||||
|
||||
dec, _ = decodeRoutePath(filter.Before)
|
||||
|
||||
*list, _ = list.Filter(func(apa *types.ApigwProfilerAggregation) (bool, error) {
|
||||
// after a specific hit and inside the limits
|
||||
if b && i < filter.Limit {
|
||||
i++
|
||||
filter.Next = encodeRoutePath(apa.Path)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// after the specific hit check
|
||||
if dec != "" && b == false {
|
||||
b = apa.Path == dec
|
||||
}
|
||||
|
||||
return false, nil
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func FilterHits(list *types.ApigwProfilerHitSet, filter *types.ApigwProfilerFilter) {
|
||||
var (
|
||||
i uint = 0
|
||||
b = filter.Before == ""
|
||||
)
|
||||
|
||||
if filter.Limit == 0 {
|
||||
filter.Limit = FILTER_NUM_ITEMS
|
||||
}
|
||||
|
||||
*list, _ = list.Filter(func(aph *types.ApigwProfilerHit) (bool, error) {
|
||||
// after a specific hit and inside the limits
|
||||
if b && i < filter.Limit {
|
||||
i++
|
||||
filter.Next = aph.ID
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// after the specific hit check
|
||||
if filter.Before != "" && b == false {
|
||||
b = aph.ID == filter.Before
|
||||
}
|
||||
|
||||
return false, nil
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func encodeRoutePath(p string) string {
|
||||
return base64.URLEncoding.EncodeToString([]byte(p))
|
||||
}
|
||||
|
||||
func decodeRoutePath(p string) (s string, err error) {
|
||||
b, err := base64.URLEncoding.DecodeString(p)
|
||||
s = string(b)
|
||||
|
||||
return
|
||||
}
|
||||
16
pkg/apigw/profiler/handler.go
Normal file
16
pkg/apigw/profiler/handler.go
Normal file
@ -0,0 +1,16 @@
|
||||
package profiler
|
||||
|
||||
import "net/http"
|
||||
|
||||
func StartHandler(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
// add some info to context
|
||||
next.ServeHTTP(rw, r)
|
||||
})
|
||||
}
|
||||
|
||||
func FinishHandler(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
next.ServeHTTP(rw, r)
|
||||
})
|
||||
}
|
||||
@ -4,11 +4,9 @@ import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
h "github.com/cortezaproject/corteza-server/pkg/http"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -19,9 +17,6 @@ const (
|
||||
FILTER_NUM_AGG_ITEMS = 10
|
||||
)
|
||||
|
||||
var sortAggFields = []string{"path", "count", "size_min", "size_max", "size_avg", "time_min", "time_max", "time_avg"}
|
||||
var sortRouteFields = []string{"time_start", "time_finish", "time_duration"}
|
||||
|
||||
type (
|
||||
Hits map[string][]*Hit
|
||||
|
||||
@ -48,13 +43,6 @@ type (
|
||||
Ts *time.Time
|
||||
Tf *time.Time
|
||||
}
|
||||
|
||||
Sort struct {
|
||||
Hit string
|
||||
Path string
|
||||
Size uint64
|
||||
Before string
|
||||
}
|
||||
)
|
||||
|
||||
func New() *Profiler {
|
||||
@ -89,7 +77,7 @@ func (p *Profiler) Push(h *Hit) (id string) {
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Profiler) Dump(s Sort) Hits {
|
||||
func (p *Profiler) Hits(s Sort) Hits {
|
||||
ll := p.l.Filter(func(k string, v *Hit) bool {
|
||||
var b bool = true
|
||||
|
||||
@ -131,19 +119,6 @@ func (s Hits) Filter(fn func(k string, v *Hit) bool) Hits {
|
||||
return ss
|
||||
}
|
||||
|
||||
func StartHandler(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
// add some info to context
|
||||
next.ServeHTTP(rw, r)
|
||||
})
|
||||
}
|
||||
|
||||
func FinishHandler(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
next.ServeHTTP(rw, r)
|
||||
})
|
||||
}
|
||||
|
||||
func (h Hits) Len() int {
|
||||
return h.Len()
|
||||
}
|
||||
@ -155,147 +130,3 @@ func (h Hits) Less(i, j int) bool {
|
||||
func (h Hits) Swap(i, j int) {
|
||||
return
|
||||
}
|
||||
|
||||
func SortAggregation(list *types.ApigwProfilerAggregationSet, filter *types.ApigwProfilerFilter) {
|
||||
for _, ff := range sortAggFields {
|
||||
fe := filter.Sort.Get(ff)
|
||||
|
||||
if fe == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if filter.Sort.Get(ff).Descending {
|
||||
sort.Sort(sort.Reverse(getSortType(ff, list)))
|
||||
break
|
||||
}
|
||||
|
||||
sort.Sort(getSortType(ff, list))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func FilterAggregation(list *types.ApigwProfilerAggregationSet, filter *types.ApigwProfilerFilter) {
|
||||
var (
|
||||
dec string = ""
|
||||
i uint = 0
|
||||
b = filter.Before == ""
|
||||
)
|
||||
|
||||
if filter.Limit == 0 {
|
||||
filter.Limit = FILTER_NUM_AGG_ITEMS
|
||||
}
|
||||
|
||||
dec, _ = decodeRoutePath(filter.Before)
|
||||
|
||||
*list, _ = list.Filter(func(apa *types.ApigwProfilerAggregation) (bool, error) {
|
||||
// after a specific hit and inside the limits
|
||||
if b && i < filter.Limit {
|
||||
i++
|
||||
filter.Next = encodeRoutePath(apa.Path)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// after the specific hit check
|
||||
if dec != "" && b == false {
|
||||
b = apa.Path == dec
|
||||
}
|
||||
|
||||
return false, nil
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func FilterHits(list *types.ApigwProfilerHitSet, filter *types.ApigwProfilerFilter) {
|
||||
var (
|
||||
i uint = 0
|
||||
b = filter.Before == ""
|
||||
)
|
||||
|
||||
if filter.Limit == 0 {
|
||||
filter.Limit = FILTER_NUM_ITEMS
|
||||
}
|
||||
|
||||
*list, _ = list.Filter(func(aph *types.ApigwProfilerHit) (bool, error) {
|
||||
// after a specific hit and inside the limits
|
||||
if b && i < filter.Limit {
|
||||
i++
|
||||
filter.Next = aph.ID
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// after the specific hit check
|
||||
if filter.Before != "" && b == false {
|
||||
b = aph.ID == filter.Before
|
||||
}
|
||||
|
||||
return false, nil
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func SortHits(list *types.ApigwProfilerHitSet, filter *types.ApigwProfilerFilter) {
|
||||
for _, ff := range sortRouteFields {
|
||||
fe := filter.Sort.Get(ff)
|
||||
|
||||
if fe == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if filter.Sort.Get(ff).Descending {
|
||||
sort.Sort(sort.Reverse(getSortTypeHit(ff, list)))
|
||||
break
|
||||
}
|
||||
|
||||
sort.Sort(getSortTypeHit(ff, list))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func getSortType(s string, list *types.ApigwProfilerAggregationSet) sort.Interface {
|
||||
switch s {
|
||||
case "path":
|
||||
return types.ByPath(*list)
|
||||
case "count":
|
||||
return types.ByCount(*list)
|
||||
case "size_min":
|
||||
return types.BySizeMin(*list)
|
||||
case "size_max":
|
||||
return types.BySizeMax(*list)
|
||||
case "size_avg":
|
||||
return types.BySizeAvg(*list)
|
||||
case "time_min":
|
||||
return types.ByTimeMin(*list)
|
||||
case "time_max":
|
||||
return types.ByTimeMax(*list)
|
||||
case "time_avg":
|
||||
return types.ByTimeAvg(*list)
|
||||
default:
|
||||
return types.ByCount(*list)
|
||||
}
|
||||
}
|
||||
|
||||
func getSortTypeHit(s string, list *types.ApigwProfilerHitSet) sort.Interface {
|
||||
switch s {
|
||||
case "time_start":
|
||||
return types.BySTime(*list)
|
||||
case "time_finish":
|
||||
return types.ByFTime(*list)
|
||||
case "time_duration":
|
||||
return types.ByDuration(*list)
|
||||
default:
|
||||
return types.BySTime(*list)
|
||||
}
|
||||
}
|
||||
|
||||
func encodeRoutePath(p string) string {
|
||||
return base64.URLEncoding.EncodeToString([]byte(p))
|
||||
}
|
||||
|
||||
func decodeRoutePath(p string) (s string, err error) {
|
||||
b, err := base64.URLEncoding.DecodeString(p)
|
||||
s = string(b)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
93
pkg/apigw/profiler/sort.go
Normal file
93
pkg/apigw/profiler/sort.go
Normal file
@ -0,0 +1,93 @@
|
||||
package profiler
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
|
||||
var (
|
||||
sortAggFields = []string{"path", "count", "size_min", "size_max", "size_avg", "time_min", "time_max", "time_avg"}
|
||||
sortRouteFields = []string{"time_start", "time_finish", "time_duration"}
|
||||
)
|
||||
|
||||
type (
|
||||
Sort struct {
|
||||
Hit string
|
||||
Path string
|
||||
Size uint64
|
||||
Before string
|
||||
}
|
||||
)
|
||||
|
||||
func SortAggregation(list *types.ApigwProfilerAggregationSet, filter *types.ApigwProfilerFilter) {
|
||||
for _, ff := range sortAggFields {
|
||||
fe := filter.Sort.Get(ff)
|
||||
|
||||
if fe == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if filter.Sort.Get(ff).Descending {
|
||||
sort.Sort(sort.Reverse(getSortType(ff, list)))
|
||||
break
|
||||
}
|
||||
|
||||
sort.Sort(getSortType(ff, list))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func SortHits(list *types.ApigwProfilerHitSet, filter *types.ApigwProfilerFilter) {
|
||||
for _, ff := range sortRouteFields {
|
||||
fe := filter.Sort.Get(ff)
|
||||
|
||||
if fe == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if filter.Sort.Get(ff).Descending {
|
||||
sort.Sort(sort.Reverse(getSortTypeHit(ff, list)))
|
||||
break
|
||||
}
|
||||
|
||||
sort.Sort(getSortTypeHit(ff, list))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func getSortType(s string, list *types.ApigwProfilerAggregationSet) sort.Interface {
|
||||
switch s {
|
||||
case "path":
|
||||
return types.ByPath(*list)
|
||||
case "count":
|
||||
return types.ByCount(*list)
|
||||
case "size_min":
|
||||
return types.BySizeMin(*list)
|
||||
case "size_max":
|
||||
return types.BySizeMax(*list)
|
||||
case "size_avg":
|
||||
return types.BySizeAvg(*list)
|
||||
case "time_min":
|
||||
return types.ByTimeMin(*list)
|
||||
case "time_max":
|
||||
return types.ByTimeMax(*list)
|
||||
case "time_avg":
|
||||
return types.ByTimeAvg(*list)
|
||||
default:
|
||||
return types.ByCount(*list)
|
||||
}
|
||||
}
|
||||
|
||||
func getSortTypeHit(s string, list *types.ApigwProfilerHitSet) sort.Interface {
|
||||
switch s {
|
||||
case "time_start":
|
||||
return types.BySTime(*list)
|
||||
case "time_finish":
|
||||
return types.ByFTime(*list)
|
||||
case "time_duration":
|
||||
return types.ByDuration(*list)
|
||||
default:
|
||||
return types.BySTime(*list)
|
||||
}
|
||||
}
|
||||
@ -8,19 +8,22 @@ import (
|
||||
"github.com/cortezaproject/corteza-server/pkg/apigw/filter"
|
||||
"github.com/cortezaproject/corteza-server/pkg/apigw/filter/proxy"
|
||||
"github.com/cortezaproject/corteza-server/pkg/apigw/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
)
|
||||
|
||||
type (
|
||||
Registry struct {
|
||||
h map[string]types.Handler
|
||||
opts *options.ApigwOpt
|
||||
h map[string]types.Handler
|
||||
}
|
||||
|
||||
secureStorageTodo struct{}
|
||||
)
|
||||
|
||||
func NewRegistry() *Registry {
|
||||
func NewRegistry(opts *options.ApigwOpt) *Registry {
|
||||
return &Registry{
|
||||
h: map[string]types.Handler{},
|
||||
h: map[string]types.Handler{},
|
||||
opts: opts,
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,11 +46,15 @@ func (r *Registry) Get(identifier string) (types.Handler, error) {
|
||||
return nil, fmt.Errorf("could not get element from registry: %s", identifier)
|
||||
}
|
||||
|
||||
return f.New(), nil
|
||||
return f.New(r.opts), nil
|
||||
}
|
||||
|
||||
func (r *Registry) All() (list types.FilterMetaList) {
|
||||
for _, handler := range r.h {
|
||||
if !handler.Enabled() {
|
||||
continue
|
||||
}
|
||||
|
||||
meta := handler.Meta()
|
||||
list = append(list, &meta)
|
||||
}
|
||||
@ -57,19 +64,19 @@ func (r *Registry) All() (list types.FilterMetaList) {
|
||||
|
||||
func (r *Registry) Preload() {
|
||||
// prefilters
|
||||
r.Add("queryParam", filter.NewQueryParam())
|
||||
r.Add("header", filter.NewHeader())
|
||||
r.Add("profiler", filter.NewProfiler())
|
||||
r.Add("queryParam", filter.NewQueryParam(r.opts))
|
||||
r.Add("header", filter.NewHeader(r.opts))
|
||||
r.Add("profiler", filter.NewProfiler(r.opts))
|
||||
|
||||
// processers
|
||||
r.Add("workflow", filter.NewWorkflow(NewWorkflow()))
|
||||
r.Add("proxy", proxy.New(service.DefaultLogger, http.DefaultClient, secureStorageTodo{}))
|
||||
r.Add("payload", filter.NewPayload(service.DefaultLogger))
|
||||
r.Add("workflow", filter.NewWorkflow(r.opts, NewWorkflow()))
|
||||
r.Add("proxy", proxy.New(r.opts, service.DefaultLogger, http.DefaultClient, secureStorageTodo{}))
|
||||
r.Add("payload", filter.NewPayload(r.opts, service.DefaultLogger))
|
||||
|
||||
// postfilters
|
||||
r.Add("redirection", filter.NewRedirection())
|
||||
r.Add("jsonResponse", filter.NewJsonResponse(service.Registry()))
|
||||
r.Add("defaultJsonResponse", filter.NewDefaultJsonResponse())
|
||||
r.Add("redirection", filter.NewRedirection(r.opts))
|
||||
r.Add("jsonResponse", filter.NewJsonResponse(r.opts, service.Registry()))
|
||||
r.Add("defaultJsonResponse", filter.NewDefaultJsonResponse(r.opts))
|
||||
}
|
||||
|
||||
func NewWorkflow() (wf filter.WfExecer) {
|
||||
|
||||
@ -4,13 +4,14 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/apigw/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_registryAddGet(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
r = NewRegistry()
|
||||
r = NewRegistry(&options.ApigwOpt{})
|
||||
)
|
||||
|
||||
r.Add("mockHandler", types.MockHandler{})
|
||||
@ -25,7 +26,7 @@ func Test_registryAddGet(t *testing.T) {
|
||||
func Test_registryAddGetErr(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
r = NewRegistry()
|
||||
r = NewRegistry(&options.ApigwOpt{})
|
||||
)
|
||||
|
||||
r.Add("mockHandler", types.MockHandler{})
|
||||
@ -71,7 +72,7 @@ func Test_registryMerge(t *testing.T) {
|
||||
for _, tc := range tcc {
|
||||
var (
|
||||
req = require.New(t)
|
||||
r = NewRegistry()
|
||||
r = NewRegistry(&options.ApigwOpt{})
|
||||
)
|
||||
|
||||
m, err := r.Merge(types.MockHandler{}, []byte(tc.params))
|
||||
@ -89,7 +90,7 @@ func Test_registryMerge(t *testing.T) {
|
||||
func Test_registryAll(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
r = NewRegistry()
|
||||
r = NewRegistry(&options.ApigwOpt{})
|
||||
)
|
||||
|
||||
r.Add("mockHandler", types.MockHandler{})
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
"github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
h "github.com/cortezaproject/corteza-server/pkg/http"
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@ -54,8 +55,11 @@ func (r route) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
scope.Set("opts", r.opts)
|
||||
scope.Set("request", ar)
|
||||
|
||||
spew.Dump("OPTS", r.opts)
|
||||
|
||||
// use profiler, override any profiling prefilter
|
||||
if r.opts.ProfilerEnabled {
|
||||
if r.opts.ProfilerEnabled && r.opts.ProfilerGlobal {
|
||||
spew.Dump("adding to profiler")
|
||||
// add request to profiler
|
||||
hit = r.pr.Hit(ar)
|
||||
hit.Route = r.ID
|
||||
@ -66,20 +70,28 @@ func (r route) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
r.handler.ServeHTTP(w, req)
|
||||
|
||||
if r.opts.ProfilerEnabled {
|
||||
r.pr.Push(hit)
|
||||
} else {
|
||||
if hit = actx.ProfilerFromContext(req.Context()).(*profiler.Hit); hit != nil && hit.R != nil {
|
||||
// updated hit from a possible prefilter
|
||||
// we need to push route ID even if the profiler is disabled
|
||||
hit.Route = r.ID
|
||||
r.pr.Push(hit)
|
||||
}
|
||||
}
|
||||
|
||||
r.log.Debug("finished serving route",
|
||||
zap.Duration("duration", time.Since(start)),
|
||||
)
|
||||
|
||||
if !r.opts.ProfilerEnabled {
|
||||
return
|
||||
}
|
||||
|
||||
if r.opts.ProfilerGlobal {
|
||||
r.pr.Push(hit)
|
||||
return
|
||||
}
|
||||
|
||||
if hit = actx.ProfilerFromContext(req.Context()).(*profiler.Hit); hit != nil && hit.R != nil {
|
||||
// updated hit from a possible prefilter
|
||||
// we need to push route ID even if the profiler is disabled
|
||||
hit.Route = r.ID
|
||||
hit.Status = http.StatusOK
|
||||
|
||||
r.pr.Push(hit)
|
||||
r.log.Debug("pushing request to profiler")
|
||||
}
|
||||
}
|
||||
|
||||
func (r route) String() string {
|
||||
|
||||
@ -59,7 +59,7 @@ func Setup(opts *options.ApigwOpt, log *zap.Logger, storer storer) {
|
||||
func New(opts *options.ApigwOpt, logger *zap.Logger, storer storer) *apigw {
|
||||
var (
|
||||
pr = profiler.New()
|
||||
reg = registry.NewRegistry()
|
||||
reg = registry.NewRegistry(opts)
|
||||
)
|
||||
|
||||
reg.Preload()
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/apigw/registry"
|
||||
"github.com/cortezaproject/corteza-server/pkg/apigw/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
st "github.com/cortezaproject/corteza-server/system/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap"
|
||||
@ -159,7 +160,7 @@ func Test_serviceInit(t *testing.T) {
|
||||
ctx = context.Background()
|
||||
)
|
||||
|
||||
reg := registry.NewRegistry()
|
||||
reg := registry.NewRegistry(&options.ApigwOpt{})
|
||||
|
||||
for hn, h := range tc.reg {
|
||||
reg.Add(hn, h)
|
||||
|
||||
@ -3,6 +3,8 @@ package types
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
)
|
||||
|
||||
type (
|
||||
@ -18,9 +20,10 @@ type (
|
||||
HTTPHandler
|
||||
fmt.Stringer
|
||||
|
||||
New() Handler
|
||||
New(*options.ApigwOpt) Handler
|
||||
Merge([]byte) (Handler, error)
|
||||
Meta() FilterMeta
|
||||
Enabled() bool
|
||||
}
|
||||
|
||||
HandlerFunc func(rw http.ResponseWriter, r *http.Request) error
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
st "github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
|
||||
@ -32,10 +33,14 @@ type (
|
||||
MockRoundTripper func(*http.Request) (*http.Response, error)
|
||||
)
|
||||
|
||||
func (h MockHandler) New() Handler {
|
||||
func (h MockHandler) New(opts *options.ApigwOpt) Handler {
|
||||
return MockHandler{}
|
||||
}
|
||||
|
||||
func (h MockHandler) Enabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (h MockHandler) String() string {
|
||||
return "MockHandler"
|
||||
}
|
||||
|
||||
1
pkg/options/options.gen.go
generated
1
pkg/options/options.gen.go
generated
@ -86,6 +86,7 @@ type (
|
||||
Debug bool `env:"APIGW_DEBUG"`
|
||||
LogEnabled bool `env:"APIGW_LOG_ENABLED"`
|
||||
ProfilerEnabled bool `env:"APIGW_PROFILER_ENABLED"`
|
||||
ProfilerGlobal bool `env:"APIGW_PROFILER_GLOBAL"`
|
||||
LogRequestBody bool `env:"APIGW_LOG_REQUEST_BODY"`
|
||||
ProxyEnableDebugLog bool `env:"APIGW_PROXY_ENABLE_DEBUG_LOG"`
|
||||
ProxyFollowRedirects bool `env:"APIGW_PROXY_FOLLOW_REDIRECTS"`
|
||||
|
||||
@ -212,6 +212,7 @@ type (
|
||||
// This only holds the value of APIGW_PROFILER_ENABLED for now
|
||||
//
|
||||
ProfilerEnabled bool `kv:"-" json:"profilerEnabled"`
|
||||
ProfilerGlobal bool `kv:"-" json:"profilerGlobal"`
|
||||
} `kv:"apigw" json:"apigw"`
|
||||
|
||||
// UserInterface settings
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user