pkg/tracing/opencensus.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
}
}