mirror of https://github.com/knative/pkg.git
191 lines
4.8 KiB
Go
191 lines
4.8 KiB
Go
/*
|
|
Copyright 2020 The Knative Authors
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package tracing
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"sync"
|
|
|
|
oczipkin "contrib.go.opencensus.io/exporter/zipkin"
|
|
"github.com/openzipkin/zipkin-go"
|
|
httpreporter "github.com/openzipkin/zipkin-go/reporter/http"
|
|
"go.opencensus.io/trace"
|
|
"go.uber.org/zap"
|
|
|
|
"knative.dev/pkg/tracing/config"
|
|
)
|
|
|
|
// ConfigOption is the interface for adding additional exporters and configuring opencensus tracing.
|
|
type ConfigOption func(*config.Config) error
|
|
|
|
// OpenCensusTracer is responsible for managing and updating configuration of OpenCensus tracing
|
|
type OpenCensusTracer struct {
|
|
curCfg *config.Config
|
|
configOptions []ConfigOption
|
|
|
|
closer io.Closer
|
|
exporter trace.Exporter
|
|
}
|
|
|
|
// OpenCensus tracing keeps state in globals and therefore we can only run one OpenCensusTracer
|
|
var (
|
|
octMutex sync.Mutex
|
|
globalOct *OpenCensusTracer
|
|
)
|
|
|
|
func NewOpenCensusTracer(configOptions ...ConfigOption) *OpenCensusTracer {
|
|
return &OpenCensusTracer{
|
|
configOptions: configOptions,
|
|
}
|
|
}
|
|
|
|
func (oct *OpenCensusTracer) ApplyConfig(cfg *config.Config) error {
|
|
err := oct.acquireGlobal()
|
|
defer octMutex.Unlock()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Short circuit if our config hasn't changed.
|
|
if oct.curCfg != nil && oct.curCfg.Equals(cfg) {
|
|
return nil
|
|
}
|
|
|
|
// Apply config options
|
|
for _, configOpt := range oct.configOptions {
|
|
if err = configOpt(cfg); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Set config
|
|
trace.ApplyConfig(*createOCTConfig(cfg))
|
|
|
|
return nil
|
|
}
|
|
|
|
func (oct *OpenCensusTracer) Finish() error {
|
|
err := oct.acquireGlobal()
|
|
defer octMutex.Unlock()
|
|
if err != nil {
|
|
return errors.New("finish called on OpenTracer which is not the global OpenCensusTracer")
|
|
}
|
|
|
|
for _, configOpt := range oct.configOptions {
|
|
if err = configOpt(nil); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
globalOct = nil
|
|
|
|
return nil
|
|
}
|
|
|
|
func (oct *OpenCensusTracer) acquireGlobal() error {
|
|
octMutex.Lock()
|
|
|
|
if globalOct == nil {
|
|
globalOct = oct
|
|
} else if globalOct != oct {
|
|
return errors.New("an OpenCensusTracer already exists and only one can be run at a time")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func createOCTConfig(cfg *config.Config) *trace.Config {
|
|
octCfg := trace.Config{}
|
|
|
|
if cfg.Backend != config.None {
|
|
if cfg.Debug {
|
|
octCfg.DefaultSampler = trace.AlwaysSample()
|
|
} else {
|
|
octCfg.DefaultSampler = trace.ProbabilitySampler(cfg.SampleRate)
|
|
}
|
|
} else {
|
|
octCfg.DefaultSampler = trace.NeverSample()
|
|
}
|
|
|
|
return &octCfg
|
|
}
|
|
|
|
// WithExporter returns a ConfigOption for use with NewOpenCensusTracer that configures
|
|
// it to export traces based on the configuration read from config-tracing.
|
|
func WithExporter(name string, logger *zap.SugaredLogger) ConfigOption {
|
|
return WithExporterFull(name, name, logger)
|
|
}
|
|
|
|
// WithExporterFull supports host argument for WithExporter.
|
|
// The host arg is used for a value of tag ip="{IP}" so you can use an actual IP. Otherwise,
|
|
// the host name must be able to be resolved.
|
|
// e.g)
|
|
// "name" is a service name like activator-service.
|
|
// "host" is a endpoint IP like activator-service's endpoint IP.
|
|
func WithExporterFull(name, host string, logger *zap.SugaredLogger) ConfigOption {
|
|
return func(cfg *config.Config) error {
|
|
var (
|
|
exporter trace.Exporter
|
|
closer io.Closer
|
|
)
|
|
switch cfg.Backend {
|
|
case config.Zipkin:
|
|
// If host isn't specified, then zipkin.NewEndpoint will return an error saying that it
|
|
// can't find the host named ''. So, if not specified, default it to this machine's
|
|
// hostname.
|
|
if host == "" {
|
|
n, err := os.Hostname()
|
|
if err != nil {
|
|
return fmt.Errorf("unable to get hostname: %w", err)
|
|
}
|
|
host = n
|
|
}
|
|
if name == "" {
|
|
name = host
|
|
}
|
|
zipEP, err := zipkin.NewEndpoint(name, host)
|
|
if err != nil {
|
|
logger.Errorw("error building zipkin endpoint", zap.Error(err))
|
|
return err
|
|
}
|
|
reporter := httpreporter.NewReporter(cfg.ZipkinEndpoint)
|
|
exporter = oczipkin.NewExporter(reporter, zipEP)
|
|
closer = reporter
|
|
|
|
default:
|
|
// Disables tracing.
|
|
}
|
|
if exporter != nil {
|
|
trace.RegisterExporter(exporter)
|
|
}
|
|
// We know this is set because we are called with acquireGlobal lock held
|
|
if globalOct.exporter != nil {
|
|
trace.UnregisterExporter(globalOct.exporter)
|
|
}
|
|
if globalOct.closer != nil {
|
|
globalOct.closer.Close()
|
|
}
|
|
|
|
globalOct.exporter = exporter
|
|
globalOct.closer = closer
|
|
|
|
return nil
|
|
}
|
|
}
|