MM-21898: Part 2. Add opentracing (#13904)

* initial implementation of opentracing

* app layer

* Revert Makefile

* .

* cleanup

* .

* .

* .

* .

* .

* .

* .

* .

* .

* .

* .

* [ci]

* autogenerate interface

* .

* missed vendor files

* updated interfaces

* updated store layers

* lint fixes

* .

* finishing layer generators and nested spans

* added errors and b3 support

* code review

* .

* .

* fixed build error due to misplased flag.Parse()

* code review addressed
Этот коммит содержится в:
Miguel de la Cruz
2020-03-05 14:46:08 +01:00
коммит произвёл GitHub
родитель 5a34ec4793
Коммит 182c29b456
154 изменённых файлов: 47653 добавлений и 264 удалений

Просмотреть файл

@@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// Code generated by "make store-layers"
// Code generated by "make app-layers"
// DO NOT EDIT
package app

247
app/layer_generators/main.go Обычный файл
Просмотреть файл

@@ -0,0 +1,247 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package main
import (
"bytes"
"flag"
"fmt"
"go/ast"
"go/parser"
"go/token"
"io/ioutil"
"log"
"os"
"path"
"strings"
"text/template"
"golang.org/x/tools/imports"
)
var (
reserved = []string{"AcceptLanguage", "AccountMigration", "Cluster", "Compliance", "Context", "DataRetention", "Elasticsearch", "HTTPService", "ImageProxy", "IpAddress", "Ldap", "Log", "MessageExport", "Metrics", "Notification", "NotificationsLog", "Path", "RequestId", "Saml", "Session", "SetIpAddress", "SetRequestId", "SetSession", "SetStore", "SetT", "Srv", "Store", "T", "Timezones", "UserAgent", "SetUserAgent", "SetAcceptLanguage", "SetPath", "SetContext", "SetServer", "GetT"}
outputFile string
inputFile string
outputFileTemplate string
)
const (
OPEN_TRACING_PARAMS_MARKER = "@openTracingParams"
APP_ERROR_TYPE = "*model.AppError"
)
func init() {
flag.StringVar(&inputFile, "in", path.Join("..", "app_iface.go"), "App interface file")
flag.StringVar(&outputFile, "out", path.Join("..", "opentracing_layer.go"), "Output file")
flag.StringVar(&outputFileTemplate, "template", "opentracing_layer.go.tmpl", "Output template file")
}
func main() {
flag.Parse()
code, err := generateLayer("OpenTracingAppLayer", outputFileTemplate)
if err != nil {
log.Fatal(err)
}
formattedCode, err := imports.Process(outputFile, code, &imports.Options{Comments: true})
if err != nil {
log.Fatal(err)
}
err = ioutil.WriteFile(outputFile, formattedCode, 0644)
if err != nil {
log.Fatal(err)
}
}
type methodParam struct {
Name string
Type string
}
type methodData struct {
ParamsToTrace map[string]bool
Params []methodParam
Results []string
}
type storeMetadata struct {
Name string
Methods map[string]methodData
}
func formatNode(src []byte, node ast.Expr) string {
return string(src[node.Pos()-1 : node.End()-1])
}
func extractMethodMetadata(method *ast.Field, src []byte) methodData {
params := []methodParam{}
paramsToTrace := map[string]bool{}
results := []string{}
e := method.Type.(*ast.FuncType)
if method.Doc != nil {
for _, comment := range method.Doc.List {
s := comment.Text
if idx := strings.Index(s, OPEN_TRACING_PARAMS_MARKER); idx != -1 {
for _, p := range strings.Split(s[idx+len(OPEN_TRACING_PARAMS_MARKER):], ",") {
paramsToTrace[strings.TrimSpace(p)] = true
}
}
}
}
if e.Params != nil {
for _, param := range e.Params.List {
for _, paramName := range param.Names {
paramType := (formatNode(src, param.Type))
params = append(params, methodParam{Name: paramName.Name, Type: paramType})
}
}
}
if e.Results != nil {
for _, result := range e.Results.List {
results = append(results, formatNode(src, result.Type))
}
}
for paramName := range paramsToTrace {
found := false
for _, param := range params {
if param.Name == paramName {
found = true
break
}
}
if !found {
log.Fatalf("Unable to find a parameter called '%s' (method '%s') that is mentioned in the '%s' comment. Maybe it was renamed?", paramName, method.Names[0].Name, OPEN_TRACING_PARAMS_MARKER)
}
}
return methodData{Params: params, Results: results, ParamsToTrace: paramsToTrace}
}
func extractStoreMetadata() (*storeMetadata, error) {
// Create the AST by parsing src.
fset := token.NewFileSet() // positions are relative to fset
file, err := os.Open(inputFile)
if err != nil {
return nil, fmt.Errorf("Unable to open %s file: %w", inputFile, err)
}
src, err := ioutil.ReadAll(file)
if err != nil {
return nil, err
}
defer file.Close()
f, err := parser.ParseFile(fset, "../app_iface.go", src, parser.AllErrors|parser.ParseComments)
if err != nil {
return nil, err
}
metadata := storeMetadata{Methods: map[string]methodData{}}
ast.Inspect(f, func(n ast.Node) bool {
switch x := n.(type) {
case *ast.TypeSpec:
if x.Name.Name == "AppIface" {
for _, method := range x.Type.(*ast.InterfaceType).Methods.List {
methodName := method.Names[0].Name
found := false
for _, reservedMethod := range reserved {
if methodName == reservedMethod {
found = true
break
}
}
if found {
continue
}
metadata.Methods[methodName] = extractMethodMetadata(method, src)
}
}
}
return true
})
return &metadata, err
}
func generateLayer(name, templateFile string) ([]byte, error) {
out := bytes.NewBufferString("")
metadata, err := extractStoreMetadata()
if err != nil {
return nil, err
}
metadata.Name = name
myFuncs := template.FuncMap{
"joinResults": func(results []string) string {
return strings.Join(results, ", ")
},
"joinResultsForSignature": func(results []string) string {
switch len(results) {
case 0:
return ""
case 1:
return strings.Join(results, ", ")
}
return fmt.Sprintf("(%s)", strings.Join(results, ", "))
},
"genResultsVars": func(results []string) string {
vars := make([]string, 0, len(results))
for i := range results {
vars = append(vars, fmt.Sprintf("resultVar%d", i))
}
return strings.Join(vars, ", ")
},
"errorToBoolean": func(results []string) string {
for i, typeName := range results {
if typeName == APP_ERROR_TYPE {
return fmt.Sprintf("resultVar%d == nil", i)
}
}
return "true"
},
"errorPresent": func(results []string) bool {
for _, typeName := range results {
if typeName == "*model.AppError" {
return true
}
}
return false
},
"errorVar": func(results []string) string {
for i, typeName := range results {
if typeName == "*model.AppError" {
return fmt.Sprintf("resultVar%d", i)
}
}
return ""
},
"joinParams": func(params []methodParam) string {
paramsNames := []string{}
for _, param := range params {
s := param.Name
if strings.HasPrefix(param.Type, "...") {
s += "..."
}
paramsNames = append(paramsNames, s)
}
return strings.Join(paramsNames, ", ")
},
"joinParamsWithType": func(params []methodParam) string {
paramsWithType := []string{}
for _, param := range params {
paramsWithType = append(paramsWithType, fmt.Sprintf("%s %s", param.Name, param.Type))
}
return strings.Join(paramsWithType, ", ")
},
}
t := template.Must(template.New("opentracing_layer.go.tmpl").Funcs(myFuncs).ParseFiles(templateFile))
err = t.Execute(out, metadata)
if err != nil {
return nil, err
}
return out.Bytes(), nil
}

Просмотреть файл

@@ -0,0 +1,218 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// Code generated by "make app-layers"
// DO NOT EDIT
package app
import (
"github.com/opentracing/opentracing-go/ext"
spanlog "github.com/opentracing/opentracing-go/log"
)
type {{.Name}} struct {
app AppIface
srv *Server
log *mlog.Logger
notificationsLog *mlog.Logger
t goi18n.TranslateFunc
session model.Session
requestId string
ipAddress string
path string
userAgent string
acceptLanguage string
accountMigration einterfaces.AccountMigrationInterface
cluster einterfaces.ClusterInterface
compliance einterfaces.ComplianceInterface
dataRetention einterfaces.DataRetentionInterface
elasticsearch einterfaces.ElasticsearchInterface
ldap einterfaces.LdapInterface
messageExport einterfaces.MessageExportInterface
metrics einterfaces.MetricsInterface
notification einterfaces.NotificationInterface
saml einterfaces.SamlInterface
httpService httpservice.HTTPService
imageProxy *imageproxy.ImageProxy
timezones *timezones.Timezones
context context.Context
ctx context.Context
}
{{range $index, $element := .Methods}}
func (a *{{$.Name}}) {{$index}}({{$element.Params | joinParamsWithType}}) {{$element.Results | joinResultsForSignature}} {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.{{$index}}")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
{{range $paramIdx, $param := $element.Params}}
{{if index $element.ParamsToTrace $param.Name}}
span.SetTag("{{$param.Name}}", {{$param.Name}})
{{end}}
{{end}}
defer span.Finish()
{{- if $element.Results | len | eq 0}}
a.app.{{$index}}({{$element.Params | joinParams}})
{{else}}
{{$element.Results | genResultsVars}} := a.app.{{$index}}({{$element.Params | joinParams}})
{{if $element.Results | errorPresent}}
if {{$element.Results | errorVar}} != nil {
span.LogFields(spanlog.Error({{$element.Results | errorVar}}))
ext.Error.Set(span, true)
}
{{end}}
return {{$element.Results | genResultsVars -}}
{{end}}}
{{end}}
func NewOpenTracingAppLayer(childApp AppIface, ctx context.Context) *{{.Name}} {
newApp := {{.Name}}{
app: childApp,
ctx: ctx,
}
newApp.srv = childApp.Srv()
newApp.log = childApp.Log()
newApp.notificationsLog = childApp.NotificationsLog()
newApp.t = childApp.GetT()
if childApp.Session() != nil {
newApp.session = *childApp.Session()
}
newApp.requestId = childApp.RequestId()
newApp.ipAddress = childApp.IpAddress()
newApp.path = childApp.Path()
newApp.userAgent = childApp.UserAgent()
newApp.acceptLanguage = childApp.AcceptLanguage()
newApp.accountMigration = childApp.AccountMigration()
newApp.cluster = childApp.Cluster()
newApp.compliance = childApp.Compliance()
newApp.dataRetention = childApp.DataRetention()
newApp.elasticsearch = childApp.Elasticsearch()
newApp.ldap = childApp.Ldap()
newApp.messageExport = childApp.MessageExport()
newApp.metrics = childApp.Metrics()
newApp.notification = childApp.Notification()
newApp.saml = childApp.Saml()
newApp.httpService = childApp.HTTPService()
newApp.imageProxy = childApp.ImageProxy()
newApp.timezones = childApp.Timezones()
newApp.context = childApp.Context()
return &newApp
}
func (a *{{.Name}}) Srv() *Server {
return a.srv
}
func (a *{{.Name}}) Log() *mlog.Logger {
return a.log
}
func (a *{{.Name}}) NotificationsLog() *mlog.Logger {
return a.notificationsLog
}
func (a *{{.Name}}) T(translationID string, args ...interface{}) string {
return a.t(translationID, args...)
}
func (a *{{.Name}}) Session() *model.Session {
return &a.session
}
func (a *{{.Name}}) RequestId() string {
return a.requestId
}
func (a *{{.Name}}) IpAddress() string {
return a.ipAddress
}
func (a *{{.Name}}) Path() string {
return a.path
}
func (a *{{.Name}}) UserAgent() string {
return a.userAgent
}
func (a *{{.Name}}) AcceptLanguage() string {
return a.acceptLanguage
}
func (a *{{.Name}}) AccountMigration() einterfaces.AccountMigrationInterface {
return a.accountMigration
}
func (a *{{.Name}}) Cluster() einterfaces.ClusterInterface {
return a.cluster
}
func (a *{{.Name}}) Compliance() einterfaces.ComplianceInterface {
return a.compliance
}
func (a *{{.Name}}) DataRetention() einterfaces.DataRetentionInterface {
return a.dataRetention
}
func (a *{{.Name}}) Elasticsearch() einterfaces.ElasticsearchInterface {
return a.elasticsearch
}
func (a *{{.Name}}) Ldap() einterfaces.LdapInterface {
return a.ldap
}
func (a *{{.Name}}) MessageExport() einterfaces.MessageExportInterface {
return a.messageExport
}
func (a *{{.Name}}) Metrics() einterfaces.MetricsInterface {
return a.metrics
}
func (a *{{.Name}}) Notification() einterfaces.NotificationInterface {
return a.notification
}
func (a *{{.Name}}) Saml() einterfaces.SamlInterface {
return a.saml
}
func (a *{{.Name}}) HTTPService() httpservice.HTTPService {
return a.httpService
}
func (a *{{.Name}}) ImageProxy() *imageproxy.ImageProxy {
return a.imageProxy
}
func (a *{{.Name}}) Timezones() *timezones.Timezones {
return a.timezones
}
func (a *{{.Name}}) Context() context.Context {
return a.context
}
func (a *{{.Name}}) SetSession(sess *model.Session) {
a.session = *sess
}
func (a *{{.Name}}) SetT(t goi18n.TranslateFunc){
a.t = t
}
func (a *{{.Name}}) SetRequestId(str string){
a.requestId = str
}
func (a *{{.Name}}) SetIpAddress(str string){
a.ipAddress = str
}
func (a *{{.Name}}) SetUserAgent(str string){
a.userAgent = str
}
func (a *{{.Name}}) SetAcceptLanguage(str string) {
a.acceptLanguage = str
}
func (a *{{.Name}}) SetPath(str string){
a.path = str
}
func (a *{{.Name}}) SetContext(c context.Context){
a.context = c
}
func (a *{{.Name}}) SetServer(srv *Server) {
a.srv = srv
}
func (a *{{.Name}}) GetT() goi18n.TranslateFunc {
return a.t
}