go.uber.org/yarpc@v1.72.1/config.go (about)

     1  // Copyright (c) 2022 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package yarpc
    22  
    23  import (
    24  	"context"
    25  	"time"
    26  
    27  	opentracing "github.com/opentracing/opentracing-go"
    28  	"github.com/uber-go/tally"
    29  	"go.uber.org/net/metrics"
    30  	"go.uber.org/net/metrics/tallypush"
    31  	"go.uber.org/yarpc/api/middleware"
    32  	"go.uber.org/yarpc/internal/observability"
    33  	"go.uber.org/zap"
    34  	"go.uber.org/zap/zapcore"
    35  )
    36  
    37  const (
    38  	// Sleep between pushes to Tally metrics. At some point, we may want this
    39  	// to be configurable.
    40  	_tallyPushInterval = 500 * time.Millisecond
    41  	_packageName       = "yarpc"
    42  )
    43  
    44  // DirectionalLogLevelConfig may override the log levels for any combination of
    45  // successes, failures, and application errors.
    46  type DirectionalLogLevelConfig struct {
    47  	// Level at which successful requests are logged.
    48  	// Defaults to DebugLevel.
    49  	Success *zapcore.Level
    50  	// Level at which errors are logged.
    51  	// Thrift exceptions are application errors, which we log as a separate
    52  	// class from success and failure.
    53  	// Deprecated in favor of ServerError and ClientError.
    54  	Failure *zapcore.Level
    55  	// Level at which application errors are logged.
    56  	// All Thrift exceptions are considered application errors.
    57  	// All errors from Protobuf handlers are application errors.
    58  	// Defaults to ErrorLevel.
    59  	// Deprecated in favor of ServerError and ClientError.
    60  	ApplicationError *zapcore.Level
    61  	// Level at which client errors are logged.
    62  	// All Thrift exceptions are considered application errors if
    63  	// they are not annotated with the option rpc.code.
    64  	// Defaults to ErrorLevel.
    65  	ClientError *zapcore.Level
    66  	// Level at which server errors are logged.
    67  	// Defaults to ErrorLevel.
    68  	ServerError *zapcore.Level
    69  }
    70  
    71  // LogLevelConfig configures the levels at which YARPC logs various things.
    72  type LogLevelConfig struct {
    73  	// Level at which successful requests are logged.
    74  	// Defaults to DebugLevel.
    75  	// Can be overridden by Inbound.Success or Outbound.Success for inbound or
    76  	// outbound requests.
    77  	Success *zapcore.Level
    78  	// Level at which errors are logged.
    79  	// Thrift exceptions are application errors, which we log as a separate
    80  	// class from success and failure.
    81  	// Can be overridden by Inbound.Failure or Outbound.Failure for inbound or
    82  	// outbound requests.
    83  	// Deprecated in favor of ServerError and ClientError.
    84  	Failure *zapcore.Level
    85  	// Level at which application errors are logged.
    86  	// All Thrift exceptions are considered application errors.
    87  	// Defaults to ErrorLevel.
    88  	// Can be overridden by Inbound.ApplicationError or
    89  	// Outbound.ApplicationError for inbound or outbound requests.
    90  	// Deprecated in favor of ServerError and ClientError.
    91  	ApplicationError *zapcore.Level
    92  	// Level at which client errors are logged.
    93  	// All Thrift exceptions are considered application errors if
    94  	// they are not annotated with the option rpc.code.
    95  	// Defaults to ErrorLevel.
    96  	// Can be overridden by Inbound.ApplicationError or
    97  	// Outbound.ApplicationError for inbound or outbound requests.
    98  	ClientError *zapcore.Level
    99  	// Level at which server errors are logged.
   100  	// Defaults to ErrorLevel.
   101  	// Can be overridden by Inbound.ApplicationError or
   102  	// Outbound.ApplicationError for inbound or outbound requests.
   103  	ServerError *zapcore.Level
   104  	// Specific overrides for inbound and outbound requests.
   105  	Inbound, Outbound DirectionalLogLevelConfig
   106  }
   107  
   108  // LoggingConfig describes how logging should be configured.
   109  type LoggingConfig struct {
   110  	// Supplies a logger for the dispatcher. By default, no logs are
   111  	// emitted.
   112  	Zap *zap.Logger
   113  
   114  	// If supplied, ExtractContext is used to log request-scoped
   115  	// information carried on the context (e.g., trace and span IDs).
   116  	ContextExtractor func(context.Context) zapcore.Field
   117  
   118  	// Levels configures the levels at which YARPC logs various messages.
   119  	Levels LogLevelConfig
   120  }
   121  
   122  func (c LoggingConfig) logger(name string) *zap.Logger {
   123  	if c.Zap == nil {
   124  		return zap.NewNop()
   125  	}
   126  	return c.Zap.Named(_packageName).With(
   127  		// Use a namespace to prevent key collisions with other libraries.
   128  		zap.Namespace(_packageName),
   129  		zap.String("dispatcher", name),
   130  	)
   131  }
   132  
   133  func (c LoggingConfig) extractor() observability.ContextExtractor {
   134  	if c.ContextExtractor == nil {
   135  		return observability.NewNopContextExtractor()
   136  	}
   137  	return observability.ContextExtractor(c.ContextExtractor)
   138  }
   139  
   140  // MetricsConfig describes how telemetry should be configured.
   141  // Scope and Tally are exclusive; choose one.
   142  // If neither is present, metrics are not recorded, all instrumentation becomes
   143  // no-ops.
   144  // If both are present, we emit a warning and ignore Tally.
   145  // If a metrics scope is preseent, we use that scope to record metrics and they
   146  // are not pushed to Tally.
   147  // If Tally is present, we use its metrics scope and push them periodically.
   148  type MetricsConfig struct {
   149  	// Metrics is a *"go.uber.org/net/metrics".Scope for recording stats.
   150  	// YARPC does not push these metrics; pushing metrics from the root is an
   151  	// external concern.
   152  	Metrics *metrics.Scope
   153  	// Tally scope used for pushing to M3 or StatsD-based systems. By
   154  	// default, metrics are collected in memory but not pushed.
   155  	// TODO deprecate this option for metrics configuration.
   156  	Tally tally.Scope
   157  	// TagsBlocklist enlists tags' keys that should be suppressed from all the metrics
   158  	// emitted from w/in YARPC middleware.
   159  	TagsBlocklist []string
   160  }
   161  
   162  func (c MetricsConfig) scope(name string, logger *zap.Logger) (*metrics.Scope, context.CancelFunc) {
   163  	// Neither: no-op metrics, not pushed
   164  	if c.Metrics == nil && c.Tally == nil {
   165  		return nil, func() {}
   166  	}
   167  
   168  	// Both: ignore Tally and warn.
   169  	if c.Metrics != nil && c.Tally != nil {
   170  		logger.Warn("yarpc.NewDispatcher expects only one of Metrics.Tally or Metrics.Scope. " +
   171  			"To push to Tally, either use a Metrics.Scope and use tallypush, or just pass a Tally Scope")
   172  		c.Tally = nil
   173  	}
   174  
   175  	// Hereafter: We have one of either c.Metrics or c.Tally exclusively.
   176  
   177  	var root *metrics.Root    // For pushing, if present
   178  	var parent *metrics.Scope // For measuring
   179  
   180  	if c.Metrics != nil {
   181  		// root remains nil
   182  		parent = c.Metrics
   183  	} else { // c.Tally != nil
   184  		root = metrics.New()
   185  		parent = root.Scope()
   186  	}
   187  
   188  	meter := parent.Tagged(metrics.Tags{
   189  		"component":  _packageName,
   190  		"dispatcher": name,
   191  	})
   192  
   193  	// When we have c.Metrics, we do not push
   194  	if root == nil {
   195  		return meter, func() {}
   196  	}
   197  
   198  	// When we have c.Tally, we measure *and* push
   199  	stopMeter, err := root.Push(tallypush.New(c.Tally), _tallyPushInterval)
   200  	if err != nil {
   201  		logger.Error("Failed to start pushing metrics to Tally.", zap.Error(err))
   202  		return meter, func() {}
   203  	}
   204  	return meter, stopMeter
   205  }
   206  
   207  // Config specifies the parameters of a new Dispatcher constructed via
   208  // NewDispatcher.
   209  type Config struct {
   210  	// Name of the service. This is the name used by other services when
   211  	// making requests to this service.
   212  	Name string
   213  
   214  	// Inbounds define how this service receives incoming requests from other
   215  	// services.
   216  	//
   217  	// This may be nil if this service does not receive any requests.
   218  	Inbounds Inbounds
   219  
   220  	// Outbounds defines how this service makes requests to other services.
   221  	//
   222  	// This may be nil if this service does not send any requests.
   223  	Outbounds Outbounds
   224  
   225  	// Inbound and Outbound Middleware that will be applied to all incoming
   226  	// and outgoing requests respectively.
   227  	//
   228  	// These may be nil if there is no middleware to apply.
   229  	InboundMiddleware  InboundMiddleware
   230  	OutboundMiddleware OutboundMiddleware
   231  
   232  	// Tracer is meant to add/record tracing information to a request.
   233  	//
   234  	// Deprecated: The dispatcher does nothing with this property.  Set the
   235  	// tracer directly on the transports used to build inbounds and outbounds.
   236  	Tracer opentracing.Tracer
   237  
   238  	// RouterMiddleware is middleware to control how requests are routed.
   239  	RouterMiddleware middleware.Router
   240  
   241  	// Configures logging.
   242  	Logging LoggingConfig
   243  
   244  	// Configures telemetry.
   245  	Metrics MetricsConfig
   246  
   247  	// DisableAutoObservabilityMiddleware is used to stop the dispatcher from
   248  	// automatically attaching observability middleware to all inbounds and
   249  	// outbounds.  It is the assumption that if if this option is disabled the
   250  	// observability middleware is being inserted in the Inbound/Outbound
   251  	// Middleware.
   252  	DisableAutoObservabilityMiddleware bool
   253  }