You've already forked opentelemetry-go
							
							
				mirror of
				https://github.com/open-telemetry/opentelemetry-go.git
				synced 2025-10-31 00:07:40 +02:00 
			
		
		
		
	Add HTTP metrics exporter for OTLP (#2022)
* add HTTP support for OTLP metrics exporter * add to changelog * fix unit test not covered before * add otlpmetrichttp module to .github/dependabot.yml * add test file for client.go * run make commit to clean repository * fix err occurs in TestCancelledContext * fix err in TestCancelledContext which is nil * fix err in TestCancelledContext which is nil, occurs occasionally * fix unittest err in TestCancelledContext which occurs occasionally * fix ineffectual assignment to err when running lint * Update CHANGELOG.md Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com> * Update exporters/otlp/otlpmetric/otlpmetrichttp/doc.go Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com> * Update exporters/otlp/otlpmetric/otlpmetrichttp/doc.go Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com> * del options that not used in http exporter metrics Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
This commit is contained in:
		
							
								
								
									
										10
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							| @@ -316,3 +316,13 @@ updates: | ||||
|     schedule: | ||||
|       day: sunday | ||||
|       interval: weekly | ||||
|   - | ||||
|     package-ecosystem: gomod | ||||
|     directory: /exporters/otlp/otlpmetric/otlpmetrichttp | ||||
|     labels: | ||||
|       - dependencies | ||||
|       - go | ||||
|       - "Skip Changelog" | ||||
|     schedule: | ||||
|       day: sunday | ||||
|       interval: weekly | ||||
|   | ||||
| @@ -10,6 +10,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm | ||||
|  | ||||
| ### Added | ||||
|  | ||||
| - Adds HTTP support for OTLP metrics exporter. (#2022) | ||||
|  | ||||
| ### Changed | ||||
|  | ||||
| ### Deprecated | ||||
|   | ||||
| @@ -69,3 +69,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -65,3 +65,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -65,3 +65,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -67,3 +67,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -69,3 +69,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -68,3 +68,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -68,3 +68,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -67,3 +67,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -66,3 +66,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -69,3 +69,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -75,3 +75,5 @@ replace go.opentelemetry.io/otel/exporters/zipkin => ../../zipkin | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ./otlpmetrichttp | ||||
|   | ||||
| @@ -24,9 +24,16 @@ import ( | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// DefaultMaxAttempts describes how many times the driver | ||||
| 	// should retry the sending of the payload in case of a | ||||
| 	// retryable error. | ||||
| 	DefaultMaxAttempts int = 5 | ||||
| 	// DefaultMetricsPath is a default URL path for endpoint that | ||||
| 	// receives metrics. | ||||
| 	DefaultMetricsPath string = "/v1/metrics" | ||||
| 	// DefaultBackoff is a default base backoff time used in the | ||||
| 	// exponential backoff strategy. | ||||
| 	DefaultBackoff time.Duration = 300 * time.Millisecond | ||||
| 	// DefaultTimeout is a default max waiting time for the backend to process | ||||
| 	// each span or metrics batch. | ||||
| 	DefaultTimeout time.Duration = 10 * time.Second | ||||
| @@ -60,6 +67,10 @@ type ( | ||||
| 		// Signal specific configurations | ||||
| 		Metrics SignalConfig | ||||
|  | ||||
| 		// HTTP configurations | ||||
| 		MaxAttempts int | ||||
| 		Backoff     time.Duration | ||||
|  | ||||
| 		// gRPC configurations | ||||
| 		ReconnectionPeriod time.Duration | ||||
| 		ServiceConfig      string | ||||
| @@ -243,3 +254,15 @@ func WithTimeout(duration time.Duration) GenericOption { | ||||
| 		cfg.Metrics.Timeout = duration | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func WithMaxAttempts(maxAttempts int) GenericOption { | ||||
| 	return newGenericOption(func(cfg *Config) { | ||||
| 		cfg.MaxAttempts = maxAttempts | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func WithBackoff(duration time.Duration) GenericOption { | ||||
| 	return newGenericOption(func(cfg *Config) { | ||||
| 		cfg.Backoff = duration | ||||
| 	}) | ||||
| } | ||||
|   | ||||
| @@ -321,6 +321,19 @@ func TestConfigs(t *testing.T) { | ||||
| 				assert.Equal(t, otlpconfig.GzipCompression, c.Metrics.Compression) | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "Test Mixed Environment and With Compression", | ||||
| 			opts: []otlpconfig.GenericOption{ | ||||
| 				otlpconfig.WithCompression(otlpconfig.NoCompression), | ||||
| 			}, | ||||
| 			env: map[string]string{ | ||||
| 				"OTEL_EXPORTER_OTLP_METRICS_COMPRESSION": "gzip", | ||||
| 			}, | ||||
| 			asserts: func(t *testing.T, c *otlpconfig.Config, grpcOption bool) { | ||||
| 				assert.Equal(t, otlpconfig.NoCompression, c.Metrics.Compression) | ||||
| 			}, | ||||
| 		}, | ||||
|  | ||||
| 		// Timeout Tests | ||||
| 		{ | ||||
| 			name: "Test With Timeout", | ||||
|   | ||||
| @@ -73,3 +73,5 @@ replace go.opentelemetry.io/otel/exporters/zipkin => ../../../zipkin | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../../stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../../stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../otlpmetrichttp | ||||
|   | ||||
							
								
								
									
										92
									
								
								exporters/otlp/otlpmetric/otlpmetrichttp/certificate_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								exporters/otlp/otlpmetric/otlpmetrichttp/certificate_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | ||||
| // Copyright The OpenTelemetry 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 otlpmetrichttp_test | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/elliptic" | ||||
| 	cryptorand "crypto/rand" | ||||
| 	"crypto/x509" | ||||
| 	"crypto/x509/pkix" | ||||
| 	"encoding/pem" | ||||
| 	"math/big" | ||||
| 	mathrand "math/rand" | ||||
| 	"net" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| type mathRandReader struct{} | ||||
|  | ||||
| func (mathRandReader) Read(p []byte) (n int, err error) { | ||||
| 	return mathrand.Read(p) | ||||
| } | ||||
|  | ||||
| var randReader mathRandReader | ||||
|  | ||||
| type pemCertificate struct { | ||||
| 	Certificate []byte | ||||
| 	PrivateKey  []byte | ||||
| } | ||||
|  | ||||
| // Based on https://golang.org/src/crypto/tls/generate_cert.go, | ||||
| // simplified and weakened. | ||||
| func generateWeakCertificate() (*pemCertificate, error) { | ||||
| 	priv, err := ecdsa.GenerateKey(elliptic.P256(), randReader) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	keyUsage := x509.KeyUsageDigitalSignature | ||||
| 	notBefore := time.Now() | ||||
| 	notAfter := notBefore.Add(time.Hour) | ||||
| 	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) | ||||
| 	serialNumber, err := cryptorand.Int(randReader, serialNumberLimit) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	template := x509.Certificate{ | ||||
| 		SerialNumber: serialNumber, | ||||
| 		Subject: pkix.Name{ | ||||
| 			Organization: []string{"otel-go"}, | ||||
| 		}, | ||||
| 		NotBefore:             notBefore, | ||||
| 		NotAfter:              notAfter, | ||||
| 		KeyUsage:              keyUsage, | ||||
| 		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, | ||||
| 		BasicConstraintsValid: true, | ||||
| 		DNSNames:              []string{"localhost"}, | ||||
| 		IPAddresses:           []net.IP{net.IPv6loopback, net.IPv4(127, 0, 0, 1)}, | ||||
| 	} | ||||
| 	derBytes, err := x509.CreateCertificate(randReader, &template, &template, &priv.PublicKey, priv) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	certificateBuffer := new(bytes.Buffer) | ||||
| 	if err := pem.Encode(certificateBuffer, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	privDERBytes, err := x509.MarshalPKCS8PrivateKey(priv) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	privBuffer := new(bytes.Buffer) | ||||
| 	if err := pem.Encode(privBuffer, &pem.Block{Type: "PRIVATE KEY", Bytes: privDERBytes}); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &pemCertificate{ | ||||
| 		Certificate: certificateBuffer.Bytes(), | ||||
| 		PrivateKey:  privBuffer.Bytes(), | ||||
| 	}, nil | ||||
| } | ||||
							
								
								
									
										278
									
								
								exporters/otlp/otlpmetric/otlpmetrichttp/client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								exporters/otlp/otlpmetric/otlpmetrichttp/client.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,278 @@ | ||||
| // Copyright The OpenTelemetry 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 otlpmetrichttp | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"compress/gzip" | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"math/rand" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"path" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric" | ||||
| 	metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" | ||||
|  | ||||
| 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpconfig" | ||||
|  | ||||
| 	"google.golang.org/protobuf/proto" | ||||
|  | ||||
| 	"go.opentelemetry.io/otel" | ||||
| 	colmetricpb "go.opentelemetry.io/proto/otlp/collector/metrics/v1" | ||||
| ) | ||||
|  | ||||
| const contentTypeProto = "application/x-protobuf" | ||||
|  | ||||
| // Keep it in sync with golang's DefaultTransport from net/http! We | ||||
| // have our own copy to avoid handling a situation where the | ||||
| // DefaultTransport is overwritten with some different implementation | ||||
| // of http.RoundTripper or it's modified by other package. | ||||
| var ourTransport = &http.Transport{ | ||||
| 	Proxy: http.ProxyFromEnvironment, | ||||
| 	DialContext: (&net.Dialer{ | ||||
| 		Timeout:   30 * time.Second, | ||||
| 		KeepAlive: 30 * time.Second, | ||||
| 	}).DialContext, | ||||
| 	ForceAttemptHTTP2:     true, | ||||
| 	MaxIdleConns:          100, | ||||
| 	IdleConnTimeout:       90 * time.Second, | ||||
| 	TLSHandshakeTimeout:   10 * time.Second, | ||||
| 	ExpectContinueTimeout: 1 * time.Second, | ||||
| } | ||||
|  | ||||
| type client struct { | ||||
| 	name       string | ||||
| 	cfg        otlpconfig.SignalConfig | ||||
| 	generalCfg otlpconfig.Config | ||||
| 	client     *http.Client | ||||
| 	stopCh     chan struct{} | ||||
| } | ||||
|  | ||||
| // NewClient creates a new HTTP metric client. | ||||
| func NewClient(opts ...Option) otlpmetric.Client { | ||||
| 	cfg := otlpconfig.NewDefaultConfig() | ||||
| 	otlpconfig.ApplyHTTPEnvConfigs(&cfg) | ||||
| 	for _, opt := range opts { | ||||
| 		opt.applyHTTPOption(&cfg) | ||||
| 	} | ||||
|  | ||||
| 	for pathPtr, defaultPath := range map[*string]string{ | ||||
| 		&cfg.Metrics.URLPath: defaultMetricsPath, | ||||
| 	} { | ||||
| 		tmp := strings.TrimSpace(*pathPtr) | ||||
| 		if tmp == "" { | ||||
| 			tmp = defaultPath | ||||
| 		} else { | ||||
| 			tmp = path.Clean(tmp) | ||||
| 			if !path.IsAbs(tmp) { | ||||
| 				tmp = fmt.Sprintf("/%s", tmp) | ||||
| 			} | ||||
| 		} | ||||
| 		*pathPtr = tmp | ||||
| 	} | ||||
| 	if cfg.MaxAttempts <= 0 { | ||||
| 		cfg.MaxAttempts = defaultMaxAttempts | ||||
| 	} | ||||
| 	if cfg.MaxAttempts > defaultMaxAttempts { | ||||
| 		cfg.MaxAttempts = defaultMaxAttempts | ||||
| 	} | ||||
| 	if cfg.Backoff <= 0 { | ||||
| 		cfg.Backoff = defaultBackoff | ||||
| 	} | ||||
|  | ||||
| 	httpClient := &http.Client{ | ||||
| 		Transport: ourTransport, | ||||
| 		Timeout:   cfg.Metrics.Timeout, | ||||
| 	} | ||||
| 	if cfg.Metrics.TLSCfg != nil { | ||||
| 		transport := ourTransport.Clone() | ||||
| 		transport.TLSClientConfig = cfg.Metrics.TLSCfg | ||||
| 		httpClient.Transport = transport | ||||
| 	} | ||||
|  | ||||
| 	stopCh := make(chan struct{}) | ||||
| 	return &client{ | ||||
| 		name:       "metrics", | ||||
| 		cfg:        cfg.Metrics, | ||||
| 		generalCfg: cfg, | ||||
| 		stopCh:     stopCh, | ||||
| 		client:     httpClient, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Start does nothing in a HTTP client | ||||
| func (d *client) Start(ctx context.Context) error { | ||||
| 	// nothing to do | ||||
| 	select { | ||||
| 	case <-ctx.Done(): | ||||
| 		return ctx.Err() | ||||
| 	default: | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Stop shuts down the client and interrupt any in-flight request. | ||||
| func (d *client) Stop(ctx context.Context) error { | ||||
| 	close(d.stopCh) | ||||
| 	select { | ||||
| 	case <-ctx.Done(): | ||||
| 		return ctx.Err() | ||||
| 	default: | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // UploadMetrics sends a batch of metrics to the collector. | ||||
| func (d *client) UploadMetrics(ctx context.Context, protoMetrics []*metricpb.ResourceMetrics) error { | ||||
| 	pbRequest := &colmetricpb.ExportMetricsServiceRequest{ | ||||
| 		ResourceMetrics: protoMetrics, | ||||
| 	} | ||||
| 	rawRequest, err := proto.Marshal(pbRequest) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return d.send(ctx, rawRequest) | ||||
| } | ||||
|  | ||||
| func (d *client) send(ctx context.Context, rawRequest []byte) error { | ||||
| 	address := fmt.Sprintf("%s://%s%s", d.getScheme(), d.cfg.Endpoint, d.cfg.URLPath) | ||||
| 	var cancel context.CancelFunc | ||||
| 	ctx, cancel = d.contextWithStop(ctx) | ||||
| 	defer cancel() | ||||
| 	for i := 0; i < d.generalCfg.MaxAttempts; i++ { | ||||
| 		response, err := d.singleSend(ctx, rawRequest, address) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		// We don't care about the body, so try to read it | ||||
| 		// into /dev/null and close it immediately. The | ||||
| 		// reading part is to facilitate connection reuse. | ||||
| 		_, _ = io.Copy(ioutil.Discard, response.Body) | ||||
| 		_ = response.Body.Close() | ||||
| 		switch response.StatusCode { | ||||
| 		case http.StatusOK: | ||||
| 			return nil | ||||
| 		case http.StatusTooManyRequests: | ||||
| 			fallthrough | ||||
| 		case http.StatusServiceUnavailable: | ||||
| 			select { | ||||
| 			case <-time.After(getWaitDuration(d.generalCfg.Backoff, i)): | ||||
| 				continue | ||||
| 			case <-ctx.Done(): | ||||
| 				return ctx.Err() | ||||
| 			} | ||||
| 		default: | ||||
| 			return fmt.Errorf("failed to send %s to %s with HTTP status %s", d.name, address, response.Status) | ||||
| 		} | ||||
| 	} | ||||
| 	return fmt.Errorf("failed to send data to %s after %d tries", address, d.generalCfg.MaxAttempts) | ||||
| } | ||||
|  | ||||
| func (d *client) getScheme() string { | ||||
| 	if d.cfg.Insecure { | ||||
| 		return "http" | ||||
| 	} | ||||
| 	return "https" | ||||
| } | ||||
|  | ||||
| func getWaitDuration(backoff time.Duration, i int) time.Duration { | ||||
| 	// Strategy: after nth failed attempt, attempt resending after | ||||
| 	// k * initialBackoff + jitter, where k is a random number in | ||||
| 	// range [0, 2^n-1), and jitter is a random percentage of | ||||
| 	// initialBackoff from [-5%, 5%). | ||||
| 	// | ||||
| 	// Based on | ||||
| 	// https://en.wikipedia.org/wiki/Exponential_backoff#Example_exponential_backoff_algorithm | ||||
| 	// | ||||
| 	// Jitter is our addition. | ||||
|  | ||||
| 	// There won't be an overflow, since i is capped to | ||||
| 	// defaultMaxAttempts (5). | ||||
| 	upperK := (int64)(1) << (i + 1) | ||||
| 	jitterPercent := (rand.Float64() - 0.5) / 10. | ||||
| 	jitter := jitterPercent * (float64)(backoff) | ||||
| 	k := rand.Int63n(upperK) | ||||
| 	return (time.Duration)(k)*backoff + (time.Duration)(jitter) | ||||
| } | ||||
|  | ||||
| func (d *client) contextWithStop(ctx context.Context) (context.Context, context.CancelFunc) { | ||||
| 	// Unify the parent context Done signal with the client's stop | ||||
| 	// channel. | ||||
| 	ctx, cancel := context.WithCancel(ctx) | ||||
| 	go func(ctx context.Context, cancel context.CancelFunc) { | ||||
| 		select { | ||||
| 		case <-ctx.Done(): | ||||
| 			// Nothing to do, either cancelled or deadline | ||||
| 			// happened. | ||||
| 		case <-d.stopCh: | ||||
| 			cancel() | ||||
| 		} | ||||
| 	}(ctx, cancel) | ||||
| 	return ctx, cancel | ||||
| } | ||||
|  | ||||
| func (d *client) singleSend(ctx context.Context, rawRequest []byte, address string) (*http.Response, error) { | ||||
| 	request, err := http.NewRequestWithContext(ctx, http.MethodPost, address, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	bodyReader, contentLength, headers := d.prepareBody(rawRequest) | ||||
| 	// Not closing bodyReader through defer, the HTTP Client's | ||||
| 	// Transport will do it for us | ||||
| 	request.Body = bodyReader | ||||
| 	request.ContentLength = contentLength | ||||
| 	for key, values := range headers { | ||||
| 		for _, value := range values { | ||||
| 			request.Header.Add(key, value) | ||||
| 		} | ||||
| 	} | ||||
| 	return d.client.Do(request) | ||||
| } | ||||
|  | ||||
| func (d *client) prepareBody(rawRequest []byte) (io.ReadCloser, int64, http.Header) { | ||||
| 	var bodyReader io.ReadCloser | ||||
| 	headers := http.Header{} | ||||
| 	for k, v := range d.cfg.Headers { | ||||
| 		headers.Set(k, v) | ||||
| 	} | ||||
| 	contentLength := (int64)(len(rawRequest)) | ||||
| 	headers.Set("Content-Type", contentTypeProto) | ||||
| 	requestReader := bytes.NewBuffer(rawRequest) | ||||
| 	switch Compression(d.cfg.Compression) { | ||||
| 	case NoCompression: | ||||
| 		bodyReader = ioutil.NopCloser(requestReader) | ||||
| 	case GzipCompression: | ||||
| 		preader, pwriter := io.Pipe() | ||||
| 		go func() { | ||||
| 			defer pwriter.Close() | ||||
| 			gzipper := gzip.NewWriter(pwriter) | ||||
| 			defer gzipper.Close() | ||||
| 			_, err := io.Copy(gzipper, requestReader) | ||||
| 			if err != nil { | ||||
| 				otel.Handle(fmt.Errorf("otlphttp: failed to gzip request: %v", err)) | ||||
| 			} | ||||
| 		}() | ||||
| 		headers.Set("Content-Encoding", "gzip") | ||||
| 		bodyReader = preader | ||||
| 		contentLength = -1 | ||||
| 	} | ||||
| 	return bodyReader, contentLength, headers | ||||
| } | ||||
							
								
								
									
										430
									
								
								exporters/otlp/otlpmetric/otlpmetrichttp/client_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										430
									
								
								exporters/otlp/otlpmetric/otlpmetrichttp/client_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,430 @@ | ||||
| // Copyright The OpenTelemetry 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 otlpmetrichttp_test | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/stretchr/testify/require" | ||||
|  | ||||
| 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric" | ||||
| 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpmetrictest" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	relOtherMetricsPath = "post/metrics/here" | ||||
| 	otherMetricsPath    = "/post/metrics/here" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	oneRecord = otlpmetrictest.OneRecordCheckpointSet{} | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	testHeaders = map[string]string{ | ||||
| 		"Otel-Go-Key-1": "somevalue", | ||||
| 		"Otel-Go-Key-2": "someothervalue", | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| func TestEndToEnd(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		name  string | ||||
| 		opts  []otlpmetrichttp.Option | ||||
| 		mcCfg mockCollectorConfig | ||||
| 		tls   bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name: "no extra options", | ||||
| 			opts: nil, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "with gzip compression", | ||||
| 			opts: []otlpmetrichttp.Option{ | ||||
| 				otlpmetrichttp.WithCompression(otlpmetrichttp.GzipCompression), | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "with empty paths (forced to defaults)", | ||||
| 			opts: []otlpmetrichttp.Option{ | ||||
| 				otlpmetrichttp.WithURLPath(""), | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "with relative paths", | ||||
| 			opts: []otlpmetrichttp.Option{ | ||||
| 				otlpmetrichttp.WithURLPath(relOtherMetricsPath), | ||||
| 			}, | ||||
| 			mcCfg: mockCollectorConfig{ | ||||
| 				MetricsURLPath: otherMetricsPath, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "with TLS", | ||||
| 			opts: nil, | ||||
| 			mcCfg: mockCollectorConfig{ | ||||
| 				WithTLS: true, | ||||
| 			}, | ||||
| 			tls: true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "with extra headers", | ||||
| 			opts: []otlpmetrichttp.Option{ | ||||
| 				otlpmetrichttp.WithHeaders(testHeaders), | ||||
| 			}, | ||||
| 			mcCfg: mockCollectorConfig{ | ||||
| 				ExpectedHeaders: testHeaders, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, tc := range tests { | ||||
| 		t.Run(tc.name, func(t *testing.T) { | ||||
| 			mc := runMockCollector(t, tc.mcCfg) | ||||
| 			defer mc.MustStop(t) | ||||
| 			allOpts := []otlpmetrichttp.Option{ | ||||
| 				otlpmetrichttp.WithEndpoint(mc.Endpoint()), | ||||
| 			} | ||||
| 			if tc.tls { | ||||
| 				tlsConfig := mc.ClientTLSConfig() | ||||
| 				require.NotNil(t, tlsConfig) | ||||
| 				allOpts = append(allOpts, otlpmetrichttp.WithTLSClientConfig(tlsConfig)) | ||||
| 			} else { | ||||
| 				allOpts = append(allOpts, otlpmetrichttp.WithInsecure()) | ||||
| 			} | ||||
| 			allOpts = append(allOpts, tc.opts...) | ||||
| 			client := otlpmetrichttp.NewClient(allOpts...) | ||||
| 			ctx := context.Background() | ||||
| 			exporter, err := otlpmetric.New(ctx, client) | ||||
| 			if assert.NoError(t, err) { | ||||
| 				defer func() { | ||||
| 					assert.NoError(t, exporter.Shutdown(ctx)) | ||||
| 				}() | ||||
| 				otlpmetrictest.RunEndToEndTest(ctx, t, exporter, mc) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestExporterShutdown(t *testing.T) { | ||||
| 	mc := runMockCollector(t, mockCollectorConfig{}) | ||||
| 	defer func() { | ||||
| 		_ = mc.Stop() | ||||
| 	}() | ||||
|  | ||||
| 	<-time.After(5 * time.Millisecond) | ||||
|  | ||||
| 	otlpmetrictest.RunExporterShutdownTest(t, func() otlpmetric.Client { | ||||
| 		return otlpmetrichttp.NewClient( | ||||
| 			otlpmetrichttp.WithInsecure(), | ||||
| 			otlpmetrichttp.WithEndpoint(mc.endpoint), | ||||
| 		) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func TestRetry(t *testing.T) { | ||||
| 	statuses := []int{ | ||||
| 		http.StatusTooManyRequests, | ||||
| 		http.StatusServiceUnavailable, | ||||
| 	} | ||||
| 	mcCfg := mockCollectorConfig{ | ||||
| 		InjectHTTPStatus: statuses, | ||||
| 	} | ||||
| 	mc := runMockCollector(t, mcCfg) | ||||
| 	defer mc.MustStop(t) | ||||
| 	client := otlpmetrichttp.NewClient( | ||||
| 		otlpmetrichttp.WithEndpoint(mc.Endpoint()), | ||||
| 		otlpmetrichttp.WithInsecure(), | ||||
| 		otlpmetrichttp.WithMaxAttempts(len(statuses)+1), | ||||
| 	) | ||||
| 	ctx := context.Background() | ||||
| 	exporter, err := otlpmetric.New(ctx, client) | ||||
| 	require.NoError(t, err) | ||||
| 	defer func() { | ||||
| 		assert.NoError(t, exporter.Shutdown(ctx)) | ||||
| 	}() | ||||
| 	err = exporter.Export(ctx, oneRecord) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Len(t, mc.GetMetrics(), 1) | ||||
| } | ||||
|  | ||||
| func TestTimeout(t *testing.T) { | ||||
| 	mcCfg := mockCollectorConfig{ | ||||
| 		InjectDelay: 100 * time.Millisecond, | ||||
| 	} | ||||
| 	mc := runMockCollector(t, mcCfg) | ||||
| 	defer mc.MustStop(t) | ||||
| 	client := otlpmetrichttp.NewClient( | ||||
| 		otlpmetrichttp.WithEndpoint(mc.Endpoint()), | ||||
| 		otlpmetrichttp.WithInsecure(), | ||||
| 		otlpmetrichttp.WithTimeout(50*time.Millisecond), | ||||
| 	) | ||||
| 	ctx := context.Background() | ||||
| 	exporter, err := otlpmetric.New(ctx, client) | ||||
| 	require.NoError(t, err) | ||||
| 	defer func() { | ||||
| 		assert.NoError(t, exporter.Shutdown(ctx)) | ||||
| 	}() | ||||
| 	err = exporter.Export(ctx, oneRecord) | ||||
| 	assert.Equal(t, true, os.IsTimeout(err)) | ||||
| } | ||||
|  | ||||
| func TestRetryFailed(t *testing.T) { | ||||
| 	statuses := []int{ | ||||
| 		http.StatusTooManyRequests, | ||||
| 		http.StatusServiceUnavailable, | ||||
| 	} | ||||
| 	mcCfg := mockCollectorConfig{ | ||||
| 		InjectHTTPStatus: statuses, | ||||
| 	} | ||||
| 	mc := runMockCollector(t, mcCfg) | ||||
| 	defer mc.MustStop(t) | ||||
| 	driver := otlpmetrichttp.NewClient( | ||||
| 		otlpmetrichttp.WithEndpoint(mc.Endpoint()), | ||||
| 		otlpmetrichttp.WithInsecure(), | ||||
| 		otlpmetrichttp.WithMaxAttempts(1), | ||||
| 	) | ||||
| 	ctx := context.Background() | ||||
| 	exporter, err := otlpmetric.New(ctx, driver) | ||||
| 	require.NoError(t, err) | ||||
| 	defer func() { | ||||
| 		assert.NoError(t, exporter.Shutdown(ctx)) | ||||
| 	}() | ||||
| 	err = exporter.Export(ctx, oneRecord) | ||||
| 	assert.Error(t, err) | ||||
| 	assert.Empty(t, mc.GetMetrics()) | ||||
| } | ||||
|  | ||||
| func TestNoRetry(t *testing.T) { | ||||
| 	statuses := []int{ | ||||
| 		http.StatusBadRequest, | ||||
| 	} | ||||
| 	mcCfg := mockCollectorConfig{ | ||||
| 		InjectHTTPStatus: statuses, | ||||
| 	} | ||||
| 	mc := runMockCollector(t, mcCfg) | ||||
| 	defer mc.MustStop(t) | ||||
| 	driver := otlpmetrichttp.NewClient( | ||||
| 		otlpmetrichttp.WithEndpoint(mc.Endpoint()), | ||||
| 		otlpmetrichttp.WithInsecure(), | ||||
| 		otlpmetrichttp.WithMaxAttempts(len(statuses)+1), | ||||
| 	) | ||||
| 	ctx := context.Background() | ||||
| 	exporter, err := otlpmetric.New(ctx, driver) | ||||
| 	require.NoError(t, err) | ||||
| 	defer func() { | ||||
| 		assert.NoError(t, exporter.Shutdown(ctx)) | ||||
| 	}() | ||||
| 	err = exporter.Export(ctx, oneRecord) | ||||
| 	assert.Error(t, err) | ||||
| 	assert.Equal(t, fmt.Sprintf("failed to send metrics to http://%s/v1/metrics with HTTP status 400 Bad Request", mc.endpoint), err.Error()) | ||||
| 	assert.Empty(t, mc.GetMetrics()) | ||||
| } | ||||
|  | ||||
| func TestEmptyData(t *testing.T) { | ||||
| 	mcCfg := mockCollectorConfig{} | ||||
| 	mc := runMockCollector(t, mcCfg) | ||||
| 	defer mc.MustStop(t) | ||||
| 	driver := otlpmetrichttp.NewClient( | ||||
| 		otlpmetrichttp.WithEndpoint(mc.Endpoint()), | ||||
| 		otlpmetrichttp.WithInsecure(), | ||||
| 	) | ||||
| 	ctx := context.Background() | ||||
| 	exporter, err := otlpmetric.New(ctx, driver) | ||||
| 	require.NoError(t, err) | ||||
| 	defer func() { | ||||
| 		assert.NoError(t, exporter.Shutdown(ctx)) | ||||
| 	}() | ||||
| 	assert.NoError(t, err) | ||||
| 	err = exporter.Export(ctx, oneRecord) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.NotEmpty(t, mc.GetMetrics()) | ||||
| } | ||||
|  | ||||
| func TestUnreasonableMaxAttempts(t *testing.T) { | ||||
| 	// Max attempts is 5, we set collector to fail 7 times and try | ||||
| 	// to configure max attempts to be either negative or too | ||||
| 	// large. Since we set max attempts to 5 in such cases, | ||||
| 	// exporting to the collector should fail. | ||||
| 	type testcase struct { | ||||
| 		name        string | ||||
| 		maxAttempts int | ||||
| 	} | ||||
| 	for _, tc := range []testcase{ | ||||
| 		{ | ||||
| 			name:        "negative max attempts", | ||||
| 			maxAttempts: -3, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:        "too large max attempts", | ||||
| 			maxAttempts: 10, | ||||
| 		}, | ||||
| 	} { | ||||
| 		t.Run(tc.name, func(t *testing.T) { | ||||
| 			statuses := make([]int, 0, 7) | ||||
| 			for i := 0; i < cap(statuses); i++ { | ||||
| 				statuses = append(statuses, http.StatusTooManyRequests) | ||||
| 			} | ||||
| 			mcCfg := mockCollectorConfig{ | ||||
| 				InjectHTTPStatus: statuses, | ||||
| 			} | ||||
| 			mc := runMockCollector(t, mcCfg) | ||||
| 			defer mc.MustStop(t) | ||||
| 			driver := otlpmetrichttp.NewClient( | ||||
| 				otlpmetrichttp.WithEndpoint(mc.Endpoint()), | ||||
| 				otlpmetrichttp.WithInsecure(), | ||||
| 				otlpmetrichttp.WithMaxAttempts(tc.maxAttempts), | ||||
| 				otlpmetrichttp.WithBackoff(time.Millisecond), | ||||
| 			) | ||||
| 			ctx := context.Background() | ||||
| 			exporter, err := otlpmetric.New(ctx, driver) | ||||
| 			require.NoError(t, err) | ||||
| 			defer func() { | ||||
| 				assert.NoError(t, exporter.Shutdown(ctx)) | ||||
| 			}() | ||||
| 			err = exporter.Export(ctx, oneRecord) | ||||
| 			assert.Error(t, err) | ||||
| 			assert.Empty(t, mc.GetMetrics()) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestUnreasonableBackoff(t *testing.T) { | ||||
| 	// This sets backoff to negative value, which gets corrected | ||||
| 	// to default backoff instead of being used. Default max | ||||
| 	// attempts is 5, so we set the collector to fail 4 times, but | ||||
| 	// we set the deadline to 3 times of the default backoff, so | ||||
| 	// this should show that deadline is not met, meaning that the | ||||
| 	// retries weren't immediate (as negative backoff could | ||||
| 	// imply). | ||||
| 	statuses := make([]int, 0, 4) | ||||
| 	for i := 0; i < cap(statuses); i++ { | ||||
| 		statuses = append(statuses, http.StatusTooManyRequests) | ||||
| 	} | ||||
| 	mcCfg := mockCollectorConfig{ | ||||
| 		InjectHTTPStatus: statuses, | ||||
| 	} | ||||
| 	mc := runMockCollector(t, mcCfg) | ||||
| 	defer mc.MustStop(t) | ||||
| 	driver := otlpmetrichttp.NewClient( | ||||
| 		otlpmetrichttp.WithEndpoint(mc.Endpoint()), | ||||
| 		otlpmetrichttp.WithInsecure(), | ||||
| 		otlpmetrichttp.WithBackoff(-time.Millisecond), | ||||
| 	) | ||||
| 	ctx, cancel := context.WithTimeout(context.Background(), 3*(300*time.Millisecond)) | ||||
| 	defer cancel() | ||||
| 	exporter, err := otlpmetric.New(ctx, driver) | ||||
| 	require.NoError(t, err) | ||||
| 	defer func() { | ||||
| 		assert.NoError(t, exporter.Shutdown(context.Background())) | ||||
| 	}() | ||||
| 	err = exporter.Export(ctx, oneRecord) | ||||
| 	assert.Error(t, err) | ||||
| 	assert.Empty(t, mc.GetMetrics()) | ||||
| } | ||||
|  | ||||
| func TestCancelledContext(t *testing.T) { | ||||
| 	statuses := []int{ | ||||
| 		http.StatusBadRequest, | ||||
| 	} | ||||
| 	mcCfg := mockCollectorConfig{ | ||||
| 		InjectHTTPStatus: statuses, | ||||
| 	} | ||||
| 	mc := runMockCollector(t, mcCfg) | ||||
| 	defer mc.MustStop(t) | ||||
| 	driver := otlpmetrichttp.NewClient( | ||||
| 		otlpmetrichttp.WithEndpoint(mc.Endpoint()), | ||||
| 		otlpmetrichttp.WithInsecure(), | ||||
| 	) | ||||
| 	ctx, cancel := context.WithCancel(context.Background()) | ||||
| 	exporter, err := otlpmetric.New(ctx, driver) | ||||
| 	require.NoError(t, err) | ||||
| 	defer func() { | ||||
| 		assert.NoError(t, exporter.Shutdown(context.Background())) | ||||
| 	}() | ||||
| 	cancel() | ||||
| 	_ = exporter.Export(ctx, oneRecord) | ||||
| 	assert.Empty(t, mc.GetMetrics()) | ||||
| } | ||||
|  | ||||
| func TestDeadlineContext(t *testing.T) { | ||||
| 	statuses := make([]int, 0, 5) | ||||
| 	for i := 0; i < cap(statuses); i++ { | ||||
| 		statuses = append(statuses, http.StatusTooManyRequests) | ||||
| 	} | ||||
| 	mcCfg := mockCollectorConfig{ | ||||
| 		InjectHTTPStatus: statuses, | ||||
| 	} | ||||
| 	mc := runMockCollector(t, mcCfg) | ||||
| 	defer mc.MustStop(t) | ||||
| 	driver := otlpmetrichttp.NewClient( | ||||
| 		otlpmetrichttp.WithEndpoint(mc.Endpoint()), | ||||
| 		otlpmetrichttp.WithInsecure(), | ||||
| 		otlpmetrichttp.WithBackoff(time.Minute), | ||||
| 	) | ||||
| 	ctx := context.Background() | ||||
| 	exporter, err := otlpmetric.New(ctx, driver) | ||||
| 	require.NoError(t, err) | ||||
| 	defer func() { | ||||
| 		assert.NoError(t, exporter.Shutdown(context.Background())) | ||||
| 	}() | ||||
| 	ctx, cancel := context.WithTimeout(ctx, time.Second) | ||||
| 	defer cancel() | ||||
| 	err = exporter.Export(ctx, oneRecord) | ||||
| 	assert.Error(t, err) | ||||
| 	assert.Empty(t, mc.GetMetrics()) | ||||
| } | ||||
|  | ||||
| func TestStopWhileExporting(t *testing.T) { | ||||
| 	statuses := make([]int, 0, 5) | ||||
| 	for i := 0; i < cap(statuses); i++ { | ||||
| 		statuses = append(statuses, http.StatusTooManyRequests) | ||||
| 	} | ||||
| 	mcCfg := mockCollectorConfig{ | ||||
| 		InjectHTTPStatus: statuses, | ||||
| 	} | ||||
| 	mc := runMockCollector(t, mcCfg) | ||||
| 	defer mc.MustStop(t) | ||||
| 	driver := otlpmetrichttp.NewClient( | ||||
| 		otlpmetrichttp.WithEndpoint(mc.Endpoint()), | ||||
| 		otlpmetrichttp.WithInsecure(), | ||||
| 		otlpmetrichttp.WithBackoff(time.Minute), | ||||
| 	) | ||||
| 	ctx := context.Background() | ||||
| 	exporter, err := otlpmetric.New(ctx, driver) | ||||
| 	require.NoError(t, err) | ||||
| 	defer func() { | ||||
| 		assert.NoError(t, exporter.Shutdown(ctx)) | ||||
| 	}() | ||||
| 	doneCh := make(chan struct{}) | ||||
| 	go func() { | ||||
| 		err := exporter.Export(ctx, oneRecord) | ||||
| 		assert.Error(t, err) | ||||
| 		assert.Empty(t, mc.GetMetrics()) | ||||
| 		close(doneCh) | ||||
| 	}() | ||||
| 	<-time.After(time.Second) | ||||
| 	err = exporter.Shutdown(ctx) | ||||
| 	assert.NoError(t, err) | ||||
| 	<-doneCh | ||||
| } | ||||
							
								
								
									
										23
									
								
								exporters/otlp/otlpmetric/otlpmetrichttp/doc.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								exporters/otlp/otlpmetric/otlpmetrichttp/doc.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| // Copyright The OpenTelemetry 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 otlpmetrichttp provides a client that sends metrics to the collector | ||||
| using HTTP with binary protobuf payloads. | ||||
|  | ||||
| This package is currently in a pre-GA phase. Backwards incompatible changes | ||||
| may be introduced in subsequent minor version releases as we work to track the | ||||
| evolving OpenTelemetry specification and user feedback. | ||||
| */ | ||||
| package otlpmetrichttp // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" | ||||
							
								
								
									
										31
									
								
								exporters/otlp/otlpmetric/otlpmetrichttp/exporter.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								exporters/otlp/otlpmetric/otlpmetrichttp/exporter.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| // Copyright The OpenTelemetry 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 otlpmetrichttp // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
|  | ||||
| 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric" | ||||
| ) | ||||
|  | ||||
| // New constructs a new Exporter and starts it. | ||||
| func New(ctx context.Context, opts ...Option) (*otlpmetric.Exporter, error) { | ||||
| 	return otlpmetric.New(ctx, NewClient(opts...)) | ||||
| } | ||||
|  | ||||
| // NewUnstarted constructs a new Exporter and does not start it. | ||||
| func NewUnstarted(opts ...Option) *otlpmetric.Exporter { | ||||
| 	return otlpmetric.NewUnstarted(NewClient(opts...)) | ||||
| } | ||||
							
								
								
									
										79
									
								
								exporters/otlp/otlpmetric/otlpmetrichttp/go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								exporters/otlp/otlpmetric/otlpmetrichttp/go.mod
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| module go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp | ||||
|  | ||||
| go 1.15 | ||||
|  | ||||
| require ( | ||||
| 	github.com/stretchr/testify v1.7.0 | ||||
| 	go.opentelemetry.io/otel v1.0.0-RC1 | ||||
| 	go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.21.0 | ||||
| 	go.opentelemetry.io/proto/otlp v0.9.0 | ||||
| 	google.golang.org/protobuf v1.26.0 | ||||
| ) | ||||
|  | ||||
| replace go.opentelemetry.io/otel => ../../../.. | ||||
|  | ||||
| replace go.opentelemetry.io/otel/sdk => ../../../../sdk | ||||
|  | ||||
| replace go.opentelemetry.io/otel/sdk/metric => ../../../../sdk/metric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp => ../.. | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../ | ||||
|  | ||||
| replace go.opentelemetry.io/otel/metric => ../../../../metric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/oteltest => ../../../../oteltest | ||||
|  | ||||
| replace go.opentelemetry.io/otel/trace => ../../../../trace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/bridge/opencensus => ../../../../bridge/opencensus | ||||
|  | ||||
| replace go.opentelemetry.io/otel/bridge/opentracing => ../../../../bridge/opentracing | ||||
|  | ||||
| replace go.opentelemetry.io/otel/example/jaeger => ../../../../example/jaeger | ||||
|  | ||||
| replace go.opentelemetry.io/otel/example/namedtracer => ../../../../example/namedtracer | ||||
|  | ||||
| replace go.opentelemetry.io/otel/example/opencensus => ../../../../example/opencensus | ||||
|  | ||||
| replace go.opentelemetry.io/otel/example/otel-collector => ../../../../example/otel-collector | ||||
|  | ||||
| replace go.opentelemetry.io/otel/example/passthrough => ../../../../example/passthrough | ||||
|  | ||||
| replace go.opentelemetry.io/otel/example/prom-collector => ../../../../example/prom-collector | ||||
|  | ||||
| replace go.opentelemetry.io/otel/example/prometheus => ../../../../example/prometheus | ||||
|  | ||||
| replace go.opentelemetry.io/otel/example/zipkin => ../../../../example/zipkin | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../../../metric/prometheus | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ./ | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../otlptrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../otlptrace/otlptracegrpc | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../otlptrace/otlptracehttp | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/trace/jaeger => ../../../trace/jaeger | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/trace/zipkin => ../../../trace/zipkin | ||||
|  | ||||
| replace go.opentelemetry.io/otel/internal/tools => ../../../../internal/tools | ||||
|  | ||||
| replace go.opentelemetry.io/otel/sdk/export/metric => ../../../../sdk/export/metric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/internal/metric => ../../../../internal/metric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/jaeger => ../../../jaeger | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/prometheus => ../../../prometheus | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/zipkin => ../../../zipkin | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../../stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../../stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../otlpmetricgrpc | ||||
							
								
								
									
										124
									
								
								exporters/otlp/otlpmetric/otlpmetrichttp/go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								exporters/otlp/otlpmetric/otlpmetrichttp/go.sum
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | ||||
| cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||
| cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||
| github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= | ||||
| github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= | ||||
| github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= | ||||
| github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= | ||||
| github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= | ||||
| github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= | ||||
| github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= | ||||
| github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= | ||||
| github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= | ||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= | ||||
| github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= | ||||
| github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= | ||||
| github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= | ||||
| github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= | ||||
| github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= | ||||
| github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= | ||||
| github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= | ||||
| github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= | ||||
| github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= | ||||
| github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= | ||||
| github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= | ||||
| github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= | ||||
| github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= | ||||
| github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= | ||||
| github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= | ||||
| github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= | ||||
| github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= | ||||
| github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= | ||||
| github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= | ||||
| github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= | ||||
| github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||
| github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= | ||||
| github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= | ||||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
| github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= | ||||
| github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= | ||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||
| github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= | ||||
| github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= | ||||
| github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||
| go.opentelemetry.io/proto/otlp v0.9.0 h1:C0g6TWmQYvjKRnljRULLWUVJGy8Uvu0NEL/5frY2/t4= | ||||
| go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= | ||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||
| golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||
| golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||
| 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/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-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= | ||||
| golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | ||||
| golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | ||||
| golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= | ||||
| golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= | ||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
| 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/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= | ||||
| golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= | ||||
| google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | ||||
| google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= | ||||
| google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= | ||||
| google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | ||||
| google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= | ||||
| google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= | ||||
| google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= | ||||
| google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= | ||||
| google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= | ||||
| google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= | ||||
| google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= | ||||
| google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= | ||||
| google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= | ||||
| google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= | ||||
| google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= | ||||
| google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= | ||||
| google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= | ||||
| google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= | ||||
| google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= | ||||
| google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||
| google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||
| google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||
| google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= | ||||
| google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= | ||||
| google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= | ||||
| google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= | ||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
| honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
							
								
								
									
										238
									
								
								exporters/otlp/otlpmetric/otlpmetrichttp/mock_collector_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										238
									
								
								exporters/otlp/otlpmetric/otlpmetrichttp/mock_collector_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,238 @@ | ||||
| // Copyright The OpenTelemetry 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 otlpmetrichttp_test | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"compress/gzip" | ||||
| 	"context" | ||||
| 	"crypto/tls" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"sync" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpconfig" | ||||
| 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpmetrictest" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/stretchr/testify/require" | ||||
| 	"google.golang.org/protobuf/proto" | ||||
|  | ||||
| 	collectormetricpb "go.opentelemetry.io/proto/otlp/collector/metrics/v1" | ||||
| 	metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" | ||||
| ) | ||||
|  | ||||
| type mockCollector struct { | ||||
| 	endpoint string | ||||
| 	server   *http.Server | ||||
|  | ||||
| 	spanLock       sync.Mutex | ||||
| 	metricsStorage otlpmetrictest.MetricsStorage | ||||
|  | ||||
| 	injectHTTPStatus  []int | ||||
| 	injectContentType string | ||||
| 	injectDelay       time.Duration | ||||
|  | ||||
| 	clientTLSConfig *tls.Config | ||||
| 	expectedHeaders map[string]string | ||||
| } | ||||
|  | ||||
| func (c *mockCollector) Stop() error { | ||||
| 	return c.server.Shutdown(context.Background()) | ||||
| } | ||||
|  | ||||
| func (c *mockCollector) MustStop(t *testing.T) { | ||||
| 	assert.NoError(t, c.server.Shutdown(context.Background())) | ||||
| } | ||||
|  | ||||
| func (c *mockCollector) GetMetrics() []*metricpb.Metric { | ||||
| 	c.spanLock.Lock() | ||||
| 	defer c.spanLock.Unlock() | ||||
| 	return c.metricsStorage.GetMetrics() | ||||
| } | ||||
|  | ||||
| func (c *mockCollector) Endpoint() string { | ||||
| 	return c.endpoint | ||||
| } | ||||
|  | ||||
| func (c *mockCollector) ClientTLSConfig() *tls.Config { | ||||
| 	return c.clientTLSConfig | ||||
| } | ||||
|  | ||||
| func (c *mockCollector) serveMetrics(w http.ResponseWriter, r *http.Request) { | ||||
| 	if c.injectDelay != 0 { | ||||
| 		time.Sleep(c.injectDelay) | ||||
| 	} | ||||
|  | ||||
| 	if !c.checkHeaders(r) { | ||||
| 		w.WriteHeader(http.StatusBadRequest) | ||||
| 		return | ||||
| 	} | ||||
| 	response := collectormetricpb.ExportMetricsServiceResponse{} | ||||
| 	rawResponse, err := proto.Marshal(&response) | ||||
| 	if err != nil { | ||||
| 		w.WriteHeader(http.StatusInternalServerError) | ||||
| 		return | ||||
| 	} | ||||
| 	if injectedStatus := c.getInjectHTTPStatus(); injectedStatus != 0 { | ||||
| 		writeReply(w, rawResponse, injectedStatus, c.injectContentType) | ||||
| 		return | ||||
| 	} | ||||
| 	rawRequest, err := readRequest(r) | ||||
| 	if err != nil { | ||||
| 		w.WriteHeader(http.StatusInternalServerError) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	request, err := unmarshalMetricsRequest(rawRequest, r.Header.Get("content-type")) | ||||
| 	if err != nil { | ||||
| 		w.WriteHeader(http.StatusBadRequest) | ||||
| 		return | ||||
| 	} | ||||
| 	writeReply(w, rawResponse, 0, c.injectContentType) | ||||
| 	c.spanLock.Lock() | ||||
| 	defer c.spanLock.Unlock() | ||||
| 	c.metricsStorage.AddMetrics(request) | ||||
| } | ||||
|  | ||||
| func unmarshalMetricsRequest(rawRequest []byte, contentType string) (*collectormetricpb.ExportMetricsServiceRequest, error) { | ||||
| 	request := &collectormetricpb.ExportMetricsServiceRequest{} | ||||
| 	if contentType != "application/x-protobuf" { | ||||
| 		return request, fmt.Errorf("invalid content-type: %s, only application/x-protobuf is supported", contentType) | ||||
| 	} | ||||
| 	err := proto.Unmarshal(rawRequest, request) | ||||
| 	return request, err | ||||
| } | ||||
|  | ||||
| func (c *mockCollector) checkHeaders(r *http.Request) bool { | ||||
| 	for k, v := range c.expectedHeaders { | ||||
| 		got := r.Header.Get(k) | ||||
| 		if got != v { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (c *mockCollector) getInjectHTTPStatus() int { | ||||
| 	if len(c.injectHTTPStatus) == 0 { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	status := c.injectHTTPStatus[0] | ||||
| 	c.injectHTTPStatus = c.injectHTTPStatus[1:] | ||||
| 	if len(c.injectHTTPStatus) == 0 { | ||||
| 		c.injectHTTPStatus = nil | ||||
| 	} | ||||
| 	return status | ||||
| } | ||||
|  | ||||
| func readRequest(r *http.Request) ([]byte, error) { | ||||
| 	if r.Header.Get("Content-Encoding") == "gzip" { | ||||
| 		return readGzipBody(r.Body) | ||||
| 	} | ||||
| 	return ioutil.ReadAll(r.Body) | ||||
| } | ||||
|  | ||||
| func readGzipBody(body io.Reader) ([]byte, error) { | ||||
| 	rawRequest := bytes.Buffer{} | ||||
| 	gunzipper, err := gzip.NewReader(body) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer gunzipper.Close() | ||||
| 	_, err = io.Copy(&rawRequest, gunzipper) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return rawRequest.Bytes(), nil | ||||
| } | ||||
|  | ||||
| func writeReply(w http.ResponseWriter, rawResponse []byte, injectHTTPStatus int, injectContentType string) { | ||||
| 	status := http.StatusOK | ||||
| 	if injectHTTPStatus != 0 { | ||||
| 		status = injectHTTPStatus | ||||
| 	} | ||||
| 	contentType := "application/x-protobuf" | ||||
| 	if injectContentType != "" { | ||||
| 		contentType = injectContentType | ||||
| 	} | ||||
| 	w.Header().Set("Content-Type", contentType) | ||||
| 	w.WriteHeader(status) | ||||
| 	_, _ = w.Write(rawResponse) | ||||
| } | ||||
|  | ||||
| type mockCollectorConfig struct { | ||||
| 	MetricsURLPath    string | ||||
| 	Port              int | ||||
| 	InjectHTTPStatus  []int | ||||
| 	InjectContentType string | ||||
| 	InjectDelay       time.Duration | ||||
| 	WithTLS           bool | ||||
| 	ExpectedHeaders   map[string]string | ||||
| } | ||||
|  | ||||
| func (c *mockCollectorConfig) fillInDefaults() { | ||||
| 	if c.MetricsURLPath == "" { | ||||
| 		c.MetricsURLPath = otlpconfig.DefaultMetricsPath | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func runMockCollector(t *testing.T, cfg mockCollectorConfig) *mockCollector { | ||||
| 	cfg.fillInDefaults() | ||||
| 	ln, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", cfg.Port)) | ||||
| 	require.NoError(t, err) | ||||
| 	_, portStr, err := net.SplitHostPort(ln.Addr().String()) | ||||
| 	require.NoError(t, err) | ||||
| 	m := &mockCollector{ | ||||
| 		endpoint:          fmt.Sprintf("localhost:%s", portStr), | ||||
| 		metricsStorage:    otlpmetrictest.NewMetricsStorage(), | ||||
| 		injectHTTPStatus:  cfg.InjectHTTPStatus, | ||||
| 		injectContentType: cfg.InjectContentType, | ||||
| 		injectDelay:       cfg.InjectDelay, | ||||
| 		expectedHeaders:   cfg.ExpectedHeaders, | ||||
| 	} | ||||
| 	mux := http.NewServeMux() | ||||
| 	mux.Handle(cfg.MetricsURLPath, http.HandlerFunc(m.serveMetrics)) | ||||
| 	server := &http.Server{ | ||||
| 		Handler: mux, | ||||
| 	} | ||||
| 	if cfg.WithTLS { | ||||
| 		pem, err := generateWeakCertificate() | ||||
| 		require.NoError(t, err) | ||||
| 		tlsCertificate, err := tls.X509KeyPair(pem.Certificate, pem.PrivateKey) | ||||
| 		require.NoError(t, err) | ||||
| 		server.TLSConfig = &tls.Config{ | ||||
| 			Certificates: []tls.Certificate{tlsCertificate}, | ||||
| 		} | ||||
|  | ||||
| 		m.clientTLSConfig = &tls.Config{ | ||||
| 			InsecureSkipVerify: true, | ||||
| 		} | ||||
| 	} | ||||
| 	go func() { | ||||
| 		if cfg.WithTLS { | ||||
| 			_ = server.ServeTLS(ln, "", "") | ||||
| 		} else { | ||||
| 			_ = server.Serve(ln) | ||||
| 		} | ||||
| 	}() | ||||
| 	m.server = server | ||||
| 	return m | ||||
| } | ||||
							
								
								
									
										122
									
								
								exporters/otlp/otlpmetric/otlpmetrichttp/options.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								exporters/otlp/otlpmetric/otlpmetrichttp/options.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,122 @@ | ||||
| // Copyright The OpenTelemetry 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 otlpmetrichttp | ||||
|  | ||||
| import ( | ||||
| 	"crypto/tls" | ||||
| 	"time" | ||||
|  | ||||
| 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpconfig" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// defaultMaxAttempts describes how many times the driver | ||||
| 	// should retry the sending of the payload in case of a | ||||
| 	// retryable error. | ||||
| 	defaultMaxAttempts int = 5 | ||||
| 	// defaultMetricsPath is a default URL path for endpoint that | ||||
| 	// receives metrics. | ||||
| 	defaultMetricsPath string = "/v1/metrics" | ||||
| 	// defaultBackoff is a default base backoff time used in the | ||||
| 	// exponential backoff strategy. | ||||
| 	defaultBackoff time.Duration = 300 * time.Millisecond | ||||
| ) | ||||
|  | ||||
| // Compression describes the compression used for payloads sent to the | ||||
| // collector. | ||||
| type Compression otlpconfig.Compression | ||||
|  | ||||
| const ( | ||||
| 	// NoCompression tells the driver to send payloads without | ||||
| 	// compression. | ||||
| 	NoCompression = Compression(otlpconfig.NoCompression) | ||||
| 	// GzipCompression tells the driver to send payloads after | ||||
| 	// compressing them with gzip. | ||||
| 	GzipCompression = Compression(otlpconfig.GzipCompression) | ||||
| ) | ||||
|  | ||||
| // Option applies an option to the HTTP client. | ||||
| type Option interface { | ||||
| 	applyHTTPOption(*otlpconfig.Config) | ||||
| } | ||||
|  | ||||
| type wrappedOption struct { | ||||
| 	otlpconfig.HTTPOption | ||||
| } | ||||
|  | ||||
| func (w wrappedOption) applyHTTPOption(cfg *otlpconfig.Config) { | ||||
| 	w.ApplyHTTPOption(cfg) | ||||
| } | ||||
|  | ||||
| // WithEndpoint allows one to set the address of the collector | ||||
| // endpoint that the driver will use to send metrics. If | ||||
| // unset, it will instead try to use | ||||
| // the default endpoint (localhost:4317). Note that the endpoint | ||||
| // must not contain any URL path. | ||||
| func WithEndpoint(endpoint string) Option { | ||||
| 	return wrappedOption{otlpconfig.WithEndpoint(endpoint)} | ||||
| } | ||||
|  | ||||
| // WithCompression tells the driver to compress the sent data. | ||||
| func WithCompression(compression Compression) Option { | ||||
| 	return wrappedOption{otlpconfig.WithCompression(otlpconfig.Compression(compression))} | ||||
| } | ||||
|  | ||||
| // WithURLPath allows one to override the default URL path used | ||||
| // for sending metrics. If unset, default ("/v1/metrics") will be used. | ||||
| func WithURLPath(urlPath string) Option { | ||||
| 	return wrappedOption{otlpconfig.WithURLPath(urlPath)} | ||||
| } | ||||
|  | ||||
| // WithMaxAttempts allows one to override how many times the driver | ||||
| // will try to send the payload in case of retryable errors. | ||||
| // The max attempts is limited to at most 5 retries. If unset, | ||||
| // default (5) will be used. | ||||
| func WithMaxAttempts(maxAttempts int) Option { | ||||
| 	return wrappedOption{otlpconfig.WithMaxAttempts(maxAttempts)} | ||||
| } | ||||
|  | ||||
| // WithBackoff tells the driver to use the duration as a base of the | ||||
| // exponential backoff strategy. If unset, default (300ms) will be | ||||
| // used. | ||||
| func WithBackoff(duration time.Duration) Option { | ||||
| 	return wrappedOption{otlpconfig.WithBackoff(duration)} | ||||
| } | ||||
|  | ||||
| // WithTLSClientConfig can be used to set up a custom TLS | ||||
| // configuration for the client used to send payloads to the | ||||
| // collector. Use it if you want to use a custom certificate. | ||||
| func WithTLSClientConfig(tlsCfg *tls.Config) Option { | ||||
| 	return wrappedOption{otlpconfig.WithTLSClientConfig(tlsCfg)} | ||||
| } | ||||
|  | ||||
| // WithInsecure tells the driver to connect to the collector using the | ||||
| // HTTP scheme, instead of HTTPS. | ||||
| func WithInsecure() Option { | ||||
| 	return wrappedOption{otlpconfig.WithInsecure()} | ||||
| } | ||||
|  | ||||
| // WithHeaders allows one to tell the driver to send additional HTTP | ||||
| // headers with the payloads. Specifying headers like Content-Length, | ||||
| // Content-Encoding and Content-Type may result in a broken driver. | ||||
| func WithHeaders(headers map[string]string) Option { | ||||
| 	return wrappedOption{otlpconfig.WithHeaders(headers)} | ||||
| } | ||||
|  | ||||
| // WithTimeout tells the driver the max waiting time for the backend to process | ||||
| // each metrics batch.  If unset, the default will be 10 seconds. | ||||
| func WithTimeout(duration time.Duration) Option { | ||||
| 	return wrappedOption{otlpconfig.WithTimeout(duration)} | ||||
| } | ||||
| @@ -73,3 +73,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -70,3 +70,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../../stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../../stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -67,3 +67,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../../stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../../stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -71,3 +71,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -69,3 +69,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../otlp/otlpmet | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../otlp/otlpmetric/otlpmetricgrpc | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -68,3 +68,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../otlp/otlpmet | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../otlp/otlpmetric/otlpmetricgrpc | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -70,3 +70,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							| @@ -66,3 +66,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ./e | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ./exporters/stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ./exporters/stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ./exporters/otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -65,3 +65,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -69,3 +69,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -66,3 +66,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../exporters/stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../exporters/stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../exporters/otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -65,3 +65,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../exporters/stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../exporters/stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../exporters/otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -66,3 +66,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../../exporters/stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../../exporters/stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../../exporters/otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -67,3 +67,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../exporters/stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../exporters/stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../exporters/otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -69,3 +69,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
| @@ -65,3 +65,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../exporters/stdout/stdoutmetric | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../exporters/stdout/stdouttrace | ||||
|  | ||||
| replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../exporters/otlp/otlpmetric/otlpmetrichttp | ||||
|   | ||||
		Reference in New Issue
	
	Block a user