1
0
mirror of https://github.com/astaxie/beego.git synced 2024-11-21 22:20:55 +00:00

Merge pull request #4148 from flycash/fix/end2end

Fix prometheus and opentracing bug found in end2end tests
This commit is contained in:
Ming Deng 2020-08-11 16:33:54 +08:00 committed by GitHub
commit d326d74c34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 256 additions and 196 deletions

View File

@ -16,15 +16,15 @@ package beego
var (
// Deprecated: using pkg/, we will delete this in v2.1.0
BuildVersion string
BuildVersion string
// Deprecated: using pkg/, we will delete this in v2.1.0
BuildGitRevision string
// Deprecated: using pkg/, we will delete this in v2.1.0
BuildStatus string
BuildStatus string
// Deprecated: using pkg/, we will delete this in v2.1.0
BuildTag string
BuildTag string
// Deprecated: using pkg/, we will delete this in v2.1.0
BuildTime string
BuildTime string
// Deprecated: using pkg/, we will delete this in v2.1.0
GoVersion string

View File

@ -15,13 +15,13 @@
package beego
import (
"crypto/tls"
"fmt"
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
"crypto/tls"
"github.com/astaxie/beego/config"
"github.com/astaxie/beego/context"
@ -163,7 +163,7 @@ func init() {
}
appConfigPath = filepath.Join(WorkPath, "conf", filename)
if configPath := os.Getenv("BEEGO_CONFIG_PATH"); configPath != "" {
appConfigPath = configPath
appConfigPath = configPath
}
if !utils.FileExists(appConfigPath) {
appConfigPath = filepath.Join(AppPath, "conf", filename)

View File

@ -51,9 +51,9 @@ import (
// Deprecated: using pkg/config, we will delete this in v2.1.0
type Configer interface {
// Deprecated: using pkg/config, we will delete this in v2.1.0
Set(key, val string) error //support section::key type in given key when using ini type.
Set(key, val string) error //support section::key type in given key when using ini type.
// Deprecated: using pkg/config, we will delete this in v2.1.0
String(key string) string //support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same.
String(key string) string //support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same.
// Deprecated: using pkg/config, we will delete this in v2.1.0
Strings(key string) []string //get string slice
// Deprecated: using pkg/config, we will delete this in v2.1.0
@ -65,7 +65,7 @@ type Configer interface {
// Deprecated: using pkg/config, we will delete this in v2.1.0
Float(key string) (float64, error)
// Deprecated: using pkg/config, we will delete this in v2.1.0
DefaultString(key string, defaultVal string) string // support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same.
DefaultString(key string, defaultVal string) string // support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same.
// Deprecated: using pkg/config, we will delete this in v2.1.0
DefaultStrings(key string, defaultVal []string) []string //get string slice
// Deprecated: using pkg/config, we will delete this in v2.1.0

View File

@ -27,15 +27,18 @@ type fakeConfigContainer struct {
func (c *fakeConfigContainer) getData(key string) string {
return c.data[strings.ToLower(key)]
}
// Deprecated: using pkg/config, we will delete this in v2.1.0
func (c *fakeConfigContainer) Set(key, val string) error {
c.data[strings.ToLower(key)] = val
return nil
}
// Deprecated: using pkg/config, we will delete this in v2.1.0
func (c *fakeConfigContainer) String(key string) string {
return c.getData(key)
}
// Deprecated: using pkg/config, we will delete this in v2.1.0
func (c *fakeConfigContainer) DefaultString(key string, defaultval string) string {
v := c.String(key)
@ -44,6 +47,7 @@ func (c *fakeConfigContainer) DefaultString(key string, defaultval string) strin
}
return v
}
// Deprecated: using pkg/config, we will delete this in v2.1.0
func (c *fakeConfigContainer) Strings(key string) []string {
v := c.String(key)
@ -52,6 +56,7 @@ func (c *fakeConfigContainer) Strings(key string) []string {
}
return strings.Split(v, ";")
}
// Deprecated: using pkg/config, we will delete this in v2.1.0
func (c *fakeConfigContainer) DefaultStrings(key string, defaultval []string) []string {
v := c.Strings(key)
@ -60,10 +65,12 @@ func (c *fakeConfigContainer) DefaultStrings(key string, defaultval []string) []
}
return v
}
// Deprecated: using pkg/config, we will delete this in v2.1.0
func (c *fakeConfigContainer) Int(key string) (int, error) {
return strconv.Atoi(c.getData(key))
}
// Deprecated: using pkg/config, we will delete this in v2.1.0
func (c *fakeConfigContainer) DefaultInt(key string, defaultval int) int {
v, err := c.Int(key)
@ -72,10 +79,12 @@ func (c *fakeConfigContainer) DefaultInt(key string, defaultval int) int {
}
return v
}
// Deprecated: using pkg/config, we will delete this in v2.1.0
func (c *fakeConfigContainer) Int64(key string) (int64, error) {
return strconv.ParseInt(c.getData(key), 10, 64)
}
// Deprecated: using pkg/config, we will delete this in v2.1.0
func (c *fakeConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
v, err := c.Int64(key)
@ -84,10 +93,12 @@ func (c *fakeConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
}
return v
}
// Deprecated: using pkg/config, we will delete this in v2.1.0
func (c *fakeConfigContainer) Bool(key string) (bool, error) {
return ParseBool(c.getData(key))
}
// Deprecated: using pkg/config, we will delete this in v2.1.0
func (c *fakeConfigContainer) DefaultBool(key string, defaultval bool) bool {
v, err := c.Bool(key)
@ -96,10 +107,12 @@ func (c *fakeConfigContainer) DefaultBool(key string, defaultval bool) bool {
}
return v
}
// Deprecated: using pkg/config, we will delete this in v2.1.0
func (c *fakeConfigContainer) Float(key string) (float64, error) {
return strconv.ParseFloat(c.getData(key), 64)
}
// Deprecated: using pkg/config, we will delete this in v2.1.0
func (c *fakeConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
v, err := c.Float(key)
@ -108,6 +121,7 @@ func (c *fakeConfigContainer) DefaultFloat(key string, defaultval float64) float
}
return v
}
// Deprecated: using pkg/config, we will delete this in v2.1.0
func (c *fakeConfigContainer) DIY(key string) (interface{}, error) {
if v, ok := c.data[strings.ToLower(key)]; ok {
@ -115,10 +129,12 @@ func (c *fakeConfigContainer) DIY(key string) (interface{}, error) {
}
return nil, errors.New("key not find")
}
// Deprecated: using pkg/config, we will delete this in v2.1.0
func (c *fakeConfigContainer) GetSection(section string) (map[string]string, error) {
return nil, errors.New("not implement in the fakeConfigContainer")
}
// Deprecated: using pkg/config, we will delete this in v2.1.0
func (c *fakeConfigContainer) SaveConfigFile(filename string) error {
return errors.New("not implement in the fakeConfigContainer")

View File

@ -58,7 +58,6 @@ func (output *BeegoOutput) Clear() {
output.Status = 0
}
// Header sets response header item string via given key.
func (output *BeegoOutput) Header(key, val string) {
output.Context.ResponseWriter.Header().Set(key, val)

4
go.sum
View File

@ -185,6 +185,7 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -219,6 +220,9 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20200117065230-39095c1d176c h1:FodBYPZKH5tAN2O60HlglMwXGAeV/4k+NKbli79M/2c=
golang.org/x/tools v0.0.0-20200117065230-39095c1d176c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=

View File

@ -6,10 +6,11 @@ import (
"fmt"
"net/http"
"net/http/httptest"
"reflect"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/astaxie/beego/pkg/toolbox"
)
@ -230,10 +231,19 @@ func TestHealthCheckHandlerReturnsJSON(t *testing.T) {
t.Errorf("invalid response map length: got %d want %d",
len(decodedResponseBody), len(expectedResponseBody))
}
assert.Equal(t, len(expectedResponseBody), len(decodedResponseBody))
assert.Equal(t, 2, len(decodedResponseBody))
if !reflect.DeepEqual(decodedResponseBody, expectedResponseBody) {
t.Errorf("handler returned unexpected body: got %v want %v",
decodedResponseBody, expectedResponseBody)
var database, cache map[string]interface{}
if decodedResponseBody[0]["message"] == "database" {
database = decodedResponseBody[0]
cache = decodedResponseBody[1]
} else {
database = decodedResponseBody[1]
cache = decodedResponseBody[0]
}
assert.Equal(t, expectedResponseBody[0], database)
assert.Equal(t, expectedResponseBody[1], cache)
}

View File

@ -498,6 +498,7 @@ func InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) *A
// InsertFilterChain adds a FilterFunc built by filterChain.
// This filter will be executed before all filters.
// the filter's behavior is like stack
func InsertFilterChain(pattern string, filterChain FilterChain, params ...bool) *App {
BeeApp.Handlers.InsertFilterChain(pattern, filterChain, params...)
return BeeApp

View File

@ -33,6 +33,7 @@ type FilterFunc func(ctx *context.Context)
// when a request with a matching URL arrives.
type FilterRouter struct {
filterFunc FilterFunc
next *FilterRouter
tree *Tree
pattern string
returnOnOutput bool
@ -81,6 +82,8 @@ func (f *FilterRouter) filter(ctx *context.Context, urlPath string, preFilterPar
ctx.Input.SetParam(k, v)
}
}
} else if f.next != nil {
return f.next.filter(ctx, urlPath, preFilterParams)
}
if f.returnOnOutput && ctx.ResponseWriter.Started {
return true, true

View File

@ -1,4 +1,4 @@
// Copyright 2020
// Copyright 2020
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -39,7 +39,6 @@ func TestControllerRegister_InsertFilterChain(t *testing.T) {
ctx.Output.Body([]byte("hello"))
})
r, _ := http.NewRequest("GET", "/chain/user", nil)
w := httptest.NewRecorder()

View File

@ -111,4 +111,4 @@ func registerCommentRouter() error {
}
return nil
}
}

View File

@ -1,4 +1,4 @@
// Copyright 2020 beego
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,4 +1,4 @@
// Copyright 2020 beego
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -17,14 +17,11 @@ package opentracing
import (
"context"
"net/http"
"strconv"
"github.com/astaxie/beego/pkg/httplib"
logKit "github.com/go-kit/kit/log"
opentracingKit "github.com/go-kit/kit/tracing/opentracing"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/log"
"github.com/astaxie/beego/pkg/httplib"
)
type FilterChainBuilder struct {
@ -38,14 +35,8 @@ func (builder *FilterChainBuilder) FilterChain(next httplib.Filter) httplib.Filt
return func(ctx context.Context, req *httplib.BeegoHTTPRequest) (*http.Response, error) {
method := req.GetRequest().Method
host := req.GetRequest().URL.Host
path := req.GetRequest().URL.Path
proto := req.GetRequest().Proto
scheme := req.GetRequest().URL.Scheme
operationName := host + path + "#" + method
operationName := method + "#" + req.GetRequest().URL.String()
span, spanCtx := opentracing.StartSpanFromContext(ctx, operationName)
defer span.Finish()
@ -54,21 +45,24 @@ func (builder *FilterChainBuilder) FilterChain(next httplib.Filter) httplib.Filt
resp, err := next(spanCtx, req)
if resp != nil {
span.SetTag("status", strconv.Itoa(resp.StatusCode))
span.SetTag("http.status_code", resp.StatusCode)
}
span.SetTag("method", method)
span.SetTag("host", host)
span.SetTag("path", path)
span.SetTag("proto", proto)
span.SetTag("scheme", scheme)
span.LogFields(log.String("url", req.GetRequest().URL.String()))
span.SetTag("http.method", method)
span.SetTag("peer.hostname", req.GetRequest().URL.Host)
span.SetTag("http.url", req.GetRequest().URL.String())
span.SetTag("http.scheme", req.GetRequest().URL.Scheme)
span.SetTag("span.kind", "client")
span.SetTag("component", "beego")
if err != nil {
span.LogFields(log.String("error", err.Error()))
span.SetTag("error", true)
span.SetTag("message", err.Error())
} else if resp != nil && !(resp.StatusCode < 300 && resp.StatusCode >= 200) {
span.SetTag("error", true)
}
span.SetTag("peer.address", req.GetRequest().RemoteAddr)
span.SetTag("http.proto", req.GetRequest().Proto)
if builder.CustomSpanFunc != nil {
builder.CustomSpanFunc(span, ctx, req, resp, err)
}

View File

@ -1,4 +1,4 @@
// Copyright 2020 beego
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,4 +1,4 @@
// Copyright 2020 beego
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -63,11 +63,13 @@ func (builder *FilterChainBuilder) report(startTime time.Time, endTime time.Time
host := req.GetRequest().URL.Host
path := req.GetRequest().URL.Path
status := resp.StatusCode
status := -1
if resp != nil {
status = resp.StatusCode
}
dur := int(endTime.Sub(startTime) / time.Millisecond)
builder.summaryVec.WithLabelValues(proto, scheme, method, host, path,
strconv.Itoa(status), strconv.Itoa(dur), strconv.FormatBool(err == nil))
}

View File

@ -1,4 +1,4 @@
// Copyright 2020 beego
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -358,7 +358,7 @@ func addAliasWthDB(aliasName, driverName string, db *sql.DB, params ...common.KV
return al, nil
}
func newAliasWithDb(aliasName, driverName string, db *sql.DB, params ...common.KV)(*alias, error){
func newAliasWithDb(aliasName, driverName string, db *sql.DB, params ...common.KV) (*alias, error) {
kvs := common.NewKVs(params...)
var stmtCache *lru.Cache
@ -430,7 +430,6 @@ func RegisterDataBase(aliasName, driverName, dataSource string, params ...common
al *alias
)
db, err = sql.Open(driverName, dataSource)
if err != nil {
err = fmt.Errorf("register db `%s`, %s", aliasName, err.Error())

View File

@ -76,7 +76,6 @@ func TestRegisterDataBase_MaxStmtCacheSize841(t *testing.T) {
assert.Equal(t, al.DB.stmtDecoratorsLimit, 841)
}
func TestDBCache(t *testing.T) {
dataBaseCache.add("test1", &alias{})
dataBaseCache.add("default", &alias{})

View File

@ -1,4 +1,4 @@
// Copyright 2020 beego
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -27,6 +27,7 @@ import (
var _ Ormer = new(DoNothingOrm)
type DoNothingOrm struct {
}
func (d *DoNothingOrm) Read(md interface{}, cols ...string) error {
@ -149,19 +150,19 @@ func (d *DoNothingOrm) BeginWithCtxAndOpts(ctx context.Context, opts *sql.TxOpti
return nil, nil
}
func (d *DoNothingOrm) DoTx(task func(txOrm TxOrmer) error) error {
func (d *DoNothingOrm) DoTx(task func(ctx context.Context, txOrm TxOrmer) error) error {
return nil
}
func (d *DoNothingOrm) DoTxWithCtx(ctx context.Context, task func(txOrm TxOrmer) error) error {
func (d *DoNothingOrm) DoTxWithCtx(ctx context.Context, task func(ctx context.Context, txOrm TxOrmer) error) error {
return nil
}
func (d *DoNothingOrm) DoTxWithOpts(opts *sql.TxOptions, task func(txOrm TxOrmer) error) error {
func (d *DoNothingOrm) DoTxWithOpts(opts *sql.TxOptions, task func(ctx context.Context, txOrm TxOrmer) error) error {
return nil
}
func (d *DoNothingOrm) DoTxWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions, task func(txOrm TxOrmer) error) error {
func (d *DoNothingOrm) DoTxWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions, task func(ctx context.Context, txOrm TxOrmer) error) error {
return nil
}

View File

@ -1,4 +1,4 @@
// Copyright 2020 beego
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,4 +1,4 @@
// Copyright 2020 beego
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -22,7 +22,7 @@ import (
// don't forget to call next(...) inside your Filter
type FilterChain func(next Filter) Filter
// Filter's behavior is a little big strang.
// Filter's behavior is a little big strange.
// it's only be called when users call methods of Ormer
type Filter func(ctx context.Context, inv *Invocation)
@ -31,6 +31,6 @@ var globalFilterChains = make([]FilterChain, 0, 4)
// AddGlobalFilterChain adds a new FilterChain
// All orm instances built after this invocation will use this filterChain,
// but instances built before this invocation will not be affected
func AddGlobalFilterChain(filterChain FilterChain) {
globalFilterChains = append(globalFilterChains, filterChain)
}
func AddGlobalFilterChain(filterChain ...FilterChain) {
globalFilterChains = append(globalFilterChains, filterChain...)
}

View File

@ -1,4 +1,4 @@
// Copyright 2020 beego
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -16,6 +16,7 @@ package opentracing
import (
"context"
"strings"
"github.com/opentracing/opentracing-go"
@ -27,6 +28,8 @@ import (
// for example:
// if we want to trace QuerySetter
// actually we trace invoking "QueryTable" and "QueryTableWithCtx"
// the method Begin*, Commit and Rollback are ignored.
// When use using those methods, it means that they want to manager their transaction manually, so we won't handle them.
type FilterChainBuilder struct {
// CustomSpanFunc users are able to custom their span
CustomSpanFunc func(span opentracing.Span, ctx context.Context, inv *orm.Invocation)
@ -35,25 +38,34 @@ type FilterChainBuilder struct {
func (builder *FilterChainBuilder) FilterChain(next orm.Filter) orm.Filter {
return func(ctx context.Context, inv *orm.Invocation) {
operationName := builder.operationName(ctx, inv)
span, spanCtx := opentracing.StartSpanFromContext(ctx, operationName)
defer span.Finish()
next(spanCtx, inv)
span.SetTag("Method", inv.Method)
span.SetTag("Table", inv.GetTableName())
span.SetTag("InsideTx", inv.InsideTx)
span.SetTag("TxName", spanCtx.Value(orm.TxNameKey))
if builder.CustomSpanFunc != nil {
builder.CustomSpanFunc(span, spanCtx, inv)
if strings.HasPrefix(inv.Method, "Begin") || inv.Method == "Commit" || inv.Method == "Rollback" {
next(ctx, inv)
return
}
span, spanCtx := opentracing.StartSpanFromContext(ctx, operationName)
defer span.Finish()
next(spanCtx, inv)
builder.buildSpan(span, spanCtx, inv)
}
}
func (builder *FilterChainBuilder) buildSpan(span opentracing.Span, ctx context.Context, inv *orm.Invocation) {
span.SetTag("orm.method", inv.Method)
span.SetTag("orm.table", inv.GetTableName())
span.SetTag("orm.insideTx", inv.InsideTx)
span.SetTag("orm.txName", ctx.Value(orm.TxNameKey))
span.SetTag("span.kind", "client")
span.SetTag("component", "beego")
if builder.CustomSpanFunc != nil {
builder.CustomSpanFunc(span, ctx, inv)
}
}
func (builder *FilterChainBuilder) operationName(ctx context.Context, inv *orm.Invocation) string {
if n, ok := ctx.Value(orm.TxNameKey).(string); ok {
return inv.Method + "#" + n
return inv.Method + "#tx(" + n + ")"
}
return inv.Method + "#" + inv.GetTableName()
}

View File

@ -1,4 +1,4 @@
// Copyright 2020 beego
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -40,4 +40,4 @@ func TestFilterChainBuilder_FilterChain(t *testing.T) {
TxStartTime: time.Now(),
}
builder.FilterChain(next)(context.Background(), inv)
}
}

View File

@ -1,4 +1,4 @@
// Copyright 2020 beego
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,4 +1,4 @@
// Copyright 2020 beego
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,4 +1,4 @@
// Copyright 2020 beego
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -22,7 +22,12 @@ import (
"time"
)
const TxNameKey = "TxName"
const (
TxNameKey = "TxName"
)
var _ Ormer = new(filterOrmDecorator)
var _ TxOrmer = new(filterOrmDecorator)
type filterOrmDecorator struct {
ormer
@ -41,7 +46,7 @@ func NewFilterOrmDecorator(delegate Ormer, filterChains ...FilterChain) Ormer {
ormer: delegate,
TxBeginner: delegate,
root: func(ctx context.Context, inv *Invocation) {
inv.execute()
inv.execute(ctx)
},
}
@ -59,7 +64,7 @@ func NewFilterTxOrmDecorator(delegate TxOrmer, root Filter, txName string) TxOrm
root: root,
insideTx: true,
txStartTime: time.Now(),
txName: txName,
txName: txName,
}
return res
}
@ -77,8 +82,8 @@ func (f *filterOrmDecorator) ReadWithCtx(ctx context.Context, md interface{}, co
mi: mi,
InsideTx: f.insideTx,
TxStartTime: f.txStartTime,
f: func() {
err = f.ormer.ReadWithCtx(ctx, md, cols...)
f: func(c context.Context) {
err = f.ormer.ReadWithCtx(c, md, cols...)
},
}
f.root(ctx, inv)
@ -99,8 +104,8 @@ func (f *filterOrmDecorator) ReadForUpdateWithCtx(ctx context.Context, md interf
mi: mi,
InsideTx: f.insideTx,
TxStartTime: f.txStartTime,
f: func() {
err = f.ormer.ReadForUpdateWithCtx(ctx, md, cols...)
f: func(c context.Context) {
err = f.ormer.ReadForUpdateWithCtx(c, md, cols...)
},
}
f.root(ctx, inv)
@ -126,8 +131,8 @@ func (f *filterOrmDecorator) ReadOrCreateWithCtx(ctx context.Context, md interfa
mi: mi,
InsideTx: f.insideTx,
TxStartTime: f.txStartTime,
f: func() {
ok, res, err = f.ormer.ReadOrCreateWithCtx(ctx, md, col1, cols...)
f: func(c context.Context) {
ok, res, err = f.ormer.ReadOrCreateWithCtx(c, md, col1, cols...)
},
}
f.root(ctx, inv)
@ -152,8 +157,8 @@ func (f *filterOrmDecorator) LoadRelatedWithCtx(ctx context.Context, md interfac
mi: mi,
InsideTx: f.insideTx,
TxStartTime: f.txStartTime,
f: func() {
res, err = f.ormer.LoadRelatedWithCtx(ctx, md, name, args...)
f: func(c context.Context) {
res, err = f.ormer.LoadRelatedWithCtx(c, md, name, args...)
},
}
f.root(ctx, inv)
@ -177,8 +182,8 @@ func (f *filterOrmDecorator) QueryM2MWithCtx(ctx context.Context, md interface{}
mi: mi,
InsideTx: f.insideTx,
TxStartTime: f.txStartTime,
f: func() {
res = f.ormer.QueryM2MWithCtx(ctx, md, name)
f: func(c context.Context) {
res = f.ormer.QueryM2MWithCtx(c, md, name)
},
}
f.root(ctx, inv)
@ -191,10 +196,10 @@ func (f *filterOrmDecorator) QueryTable(ptrStructOrTableName interface{}) QueryS
func (f *filterOrmDecorator) QueryTableWithCtx(ctx context.Context, ptrStructOrTableName interface{}) QuerySeter {
var (
res QuerySeter
res QuerySeter
name string
md interface{}
mi *modelInfo
md interface{}
mi *modelInfo
)
if table, ok := ptrStructOrTableName.(string); ok {
@ -213,10 +218,10 @@ func (f *filterOrmDecorator) QueryTableWithCtx(ctx context.Context, ptrStructOrT
Args: []interface{}{ptrStructOrTableName},
InsideTx: f.insideTx,
TxStartTime: f.txStartTime,
Md: md,
mi: mi,
f: func() {
res = f.ormer.QueryTableWithCtx(ctx, ptrStructOrTableName)
Md: md,
mi: mi,
f: func(c context.Context) {
res = f.ormer.QueryTableWithCtx(c, ptrStructOrTableName)
},
}
f.root(ctx, inv)
@ -231,7 +236,7 @@ func (f *filterOrmDecorator) DBStats() *sql.DBStats {
Method: "DBStats",
InsideTx: f.insideTx,
TxStartTime: f.txStartTime,
f: func() {
f: func(c context.Context) {
res = f.ormer.DBStats()
},
}
@ -256,8 +261,8 @@ func (f *filterOrmDecorator) InsertWithCtx(ctx context.Context, md interface{})
mi: mi,
InsideTx: f.insideTx,
TxStartTime: f.txStartTime,
f: func() {
res, err = f.ormer.InsertWithCtx(ctx, md)
f: func(c context.Context) {
res, err = f.ormer.InsertWithCtx(c, md)
},
}
f.root(ctx, inv)
@ -281,8 +286,8 @@ func (f *filterOrmDecorator) InsertOrUpdateWithCtx(ctx context.Context, md inter
mi: mi,
InsideTx: f.insideTx,
TxStartTime: f.txStartTime,
f: func() {
res, err = f.ormer.InsertOrUpdateWithCtx(ctx, md, colConflitAndArgs...)
f: func(c context.Context) {
res, err = f.ormer.InsertOrUpdateWithCtx(c, md, colConflitAndArgs...)
},
}
f.root(ctx, inv)
@ -317,8 +322,8 @@ func (f *filterOrmDecorator) InsertMultiWithCtx(ctx context.Context, bulk int, m
mi: mi,
InsideTx: f.insideTx,
TxStartTime: f.txStartTime,
f: func() {
res, err = f.ormer.InsertMultiWithCtx(ctx, bulk, mds)
f: func(c context.Context) {
res, err = f.ormer.InsertMultiWithCtx(c, bulk, mds)
},
}
f.root(ctx, inv)
@ -342,8 +347,8 @@ func (f *filterOrmDecorator) UpdateWithCtx(ctx context.Context, md interface{},
mi: mi,
InsideTx: f.insideTx,
TxStartTime: f.txStartTime,
f: func() {
res, err = f.ormer.UpdateWithCtx(ctx, md, cols...)
f: func(c context.Context) {
res, err = f.ormer.UpdateWithCtx(c, md, cols...)
},
}
f.root(ctx, inv)
@ -367,8 +372,8 @@ func (f *filterOrmDecorator) DeleteWithCtx(ctx context.Context, md interface{},
mi: mi,
InsideTx: f.insideTx,
TxStartTime: f.txStartTime,
f: func() {
res, err = f.ormer.DeleteWithCtx(ctx, md, cols...)
f: func(c context.Context) {
res, err = f.ormer.DeleteWithCtx(c, md, cols...)
},
}
f.root(ctx, inv)
@ -388,8 +393,8 @@ func (f *filterOrmDecorator) RawWithCtx(ctx context.Context, query string, args
Args: []interface{}{query, args},
InsideTx: f.insideTx,
TxStartTime: f.txStartTime,
f: func() {
res = f.ormer.RawWithCtx(ctx, query, args...)
f: func(c context.Context) {
res = f.ormer.RawWithCtx(c, query, args...)
},
}
f.root(ctx, inv)
@ -404,7 +409,7 @@ func (f *filterOrmDecorator) Driver() Driver {
Method: "Driver",
InsideTx: f.insideTx,
TxStartTime: f.txStartTime,
f: func() {
f: func(c context.Context) {
res = f.ormer.Driver()
},
}
@ -434,28 +439,28 @@ func (f *filterOrmDecorator) BeginWithCtxAndOpts(ctx context.Context, opts *sql.
Args: []interface{}{opts},
InsideTx: f.insideTx,
TxStartTime: f.txStartTime,
f: func() {
res, err = f.TxBeginner.BeginWithCtxAndOpts(ctx, opts)
res = NewFilterTxOrmDecorator(res, f.root, getTxNameFromCtx(ctx))
f: func(c context.Context) {
res, err = f.TxBeginner.BeginWithCtxAndOpts(c, opts)
res = NewFilterTxOrmDecorator(res, f.root, getTxNameFromCtx(c))
},
}
f.root(ctx, inv)
return res, err
}
func (f *filterOrmDecorator) DoTx(task func(txOrm TxOrmer) error) error {
func (f *filterOrmDecorator) DoTx(task func(ctx context.Context, txOrm TxOrmer) error) error {
return f.DoTxWithCtxAndOpts(context.Background(), nil, task)
}
func (f *filterOrmDecorator) DoTxWithCtx(ctx context.Context, task func(txOrm TxOrmer) error) error {
func (f *filterOrmDecorator) DoTxWithCtx(ctx context.Context, task func(ctx context.Context, txOrm TxOrmer) error) error {
return f.DoTxWithCtxAndOpts(ctx, nil, task)
}
func (f *filterOrmDecorator) DoTxWithOpts(opts *sql.TxOptions, task func(txOrm TxOrmer) error) error {
func (f *filterOrmDecorator) DoTxWithOpts(opts *sql.TxOptions, task func(ctx context.Context, txOrm TxOrmer) error) error {
return f.DoTxWithCtxAndOpts(context.Background(), opts, task)
}
func (f *filterOrmDecorator) DoTxWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions, task func(txOrm TxOrmer) error) error {
func (f *filterOrmDecorator) DoTxWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions, task func(ctx context.Context, txOrm TxOrmer) error) error {
var (
err error
)
@ -466,8 +471,8 @@ func (f *filterOrmDecorator) DoTxWithCtxAndOpts(ctx context.Context, opts *sql.T
InsideTx: f.insideTx,
TxStartTime: f.txStartTime,
TxName: getTxNameFromCtx(ctx),
f: func() {
err = f.TxBeginner.DoTxWithCtxAndOpts(ctx, opts, task)
f: func(c context.Context) {
err = doTxTemplate(f, c, opts, task)
},
}
f.root(ctx, inv)
@ -484,7 +489,7 @@ func (f *filterOrmDecorator) Commit() error {
InsideTx: f.insideTx,
TxStartTime: f.txStartTime,
TxName: f.txName,
f: func() {
f: func(c context.Context) {
err = f.TxCommitter.Commit()
},
}
@ -502,7 +507,7 @@ func (f *filterOrmDecorator) Rollback() error {
InsideTx: f.insideTx,
TxStartTime: f.txStartTime,
TxName: f.txName,
f: func() {
f: func(c context.Context) {
err = f.TxCommitter.Rollback()
},
}
@ -517,4 +522,3 @@ func getTxNameFromCtx(ctx context.Context) string {
}
return txName
}

View File

@ -1,4 +1,4 @@
// Copyright 2020 beego
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -131,49 +131,49 @@ func TestFilterOrmDecorator_DoTx(t *testing.T) {
o := &filterMockOrm{}
od := NewFilterOrmDecorator(o, func(next Filter) Filter {
return func(ctx context.Context, inv *Invocation) {
assert.Equal(t, "DoTxWithCtxAndOpts", inv.Method)
assert.Equal(t, 2, len(inv.Args))
assert.Equal(t, "", inv.GetTableName())
assert.False(t, inv.InsideTx)
if inv.Method == "DoTxWithCtxAndOpts" {
assert.Equal(t, 2, len(inv.Args))
assert.Equal(t, "", inv.GetTableName())
assert.False(t, inv.InsideTx)
}
next(ctx, inv)
}
})
err := od.DoTx(func(txOrm TxOrmer) error {
return errors.New("tx error")
err := od.DoTx(func(c context.Context, txOrm TxOrmer) error {
return nil
})
assert.NotNil(t, err)
assert.Equal(t, "tx error", err.Error())
err = od.DoTxWithCtx(context.Background(), func(txOrm TxOrmer) error {
return errors.New("tx ctx error")
err = od.DoTxWithCtx(context.Background(), func(c context.Context, txOrm TxOrmer) error {
return nil
})
assert.NotNil(t, err)
assert.Equal(t, "tx ctx error", err.Error())
err = od.DoTxWithOpts(nil, func(txOrm TxOrmer) error {
return errors.New("tx opts error")
err = od.DoTxWithOpts(nil, func(c context.Context, txOrm TxOrmer) error {
return nil
})
assert.NotNil(t, err)
assert.Equal(t, "tx opts error", err.Error())
od = NewFilterOrmDecorator(o, func(next Filter) Filter {
return func(ctx context.Context, inv *Invocation) {
assert.Equal(t, "DoTxWithCtxAndOpts", inv.Method)
assert.Equal(t, 2, len(inv.Args))
assert.Equal(t, "", inv.GetTableName())
assert.Equal(t, "do tx name", inv.TxName)
assert.False(t, inv.InsideTx)
if inv.Method == "DoTxWithCtxAndOpts" {
assert.Equal(t, 2, len(inv.Args))
assert.Equal(t, "", inv.GetTableName())
assert.Equal(t, "do tx name", inv.TxName)
assert.False(t, inv.InsideTx)
}
next(ctx, inv)
}
})
ctx := context.WithValue(context.Background(), TxNameKey, "do tx name")
err = od.DoTxWithCtxAndOpts(ctx, nil, func(txOrm TxOrmer) error {
return errors.New("tx ctx opts error")
err = od.DoTxWithCtxAndOpts(ctx, nil, func(c context.Context, txOrm TxOrmer) error {
return nil
})
assert.NotNil(t, err)
assert.Equal(t, "tx ctx opts error", err.Error())
}
func TestFilterOrmDecorator_Driver(t *testing.T) {
@ -348,6 +348,8 @@ func TestFilterOrmDecorator_ReadOrCreate(t *testing.T) {
assert.Equal(t, int64(13), i)
}
var _ Ormer = new(filterMockOrm)
// filterMockOrm is only used in this test file
type filterMockOrm struct {
DoNothingOrm
@ -377,8 +379,8 @@ func (f *filterMockOrm) InsertWithCtx(ctx context.Context, md interface{}) (int6
return 100, errors.New("insert error")
}
func (f *filterMockOrm) DoTxWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions, task func(txOrm TxOrmer) error) error {
return task(nil)
func (f *filterMockOrm) DoTxWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions, task func(c context.Context, txOrm TxOrmer) error) error {
return task(ctx, nil)
}
func (f *filterMockOrm) DeleteWithCtx(ctx context.Context, md interface{}, cols ...string) (int64, error) {

View File

@ -1,4 +1,4 @@
// Copyright 2020 beego
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -28,4 +28,5 @@ func TestAddGlobalFilterChain(t *testing.T) {
}
})
assert.Equal(t, 1, len(globalFilterChains))
globalFilterChains = nil
}

View File

@ -1,4 +1,4 @@
// Copyright 2020 beego
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -15,6 +15,7 @@
package orm
import (
"context"
"time"
)
@ -22,27 +23,27 @@ import (
type Invocation struct {
Method string
// Md may be nil in some cases. It depends on method
Md interface{}
Md interface{}
// the args are all arguments except context.Context
Args []interface{}
Args []interface{}
mi *modelInfo
// f is the Orm operation
f func()
f func(ctx context.Context)
// insideTx indicates whether this is inside a transaction
InsideTx bool
InsideTx bool
TxStartTime time.Time
TxName string
TxName string
}
func (inv *Invocation) GetTableName() string {
if inv.mi != nil{
if inv.mi != nil {
return inv.mi.table
}
return ""
}
func (inv *Invocation) execute() {
inv.f()
func (inv *Invocation) execute(ctx context.Context) {
inv.f(ctx)
}

View File

@ -1,4 +1,4 @@
// Copyright 2020
// Copyright 2020
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -28,7 +28,6 @@ import (
_ "github.com/mattn/go-sqlite3"
// As tidb can't use go get, so disable the tidb testing now
// _ "github.com/pingcap/tidb"
)
// A slice string field.

View File

@ -528,19 +528,24 @@ func (o *orm) BeginWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions) (TxO
return taskTxOrm, nil
}
func (o *orm) DoTx(task func(txOrm TxOrmer) error) error {
func (o *orm) DoTx(task func(ctx context.Context, txOrm TxOrmer) error) error {
return o.DoTxWithCtx(context.Background(), task)
}
func (o *orm) DoTxWithCtx(ctx context.Context, task func(txOrm TxOrmer) error) error {
func (o *orm) DoTxWithCtx(ctx context.Context, task func(ctx context.Context, txOrm TxOrmer) error) error {
return o.DoTxWithCtxAndOpts(ctx, nil, task)
}
func (o *orm) DoTxWithOpts(opts *sql.TxOptions, task func(txOrm TxOrmer) error) error {
func (o *orm) DoTxWithOpts(opts *sql.TxOptions, task func(ctx context.Context, txOrm TxOrmer) error) error {
return o.DoTxWithCtxAndOpts(context.Background(), opts, task)
}
func (o *orm) DoTxWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions, task func(txOrm TxOrmer) error) error {
func (o *orm) DoTxWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions, task func(ctx context.Context, txOrm TxOrmer) error) error {
return doTxTemplate(o, ctx, opts, task)
}
func doTxTemplate(o TxBeginner, ctx context.Context, opts *sql.TxOptions,
task func(ctx context.Context, txOrm TxOrmer) error) error {
_txOrm, err := o.BeginWithCtxAndOpts(ctx, opts)
if err != nil {
return err
@ -559,9 +564,8 @@ func (o *orm) DoTxWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions, task
}
}
}()
var taskTxOrm = _txOrm
err = task(taskTxOrm)
err = task(ctx, taskTxOrm)
panicked = false
return err
}
@ -588,18 +592,11 @@ func NewOrm() Ormer {
// NewOrmUsingDB create new orm with the name
func NewOrmUsingDB(aliasName string) Ormer {
o := new(orm)
if al, ok := dataBaseCache.get(aliasName); ok {
o.alias = al
if Debug {
o.db = newDbQueryLog(al, al.DB)
} else {
o.db = al.DB
}
return newDBWithAlias(al)
} else {
panic(fmt.Errorf("<Ormer.Using> unknown db alias name `%s`", aliasName))
}
return o
}
// NewOrmWithDB create a new ormer object with specify *sql.DB for query
@ -609,14 +606,21 @@ func NewOrmWithDB(driverName, aliasName string, db *sql.DB, params ...common.KV)
return nil, err
}
return newDBWithAlias(al), nil
}
func newDBWithAlias(al *alias) Ormer {
o := new(orm)
o.alias = al
if Debug {
o.db = newDbQueryLog(o.alias, db)
o.db = newDbQueryLog(al, al.DB)
} else {
o.db = db
o.db = al.DB
}
return o, nil
if len(globalFilterChains) > 0 {
return NewFilterOrmDecorator(o, globalFilterChains...)
}
return o
}

View File

@ -2523,5 +2523,3 @@ func TestInsertOrUpdate(t *testing.T) {
throwFailNow(t, AssertIs((((user2.Status+1)-1)*3)/3, test.Status))
}
}

View File

@ -96,10 +96,10 @@ type TxBeginner interface {
BeginWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions) (TxOrmer, error)
//closure control transaction
DoTx(task func(txOrm TxOrmer) error) error
DoTxWithCtx(ctx context.Context, task func(txOrm TxOrmer) error) error
DoTxWithOpts(opts *sql.TxOptions, task func(txOrm TxOrmer) error) error
DoTxWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions, task func(txOrm TxOrmer) error) error
DoTx(task func(ctx context.Context, txOrm TxOrmer) error) error
DoTxWithCtx(ctx context.Context, task func(ctx context.Context, txOrm TxOrmer) error) error
DoTxWithOpts(opts *sql.TxOptions, task func(ctx context.Context, txOrm TxOrmer) error) error
DoTxWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions, task func(ctx context.Context, txOrm TxOrmer) error) error
}
type TxCommitter interface {

View File

@ -468,12 +468,13 @@ func (p *ControllerRegister) InsertFilter(pattern string, pos int, filter Filter
// // do something
// }
// }
func (p *ControllerRegister) InsertFilterChain(pattern string, chain FilterChain, params...bool) {
func (p *ControllerRegister) InsertFilterChain(pattern string, chain FilterChain, params ...bool) {
root := p.chainRoot
filterFunc := chain(root.filterFunc)
p.chainRoot = newFilterRouter(pattern, BConfig.RouterCaseSensitive, filterFunc, params...)
}
p.chainRoot.next = root
}
// add Filter into
func (p *ControllerRegister) insertFilterRouter(pos int, mr *FilterRouter) (err error) {

View File

@ -57,7 +57,7 @@ func TestFileProvider_SessionExist(t *testing.T) {
_ = fp.SessionInit(180, sessionPath)
exists, err := fp.SessionExist(sid)
if err != nil{
if err != nil {
t.Error(err)
}
if exists {

View File

@ -1,4 +1,4 @@
// Copyright 2020 beego
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,4 +1,4 @@
// Copyright 2020 beego
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -31,7 +31,6 @@ type FilterChainBuilder struct {
CustomSpanFunc func(span opentracing.Span, ctx *beegoCtx.Context)
}
func (builder *FilterChainBuilder) FilterChain(next beego.FilterFunc) beego.FilterFunc {
return func(ctx *beegoCtx.Context) {
var (
@ -55,9 +54,21 @@ func (builder *FilterChainBuilder) FilterChain(next beego.FilterFunc) beego.Filt
next(ctx)
// if you think we need to do more things, feel free to create an issue to tell us
span.SetTag("status", ctx.Output.Status)
span.SetTag("method", ctx.Input.Method())
span.SetTag("route", ctx.Input.GetData("RouterPattern"))
span.SetTag("http.status_code", ctx.ResponseWriter.Status)
span.SetTag("http.method", ctx.Input.Method())
span.SetTag("peer.hostname", ctx.Request.Host)
span.SetTag("http.url", ctx.Request.URL.String())
span.SetTag("http.scheme", ctx.Request.URL.Scheme)
span.SetTag("span.kind", "server")
span.SetTag("component", "beego")
if ctx.Output.IsServerError() || ctx.Output.IsClientError() {
span.SetTag("error", true)
}
span.SetTag("peer.address", ctx.Request.RemoteAddr)
span.SetTag("http.proto", ctx.Request.Proto)
span.SetTag("beego.route", ctx.Input.GetData("RouterPattern"))
if builder.CustomSpanFunc != nil {
builder.CustomSpanFunc(span, ctx)
}
@ -70,7 +81,7 @@ func (builder *FilterChainBuilder) operationName(ctx *beegoCtx.Context) string {
// TODO, if we support multiple servers, this need to be changed
route, found := beego.BeeApp.Handlers.FindRouter(ctx)
if found {
operationName = route.GetPattern()
operationName = ctx.Input.Method() + "#" + route.GetPattern()
}
return operationName
}

View File

@ -1,4 +1,4 @@
// Copyright 2020 beego
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,4 +1,4 @@
// Copyright 2020 beego
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@ -1,4 +1,4 @@
// Copyright 2020 beego
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.