Added profiler to apigw
This commit is contained in:
@@ -1822,6 +1822,39 @@ endpoints:
|
||||
title: Proxy auth definitions
|
||||
path: "/proxy_auth/def"
|
||||
|
||||
- title: API Gateway profiler
|
||||
path: "/apigw/profiler"
|
||||
entrypoint: apigwProfiler
|
||||
authentication: []
|
||||
apis:
|
||||
- name: aggregation
|
||||
method: GET
|
||||
title: List aggregated list of routes
|
||||
path: "/"
|
||||
parameters:
|
||||
get:
|
||||
- { name: path, type: "string", title: "Filter by request path" }
|
||||
- { name: before, type: "string", title: "Entries before specified route" }
|
||||
- { name: sort, type: "string", title: "Sort items" }
|
||||
- { name: limit, type: "uint", title: "Limit" }
|
||||
- name: route
|
||||
method: GET
|
||||
title: List hits per route
|
||||
path: "/route/{routeID}"
|
||||
parameters:
|
||||
path:
|
||||
- { name: routeID, type: "string", title: "Route ID", required: true }
|
||||
get:
|
||||
- { name: path, type: "string", title: "Filter by request path" }
|
||||
- { name: before, type: "string", title: "Entries before specified hit ID" }
|
||||
- { name: sort, type: "string", title: "Sort items" }
|
||||
- { name: limit, type: "uint", title: "Limit" }
|
||||
- name: hit
|
||||
method: GET
|
||||
title: Hit details
|
||||
path: "/hit/{hitID}"
|
||||
parameters: { path: [ { name: hitID, type: string, required: true, title: "Hit ID" } ] }
|
||||
|
||||
- title: Locale
|
||||
entrypoint: locale
|
||||
path: "/locale"
|
||||
|
||||
158
system/rest/apigw_profiler.go
Normal file
158
system/rest/apigw_profiler.go
Normal file
@@ -0,0 +1,158 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza-server/system/rest/request"
|
||||
"github.com/cortezaproject/corteza-server/system/service"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
profilerService interface {
|
||||
Hits(context.Context, types.ApigwProfilerFilter) (types.ApigwProfilerHitSet, types.ApigwProfilerFilter, error)
|
||||
HitsAggregated(context.Context, types.ApigwProfilerFilter) (types.ApigwProfilerAggregationSet, types.ApigwProfilerFilter, error)
|
||||
}
|
||||
|
||||
ApigwProfiler struct {
|
||||
svc profilerService
|
||||
ac templateAccessController
|
||||
}
|
||||
|
||||
profilerRoutePayload struct {
|
||||
*types.ApigwProfilerHit
|
||||
}
|
||||
|
||||
profilerHitPayload struct {
|
||||
*types.ApigwProfilerAggregation
|
||||
}
|
||||
|
||||
profilerRouteSetPayload struct {
|
||||
Filter types.ApigwProfilerFilter `json:"filter"`
|
||||
Set []*profilerRoutePayload `json:"set"`
|
||||
}
|
||||
|
||||
profilerHitSetPayload struct {
|
||||
Filter types.ApigwProfilerFilter `json:"filter"`
|
||||
Set []*profilerHitPayload `json:"set"`
|
||||
}
|
||||
)
|
||||
|
||||
func (ApigwProfiler) New() *ApigwProfiler {
|
||||
return &ApigwProfiler{
|
||||
svc: service.DefaultApigwRoute,
|
||||
ac: service.DefaultAccessControl,
|
||||
}
|
||||
}
|
||||
|
||||
// List displays the the aggregated list of routes
|
||||
func (ctrl *ApigwProfiler) Aggregation(ctx context.Context, r *request.ApigwProfilerAggregation) (interface{}, error) {
|
||||
var (
|
||||
err error
|
||||
f = types.ApigwProfilerFilter{
|
||||
Path: r.Path,
|
||||
Before: r.Before,
|
||||
}
|
||||
)
|
||||
|
||||
if f.Sorting, err = filter.NewSorting(r.Sort); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if f.Paging, err = filter.NewPaging(r.Limit, ""); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
set, f, err := ctrl.svc.HitsAggregated(ctx, f)
|
||||
|
||||
return ctrl.makeFilterPayload(ctx, set, f, err)
|
||||
}
|
||||
|
||||
// Route displays the list of hits per-route
|
||||
func (ctrl *ApigwProfiler) Route(ctx context.Context, r *request.ApigwProfilerRoute) (interface{}, error) {
|
||||
var (
|
||||
err error
|
||||
f = types.ApigwProfilerFilter{
|
||||
Path: r.RouteID,
|
||||
Before: r.Before,
|
||||
}
|
||||
)
|
||||
|
||||
if f.Sorting, err = filter.NewSorting(r.Sort); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if f.Paging, err = filter.NewPaging(r.Limit, ""); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
set, f, err := ctrl.svc.Hits(ctx, f)
|
||||
|
||||
return ctrl.makeRouteFilterPayload(ctx, set, f, err)
|
||||
}
|
||||
|
||||
// Hit displays the details of a certain hit on a route
|
||||
func (ctrl *ApigwProfiler) Hit(ctx context.Context, r *request.ApigwProfilerHit) (interface{}, error) {
|
||||
var (
|
||||
f = types.ApigwProfilerFilter{
|
||||
Hit: r.HitID,
|
||||
}
|
||||
)
|
||||
|
||||
set, f, err := ctrl.svc.Hits(ctx, f)
|
||||
|
||||
if len(set) != 1 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return ctrl.makeRoutePayload(ctx, set[0], err)
|
||||
}
|
||||
|
||||
func (ctrl *ApigwProfiler) makePayload(ctx context.Context, q *types.ApigwProfilerAggregation, err error) (*profilerHitPayload, error) {
|
||||
if err != nil || q == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
qq := &profilerHitPayload{
|
||||
ApigwProfilerAggregation: q,
|
||||
}
|
||||
|
||||
return qq, nil
|
||||
}
|
||||
|
||||
func (ctrl *ApigwProfiler) makeFilterPayload(ctx context.Context, nn types.ApigwProfilerAggregationSet, f types.ApigwProfilerFilter, err error) (*profilerHitSetPayload, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msp := &profilerHitSetPayload{Filter: f, Set: make([]*profilerHitPayload, len(nn))}
|
||||
|
||||
for i := range nn {
|
||||
msp.Set[i], _ = ctrl.makePayload(ctx, nn[i], nil)
|
||||
}
|
||||
|
||||
return msp, nil
|
||||
}
|
||||
|
||||
func (ctrl *ApigwProfiler) makeRoutePayload(ctx context.Context, q *types.ApigwProfilerHit, err error) (*profilerRoutePayload, error) {
|
||||
if err != nil || q == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &profilerRoutePayload{q}, nil
|
||||
}
|
||||
|
||||
func (ctrl *ApigwProfiler) makeRouteFilterPayload(ctx context.Context, nn types.ApigwProfilerHitSet, f types.ApigwProfilerFilter, err error) (*profilerRouteSetPayload, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msp := &profilerRouteSetPayload{Filter: f, Set: make([]*profilerRoutePayload, len(nn))}
|
||||
|
||||
for i := range nn {
|
||||
msp.Set[i], _ = ctrl.makeRoutePayload(ctx, nn[i], nil)
|
||||
}
|
||||
|
||||
return msp, nil
|
||||
}
|
||||
95
system/rest/handlers/apigwProfiler.go
Normal file
95
system/rest/handlers/apigwProfiler.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package handlers
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
//
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/cortezaproject/corteza-server/pkg/api"
|
||||
"github.com/cortezaproject/corteza-server/system/rest/request"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type (
|
||||
// Internal API interface
|
||||
ApigwProfilerAPI interface {
|
||||
Aggregation(context.Context, *request.ApigwProfilerAggregation) (interface{}, error)
|
||||
Route(context.Context, *request.ApigwProfilerRoute) (interface{}, error)
|
||||
Hit(context.Context, *request.ApigwProfilerHit) (interface{}, error)
|
||||
}
|
||||
|
||||
// HTTP API interface
|
||||
ApigwProfiler struct {
|
||||
Aggregation func(http.ResponseWriter, *http.Request)
|
||||
Route func(http.ResponseWriter, *http.Request)
|
||||
Hit func(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
)
|
||||
|
||||
func NewApigwProfiler(h ApigwProfilerAPI) *ApigwProfiler {
|
||||
return &ApigwProfiler{
|
||||
Aggregation: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewApigwProfilerAggregation()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.Aggregation(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
Route: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewApigwProfilerRoute()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.Route(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
Hit: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewApigwProfilerHit()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.Hit(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (h ApigwProfiler) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) {
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middlewares...)
|
||||
r.Get("/apigw/profiler/", h.Aggregation)
|
||||
r.Get("/apigw/profiler/route/{routeID}", h.Route)
|
||||
r.Get("/apigw/profiler/hit/{hitID}", h.Hit)
|
||||
})
|
||||
}
|
||||
286
system/rest/request/apigwProfiler.go
Normal file
286
system/rest/request/apigwProfiler.go
Normal file
@@ -0,0 +1,286 @@
|
||||
package request
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
//
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza-server/pkg/payload"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// dummy vars to prevent
|
||||
// unused imports complain
|
||||
var (
|
||||
_ = chi.URLParam
|
||||
_ = multipart.ErrMessageTooLarge
|
||||
_ = payload.ParseUint64s
|
||||
_ = strings.ToLower
|
||||
_ = io.EOF
|
||||
_ = fmt.Errorf
|
||||
_ = json.NewEncoder
|
||||
)
|
||||
|
||||
type (
|
||||
// Internal API interface
|
||||
ApigwProfilerAggregation struct {
|
||||
// Path GET parameter
|
||||
//
|
||||
// Filter by request path
|
||||
Path string
|
||||
|
||||
// Before GET parameter
|
||||
//
|
||||
// Entries before specified route
|
||||
Before string
|
||||
|
||||
// Sort GET parameter
|
||||
//
|
||||
// Sort items
|
||||
Sort string
|
||||
|
||||
// Limit GET parameter
|
||||
//
|
||||
// Limit
|
||||
Limit uint
|
||||
}
|
||||
|
||||
ApigwProfilerRoute struct {
|
||||
// RouteID PATH parameter
|
||||
//
|
||||
// Route ID
|
||||
RouteID string
|
||||
|
||||
// Path GET parameter
|
||||
//
|
||||
// Filter by request path
|
||||
Path string
|
||||
|
||||
// Before GET parameter
|
||||
//
|
||||
// Entries before specified hit ID
|
||||
Before string
|
||||
|
||||
// Sort GET parameter
|
||||
//
|
||||
// Sort items
|
||||
Sort string
|
||||
|
||||
// Limit GET parameter
|
||||
//
|
||||
// Limit
|
||||
Limit uint
|
||||
}
|
||||
|
||||
ApigwProfilerHit struct {
|
||||
// HitID PATH parameter
|
||||
//
|
||||
// Hit ID
|
||||
HitID string
|
||||
}
|
||||
)
|
||||
|
||||
// NewApigwProfilerAggregation request
|
||||
func NewApigwProfilerAggregation() *ApigwProfilerAggregation {
|
||||
return &ApigwProfilerAggregation{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ApigwProfilerAggregation) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"path": r.Path,
|
||||
"before": r.Before,
|
||||
"sort": r.Sort,
|
||||
"limit": r.Limit,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ApigwProfilerAggregation) GetPath() string {
|
||||
return r.Path
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ApigwProfilerAggregation) GetBefore() string {
|
||||
return r.Before
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ApigwProfilerAggregation) GetSort() string {
|
||||
return r.Sort
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ApigwProfilerAggregation) GetLimit() uint {
|
||||
return r.Limit
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *ApigwProfilerAggregation) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
// GET params
|
||||
tmp := req.URL.Query()
|
||||
|
||||
if val, ok := tmp["path"]; ok && len(val) > 0 {
|
||||
r.Path, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["before"]; ok && len(val) > 0 {
|
||||
r.Before, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["sort"]; ok && len(val) > 0 {
|
||||
r.Sort, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["limit"]; ok && len(val) > 0 {
|
||||
r.Limit, err = payload.ParseUint(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewApigwProfilerRoute request
|
||||
func NewApigwProfilerRoute() *ApigwProfilerRoute {
|
||||
return &ApigwProfilerRoute{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ApigwProfilerRoute) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"routeID": r.RouteID,
|
||||
"path": r.Path,
|
||||
"before": r.Before,
|
||||
"sort": r.Sort,
|
||||
"limit": r.Limit,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ApigwProfilerRoute) GetRouteID() string {
|
||||
return r.RouteID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ApigwProfilerRoute) GetPath() string {
|
||||
return r.Path
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ApigwProfilerRoute) GetBefore() string {
|
||||
return r.Before
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ApigwProfilerRoute) GetSort() string {
|
||||
return r.Sort
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ApigwProfilerRoute) GetLimit() uint {
|
||||
return r.Limit
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *ApigwProfilerRoute) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
// GET params
|
||||
tmp := req.URL.Query()
|
||||
|
||||
if val, ok := tmp["path"]; ok && len(val) > 0 {
|
||||
r.Path, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["before"]; ok && len(val) > 0 {
|
||||
r.Before, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["sort"]; ok && len(val) > 0 {
|
||||
r.Sort, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["limit"]; ok && len(val) > 0 {
|
||||
r.Limit, err = payload.ParseUint(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "routeID")
|
||||
r.RouteID, err = val, nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewApigwProfilerHit request
|
||||
func NewApigwProfilerHit() *ApigwProfilerHit {
|
||||
return &ApigwProfilerHit{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ApigwProfilerHit) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"hitID": r.HitID,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ApigwProfilerHit) GetHitID() string {
|
||||
return r.HitID
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *ApigwProfilerHit) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "hitID")
|
||||
r.HitID, err = val, nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -43,6 +43,7 @@ func MountRoutes() func(r chi.Router) {
|
||||
handlers.NewQueues(Queue{}.New()).MountRoutes(r)
|
||||
handlers.NewApigwRoute(ApigwRoute{}.New()).MountRoutes(r)
|
||||
handlers.NewApigwFilter(ApigwFilter{}.New()).MountRoutes(r)
|
||||
handlers.NewApigwProfiler(ApigwProfiler{}.New()).MountRoutes(r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,15 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza-server/pkg/apigw"
|
||||
"github.com/cortezaproject/corteza-server/pkg/apigw/profiler"
|
||||
a "github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
@@ -253,3 +259,160 @@ func (svc *apigwRoute) Search(ctx context.Context, filter types.ApigwRouteFilter
|
||||
|
||||
return r, f, svc.recordAction(ctx, aProps, ApigwRouteActionSearch, err)
|
||||
}
|
||||
|
||||
// HitsAggregated fetches a list of hits from integration gateway profiler
|
||||
func (svc *apigwRoute) Hits(ctx context.Context, filter types.ApigwProfilerFilter) (r types.ApigwProfilerHitSet, f types.ApigwProfilerFilter, err error) {
|
||||
|
||||
f = filter
|
||||
r = make(types.ApigwProfilerHitSet, 0)
|
||||
|
||||
uDec, err := base64.URLEncoding.DecodeString(filter.Path)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
filter.Path = string(uDec)
|
||||
|
||||
if filter.Path == "" && filter.Hit == "" {
|
||||
err = errors.New("fetching all hits (no route and hit specified) not supported")
|
||||
return
|
||||
}
|
||||
|
||||
var sorting = profiler.Sort{
|
||||
Hit: filter.Hit,
|
||||
Path: filter.Path,
|
||||
Before: filter.Before,
|
||||
}
|
||||
|
||||
var (
|
||||
list = apigw.Service().Profiler().Hits(sorting)
|
||||
)
|
||||
|
||||
var pp = ""
|
||||
|
||||
for k, _ := range list {
|
||||
if filter.Hit != "" {
|
||||
pp = k
|
||||
break
|
||||
}
|
||||
|
||||
if filter.Path != "" && k == filter.Path {
|
||||
pp = k
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if pp == "" {
|
||||
return
|
||||
}
|
||||
|
||||
for _, h := range list[pp] {
|
||||
hh := &types.ApigwProfilerHit{
|
||||
ID: h.ID,
|
||||
|
||||
Route: h.Route,
|
||||
Status: h.Status,
|
||||
Request: *h.R,
|
||||
|
||||
Ts: h.Ts,
|
||||
Tf: h.Tf,
|
||||
D: h.D,
|
||||
Dr: float64(h.D.Microseconds()) / 1000,
|
||||
}
|
||||
|
||||
// fetch body only on hit details
|
||||
if filter.Hit != "" {
|
||||
hh.Body, _ = ioutil.ReadAll(hh.Request.Body)
|
||||
}
|
||||
|
||||
r = append(r, hh)
|
||||
}
|
||||
|
||||
// sort
|
||||
profiler.SortHits(&r, &f)
|
||||
|
||||
// filter sorted
|
||||
profiler.FilterHits(&r, &f)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// HitsAggregated fetches a list of hits from integration gateway profiler
|
||||
// and aggregates them with assigned filters
|
||||
func (svc *apigwRoute) HitsAggregated(ctx context.Context, filter types.ApigwProfilerFilter) (r types.ApigwProfilerAggregationSet, f types.ApigwProfilerFilter, err error) {
|
||||
f = filter
|
||||
r = make(types.ApigwProfilerAggregationSet, 0)
|
||||
|
||||
uDec, err := base64.URLEncoding.DecodeString(filter.Path)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
filter.Path = string(uDec)
|
||||
|
||||
var (
|
||||
list = apigw.Service().Profiler().Hits(profiler.Sort{
|
||||
Path: filter.Path,
|
||||
Before: filter.Before,
|
||||
})
|
||||
|
||||
tsum, tmin, tmax time.Duration
|
||||
ssum, smin, smax int64
|
||||
i uint64 = 1
|
||||
)
|
||||
|
||||
for p, v := range list {
|
||||
tmin, tmax, tsum = time.Hour, 0, 0
|
||||
smin, smax, ssum = math.MaxInt64, 0, 0
|
||||
|
||||
i = 0
|
||||
|
||||
for _, vv := range v {
|
||||
var (
|
||||
d = *vv.D
|
||||
s = vv.R.ContentLength
|
||||
)
|
||||
|
||||
if d < tmin {
|
||||
tmin = d
|
||||
}
|
||||
|
||||
if d > tmax {
|
||||
tmax = d
|
||||
}
|
||||
|
||||
if s < smin {
|
||||
smin = s
|
||||
}
|
||||
|
||||
if s > smax {
|
||||
smax = s
|
||||
}
|
||||
|
||||
tsum += d
|
||||
ssum += s
|
||||
i++
|
||||
}
|
||||
|
||||
r = append(r, &types.ApigwProfilerAggregation{
|
||||
Path: p,
|
||||
Count: i,
|
||||
Tmin: float64(tmin.Microseconds()) / 1000,
|
||||
Tmax: float64(tmax.Microseconds()) / 1000,
|
||||
Tavg: float64(tsum.Microseconds()) / float64(i) / 1000,
|
||||
Smin: smin,
|
||||
Smax: smax,
|
||||
Savg: float64(ssum) / float64(i),
|
||||
})
|
||||
}
|
||||
|
||||
// sort
|
||||
profiler.SortAggregation(&r, &f)
|
||||
|
||||
// filter
|
||||
profiler.FilterAggregation(&r, &f)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
h "github.com/cortezaproject/corteza-server/pkg/http"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -26,6 +27,31 @@ type (
|
||||
DeletedBy uint64 `json:"deletedBy,string,omitempty" `
|
||||
}
|
||||
|
||||
ApigwProfilerHit struct {
|
||||
ID string `json:"ID"`
|
||||
|
||||
Body []byte `json:"body"`
|
||||
Request h.Request `json:"request"`
|
||||
Route uint64 `json:"route,string"`
|
||||
Status int `json:"http_status_code,string"`
|
||||
|
||||
Ts *time.Time `json:"time_start"`
|
||||
Tf *time.Time `json:"time_finish"`
|
||||
D *time.Duration `json:"-"`
|
||||
Dr float64 `json:"time_duration"`
|
||||
}
|
||||
|
||||
ApigwProfilerAggregation struct {
|
||||
Path string `json:"path"`
|
||||
Count uint64 `json:"count"`
|
||||
Smin int64 `json:"size_min"`
|
||||
Smax int64 `json:"size_max"`
|
||||
Savg float64 `json:"size_avg"`
|
||||
Tmin float64 `json:"time_min"`
|
||||
Tmax float64 `json:"time_max"`
|
||||
Tavg float64 `json:"time_avg"`
|
||||
}
|
||||
|
||||
ApigwRouteMeta struct {
|
||||
Debug bool `json:"debug"`
|
||||
Async bool `json:"async"`
|
||||
@@ -47,6 +73,16 @@ type (
|
||||
filter.Sorting
|
||||
filter.Paging
|
||||
}
|
||||
|
||||
ApigwProfilerFilter struct {
|
||||
Hit string `json:"hit,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Before string `json:"before,omitempty"`
|
||||
Next string `json:"next,omitempty"`
|
||||
|
||||
filter.Sorting
|
||||
filter.Paging
|
||||
}
|
||||
)
|
||||
|
||||
func (cc *ApigwRouteMeta) Scan(value interface{}) error {
|
||||
@@ -67,3 +103,162 @@ func (cc *ApigwRouteMeta) Scan(value interface{}) error {
|
||||
func (cc ApigwRouteMeta) Value() (driver.Value, error) {
|
||||
return json.Marshal(cc)
|
||||
}
|
||||
|
||||
// sorting methods
|
||||
type (
|
||||
ByPath ApigwProfilerAggregationSet
|
||||
ByCount ApigwProfilerAggregationSet
|
||||
BySizeMin ApigwProfilerAggregationSet
|
||||
BySizeMax ApigwProfilerAggregationSet
|
||||
BySizeAvg ApigwProfilerAggregationSet
|
||||
ByTimeMin ApigwProfilerAggregationSet
|
||||
ByTimeMax ApigwProfilerAggregationSet
|
||||
ByTimeAvg ApigwProfilerAggregationSet
|
||||
|
||||
BySTime ApigwProfilerHitSet
|
||||
ByFTime ApigwProfilerHitSet
|
||||
ByDuration ApigwProfilerHitSet
|
||||
)
|
||||
|
||||
func (h ByPath) Len() int {
|
||||
return len(h)
|
||||
}
|
||||
|
||||
func (h ByPath) Less(i, j int) bool {
|
||||
return h[i].Path < h[j].Path
|
||||
}
|
||||
|
||||
func (h ByPath) Swap(i, j int) {
|
||||
h[i], h[j] = h[j], h[i]
|
||||
return
|
||||
}
|
||||
|
||||
func (h ByCount) Len() int {
|
||||
return len(h)
|
||||
}
|
||||
|
||||
func (h ByCount) Less(i, j int) bool {
|
||||
return h[i].Count < h[j].Count
|
||||
}
|
||||
|
||||
func (h ByCount) Swap(i, j int) {
|
||||
h[i], h[j] = h[j], h[i]
|
||||
return
|
||||
}
|
||||
|
||||
func (h BySizeMin) Len() int {
|
||||
return len(h)
|
||||
}
|
||||
|
||||
func (h BySizeMin) Less(i, j int) bool {
|
||||
return h[i].Smin < h[j].Smin
|
||||
}
|
||||
|
||||
func (h BySizeMin) Swap(i, j int) {
|
||||
h[i], h[j] = h[j], h[i]
|
||||
return
|
||||
}
|
||||
|
||||
func (h BySizeMax) Len() int {
|
||||
return len(h)
|
||||
}
|
||||
|
||||
func (h BySizeMax) Less(i, j int) bool {
|
||||
return h[i].Smax < h[j].Smax
|
||||
}
|
||||
|
||||
func (h BySizeMax) Swap(i, j int) {
|
||||
h[i], h[j] = h[j], h[i]
|
||||
return
|
||||
}
|
||||
|
||||
func (h BySizeAvg) Len() int {
|
||||
return len(h)
|
||||
}
|
||||
|
||||
func (h BySizeAvg) Less(i, j int) bool {
|
||||
return h[i].Savg < h[j].Savg
|
||||
}
|
||||
|
||||
func (h BySizeAvg) Swap(i, j int) {
|
||||
h[i], h[j] = h[j], h[i]
|
||||
return
|
||||
}
|
||||
|
||||
func (h ByTimeAvg) Len() int {
|
||||
return len(h)
|
||||
}
|
||||
|
||||
func (h ByTimeAvg) Less(i, j int) bool {
|
||||
return h[i].Tavg < h[j].Tavg
|
||||
}
|
||||
|
||||
func (h ByTimeAvg) Swap(i, j int) {
|
||||
h[i], h[j] = h[j], h[i]
|
||||
return
|
||||
}
|
||||
|
||||
func (h ByTimeMax) Len() int {
|
||||
return len(h)
|
||||
}
|
||||
|
||||
func (h ByTimeMax) Less(i, j int) bool {
|
||||
return h[i].Tmax < h[j].Tmax
|
||||
}
|
||||
|
||||
func (h ByTimeMax) Swap(i, j int) {
|
||||
h[i], h[j] = h[j], h[i]
|
||||
return
|
||||
}
|
||||
|
||||
func (h ByTimeMin) Len() int {
|
||||
return len(h)
|
||||
}
|
||||
|
||||
func (h ByTimeMin) Less(i, j int) bool {
|
||||
return h[i].Tmin < h[j].Tmin
|
||||
}
|
||||
|
||||
func (h ByTimeMin) Swap(i, j int) {
|
||||
h[i], h[j] = h[j], h[i]
|
||||
return
|
||||
}
|
||||
|
||||
func (h BySTime) Len() int {
|
||||
return len(h)
|
||||
}
|
||||
|
||||
func (h BySTime) Less(i, j int) bool {
|
||||
return h[j].Ts.After(*h[i].Ts)
|
||||
}
|
||||
|
||||
func (h BySTime) Swap(i, j int) {
|
||||
h[i], h[j] = h[j], h[i]
|
||||
return
|
||||
}
|
||||
|
||||
func (h ByFTime) Len() int {
|
||||
return len(h)
|
||||
}
|
||||
|
||||
func (h ByFTime) Less(i, j int) bool {
|
||||
return h[j].Tf.After(*h[i].Tf)
|
||||
}
|
||||
|
||||
func (h ByFTime) Swap(i, j int) {
|
||||
h[i], h[j] = h[j], h[i]
|
||||
return
|
||||
}
|
||||
|
||||
func (h ByDuration) Len() int {
|
||||
return len(h)
|
||||
}
|
||||
|
||||
func (h ByDuration) Less(i, j int) bool {
|
||||
return h[i].D.Microseconds() > h[j].D.Microseconds()
|
||||
}
|
||||
|
||||
func (h ByDuration) Swap(i, j int) {
|
||||
h[i], h[j] = h[j], h[i]
|
||||
return
|
||||
}
|
||||
|
||||
@@ -207,6 +207,13 @@ type (
|
||||
Enabled bool `kv:"-" json:"enabled"`
|
||||
} `kv:"federation" json:"federation"`
|
||||
|
||||
// Federation settings
|
||||
Apigw struct {
|
||||
// This only holds the value of APIGW_PROFILER_ENABLED for now
|
||||
//
|
||||
ProfilerEnabled bool `kv:"-" json:"profilerEnabled"`
|
||||
} `kv:"apigw" json:"apigw"`
|
||||
|
||||
// UserInterface settings
|
||||
UI struct {
|
||||
MainLogo string `kv:"main-logo" json:"mainLogo"`
|
||||
|
||||
70
system/types/type_set.gen.go
generated
70
system/types/type_set.gen.go
generated
@@ -15,6 +15,16 @@ type (
|
||||
// This type is auto-generated.
|
||||
ApigwFilterSet []*ApigwFilter
|
||||
|
||||
// ApigwProfilerAggregationSet slice of ApigwProfilerAggregation
|
||||
//
|
||||
// This type is auto-generated.
|
||||
ApigwProfilerAggregationSet []*ApigwProfilerAggregation
|
||||
|
||||
// ApigwProfilerHitSet slice of ApigwProfilerHit
|
||||
//
|
||||
// This type is auto-generated.
|
||||
ApigwProfilerHitSet []*ApigwProfilerHit
|
||||
|
||||
// ApigwRouteSet slice of ApigwRoute
|
||||
//
|
||||
// This type is auto-generated.
|
||||
@@ -162,6 +172,66 @@ func (set ApigwFilterSet) IDs() (IDs []uint64) {
|
||||
return
|
||||
}
|
||||
|
||||
// Walk iterates through every slice item and calls w(ApigwProfilerAggregation) err
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set ApigwProfilerAggregationSet) Walk(w func(*ApigwProfilerAggregation) error) (err error) {
|
||||
for i := range set {
|
||||
if err = w(set[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Filter iterates through every slice item, calls f(ApigwProfilerAggregation) (bool, err) and return filtered slice
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set ApigwProfilerAggregationSet) Filter(f func(*ApigwProfilerAggregation) (bool, error)) (out ApigwProfilerAggregationSet, err error) {
|
||||
var ok bool
|
||||
out = ApigwProfilerAggregationSet{}
|
||||
for i := range set {
|
||||
if ok, err = f(set[i]); err != nil {
|
||||
return
|
||||
} else if ok {
|
||||
out = append(out, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Walk iterates through every slice item and calls w(ApigwProfilerHit) err
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set ApigwProfilerHitSet) Walk(w func(*ApigwProfilerHit) error) (err error) {
|
||||
for i := range set {
|
||||
if err = w(set[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Filter iterates through every slice item, calls f(ApigwProfilerHit) (bool, err) and return filtered slice
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set ApigwProfilerHitSet) Filter(f func(*ApigwProfilerHit) (bool, error)) (out ApigwProfilerHitSet, err error) {
|
||||
var ok bool
|
||||
out = ApigwProfilerHitSet{}
|
||||
for i := range set {
|
||||
if ok, err = f(set[i]); err != nil {
|
||||
return
|
||||
} else if ok {
|
||||
out = append(out, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Walk iterates through every slice item and calls w(ApigwRoute) err
|
||||
//
|
||||
// This function is auto-generated.
|
||||
|
||||
112
system/types/type_set.gen_test.go
generated
112
system/types/type_set.gen_test.go
generated
@@ -104,6 +104,118 @@ func TestApigwFilterSetIDs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestApigwProfilerAggregationSetWalk(t *testing.T) {
|
||||
var (
|
||||
value = make(ApigwProfilerAggregationSet, 3)
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
// check walk with no errors
|
||||
{
|
||||
err := value.Walk(func(*ApigwProfilerAggregation) error {
|
||||
return nil
|
||||
})
|
||||
req.NoError(err)
|
||||
}
|
||||
|
||||
// check walk with error
|
||||
req.Error(value.Walk(func(*ApigwProfilerAggregation) error { return fmt.Errorf("walk error") }))
|
||||
}
|
||||
|
||||
func TestApigwProfilerAggregationSetFilter(t *testing.T) {
|
||||
var (
|
||||
value = make(ApigwProfilerAggregationSet, 3)
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
// filter nothing
|
||||
{
|
||||
set, err := value.Filter(func(*ApigwProfilerAggregation) (bool, error) {
|
||||
return true, nil
|
||||
})
|
||||
req.NoError(err)
|
||||
req.Equal(len(set), len(value))
|
||||
}
|
||||
|
||||
// filter one item
|
||||
{
|
||||
found := false
|
||||
set, err := value.Filter(func(*ApigwProfilerAggregation) (bool, error) {
|
||||
if !found {
|
||||
found = true
|
||||
return found, nil
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
req.NoError(err)
|
||||
req.Len(set, 1)
|
||||
}
|
||||
|
||||
// filter error
|
||||
{
|
||||
_, err := value.Filter(func(*ApigwProfilerAggregation) (bool, error) {
|
||||
return false, fmt.Errorf("filter error")
|
||||
})
|
||||
req.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApigwProfilerHitSetWalk(t *testing.T) {
|
||||
var (
|
||||
value = make(ApigwProfilerHitSet, 3)
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
// check walk with no errors
|
||||
{
|
||||
err := value.Walk(func(*ApigwProfilerHit) error {
|
||||
return nil
|
||||
})
|
||||
req.NoError(err)
|
||||
}
|
||||
|
||||
// check walk with error
|
||||
req.Error(value.Walk(func(*ApigwProfilerHit) error { return fmt.Errorf("walk error") }))
|
||||
}
|
||||
|
||||
func TestApigwProfilerHitSetFilter(t *testing.T) {
|
||||
var (
|
||||
value = make(ApigwProfilerHitSet, 3)
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
// filter nothing
|
||||
{
|
||||
set, err := value.Filter(func(*ApigwProfilerHit) (bool, error) {
|
||||
return true, nil
|
||||
})
|
||||
req.NoError(err)
|
||||
req.Equal(len(set), len(value))
|
||||
}
|
||||
|
||||
// filter one item
|
||||
{
|
||||
found := false
|
||||
set, err := value.Filter(func(*ApigwProfilerHit) (bool, error) {
|
||||
if !found {
|
||||
found = true
|
||||
return found, nil
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
req.NoError(err)
|
||||
req.Len(set, 1)
|
||||
}
|
||||
|
||||
// filter error
|
||||
{
|
||||
_, err := value.Filter(func(*ApigwProfilerHit) (bool, error) {
|
||||
return false, fmt.Errorf("filter error")
|
||||
})
|
||||
req.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApigwRouteSetWalk(t *testing.T) {
|
||||
var (
|
||||
value = make(ApigwRouteSet, 3)
|
||||
|
||||
@@ -23,6 +23,10 @@ types:
|
||||
labelResourceType: template
|
||||
ApigwRoute: {}
|
||||
ApigwFilter: {}
|
||||
ApigwProfilerHit:
|
||||
noIdField: true
|
||||
ApigwProfilerAggregation:
|
||||
noIdField: true
|
||||
Report:
|
||||
labelResourceType: report
|
||||
ResourceTranslation: {}
|
||||
|
||||
Reference in New Issue
Block a user