Skip to content

Commit

Permalink
Add convenience wrappers for http.Get/Post, etc. (#390)
Browse files Browse the repository at this point in the history
* Add convenience wrappers for http.Get/Post, etc.

Add convenience wrappers for http.{Get,Post,Head,PostForm}
using the otelhttp transport and taking a context.Context directly.
Makes instrumenting code using the original http convenience methods a
one-line change instead of a multiline one.

* Update instrumentation/net/http/otelhttp/client.go

Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>

* Update instrumentation/net/http/otelhttp/client.go

Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>

* Update instrumentation/net/http/otelhttp/client.go

Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>

* Update instrumentation/net/http/otelhttp/client.go

Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>

* Document new convenience wrappers in CHANGELOG

* Add tests, don't use cached global client.

* code format

* Expose DefaultClient and use it for all convenience wrappers.

* Update instrumentation/net/http/otelhttp/client.go

Co-authored-by: Sam Xie <xsambundy@gmail.com>

* Update CHANGELOG.md

Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>

* Don't bother attempting to reset the global provider.

* Assert existence of 4 clients spans for 4 client operations

* Improve comment on DefaultClient.

* Improve span testing.

* Move release note to unreleased section

Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>
Co-authored-by: Sam Xie <xsambundy@gmail.com>
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
  • Loading branch information
4 people authored Oct 21, 2020
1 parent 15f50e0 commit 4f3d787
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## [Unreleased]

### Added

- `otelhttp.{Get,Head,Post,PostForm}` convenience wrappers for their `http` counterparts. (#390)

## Fixed

- `/detectors/aws` no longer fails if instance metadata is not available (e.g. not running in AWS) (#401)
Expand Down
61 changes: 61 additions & 0 deletions instrumentation/net/http/otelhttp/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// 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 otelhttp

import (
"context"
"io"
"net/http"
"net/url"
"strings"
)

// DefaultClient is the default Client and is used by Get, Head, Post and PostForm.
// Please be careful of intitialization order - for example, if you change
// the global propagator, the DefaultClient might still be using the old one
var DefaultClient = &http.Client{Transport: NewTransport(http.DefaultTransport)}

// Get is a convenient replacement for http.Get that adds a span around the request.
func Get(ctx context.Context, url string) (resp *http.Response, err error) {
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, err
}
return DefaultClient.Do(req)
}

// Head is a convenient replacement for http.Head that adds a span around the request.
func Head(ctx context.Context, url string) (resp *http.Response, err error) {
req, err := http.NewRequestWithContext(ctx, "HEAD", url, nil)
if err != nil {
return nil, err
}
return DefaultClient.Do(req)
}

// Post is a convenient replacement for http.Post that adds a span around the request.
func Post(ctx context.Context, url, contentType string, body io.Reader) (resp *http.Response, err error) {
req, err := http.NewRequestWithContext(ctx, "POST", url, body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", contentType)
return DefaultClient.Do(req)
}

// PostForm is a convenient replacement for http.PostForm that adds a span around the request.
func PostForm(ctx context.Context, url string, data url.Values) (resp *http.Response, err error) {
return Post(ctx, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
}
80 changes: 80 additions & 0 deletions instrumentation/net/http/otelhttp/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// 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 otelhttp

import (
"context"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"

"go.opentelemetry.io/otel/api/global"

"github.com/stretchr/testify/assert"

mocktrace "go.opentelemetry.io/contrib/internal/trace"
)

func TestConvenienceWrappers(t *testing.T) {
provider, tracer := mocktrace.NewTracerProviderAndTracer(instrumentationName)
global.SetTracerProvider(provider)

content := []byte("Hello, world!")

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if _, err := w.Write(content); err != nil {
t.Fatal(err)
}
}))
defer ts.Close()

context, span := tracer.Start(context.Background(), "parent")
defer span.End()

res, err := Get(context, ts.URL)
if err != nil {
t.Fatal(err)
}
res.Body.Close()

res, err = Head(context, ts.URL)
if err != nil {
t.Fatal(err)
}
res.Body.Close()

res, err = Post(context, ts.URL, "text/plain", strings.NewReader("test"))
if err != nil {
t.Fatal(err)
}
res.Body.Close()

form := make(url.Values)
form.Set("foo", "bar")
res, err = PostForm(context, ts.URL, form)
if err != nil {
t.Fatal(err)
}
res.Body.Close()

spans := tracer.EndedSpans()
assert.Equal(t, 4, len(spans))
assert.Equal(t, "GET", spans[0].Name)
assert.Equal(t, "HEAD", spans[1].Name)
assert.Equal(t, "POST", spans[2].Name)
assert.Equal(t, "POST", spans[3].Name)
}

0 comments on commit 4f3d787

Please sign in to comment.