Skip to content

Commit 4f3d787

Browse files
johnbleyAneurysm9XSAMMrAlias
authored
Add convenience wrappers for http.Get/Post, etc. (#390)
* 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 <[email protected]> * Update instrumentation/net/http/otelhttp/client.go Co-authored-by: Anthony Mirabella <[email protected]> * Update instrumentation/net/http/otelhttp/client.go Co-authored-by: Anthony Mirabella <[email protected]> * Update instrumentation/net/http/otelhttp/client.go Co-authored-by: Anthony Mirabella <[email protected]> * 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 <[email protected]> * Update CHANGELOG.md Co-authored-by: Tyler Yahn <[email protected]> * 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 <[email protected]> Co-authored-by: Sam Xie <[email protected]> Co-authored-by: Tyler Yahn <[email protected]>
1 parent 15f50e0 commit 4f3d787

File tree

3 files changed

+145
-0
lines changed

3 files changed

+145
-0
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
88

99
## [Unreleased]
1010

11+
### Added
12+
13+
- `otelhttp.{Get,Head,Post,PostForm}` convenience wrappers for their `http` counterparts. (#390)
14+
1115
## Fixed
1216

1317
- `/detectors/aws` no longer fails if instance metadata is not available (e.g. not running in AWS) (#401)
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright The OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package otelhttp
16+
17+
import (
18+
"context"
19+
"io"
20+
"net/http"
21+
"net/url"
22+
"strings"
23+
)
24+
25+
// DefaultClient is the default Client and is used by Get, Head, Post and PostForm.
26+
// Please be careful of intitialization order - for example, if you change
27+
// the global propagator, the DefaultClient might still be using the old one
28+
var DefaultClient = &http.Client{Transport: NewTransport(http.DefaultTransport)}
29+
30+
// Get is a convenient replacement for http.Get that adds a span around the request.
31+
func Get(ctx context.Context, url string) (resp *http.Response, err error) {
32+
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
33+
if err != nil {
34+
return nil, err
35+
}
36+
return DefaultClient.Do(req)
37+
}
38+
39+
// Head is a convenient replacement for http.Head that adds a span around the request.
40+
func Head(ctx context.Context, url string) (resp *http.Response, err error) {
41+
req, err := http.NewRequestWithContext(ctx, "HEAD", url, nil)
42+
if err != nil {
43+
return nil, err
44+
}
45+
return DefaultClient.Do(req)
46+
}
47+
48+
// Post is a convenient replacement for http.Post that adds a span around the request.
49+
func Post(ctx context.Context, url, contentType string, body io.Reader) (resp *http.Response, err error) {
50+
req, err := http.NewRequestWithContext(ctx, "POST", url, body)
51+
if err != nil {
52+
return nil, err
53+
}
54+
req.Header.Set("Content-Type", contentType)
55+
return DefaultClient.Do(req)
56+
}
57+
58+
// PostForm is a convenient replacement for http.PostForm that adds a span around the request.
59+
func PostForm(ctx context.Context, url string, data url.Values) (resp *http.Response, err error) {
60+
return Post(ctx, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
61+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Copyright The OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package otelhttp
16+
17+
import (
18+
"context"
19+
"net/http"
20+
"net/http/httptest"
21+
"net/url"
22+
"strings"
23+
"testing"
24+
25+
"go.opentelemetry.io/otel/api/global"
26+
27+
"github.com/stretchr/testify/assert"
28+
29+
mocktrace "go.opentelemetry.io/contrib/internal/trace"
30+
)
31+
32+
func TestConvenienceWrappers(t *testing.T) {
33+
provider, tracer := mocktrace.NewTracerProviderAndTracer(instrumentationName)
34+
global.SetTracerProvider(provider)
35+
36+
content := []byte("Hello, world!")
37+
38+
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
39+
if _, err := w.Write(content); err != nil {
40+
t.Fatal(err)
41+
}
42+
}))
43+
defer ts.Close()
44+
45+
context, span := tracer.Start(context.Background(), "parent")
46+
defer span.End()
47+
48+
res, err := Get(context, ts.URL)
49+
if err != nil {
50+
t.Fatal(err)
51+
}
52+
res.Body.Close()
53+
54+
res, err = Head(context, ts.URL)
55+
if err != nil {
56+
t.Fatal(err)
57+
}
58+
res.Body.Close()
59+
60+
res, err = Post(context, ts.URL, "text/plain", strings.NewReader("test"))
61+
if err != nil {
62+
t.Fatal(err)
63+
}
64+
res.Body.Close()
65+
66+
form := make(url.Values)
67+
form.Set("foo", "bar")
68+
res, err = PostForm(context, ts.URL, form)
69+
if err != nil {
70+
t.Fatal(err)
71+
}
72+
res.Body.Close()
73+
74+
spans := tracer.EndedSpans()
75+
assert.Equal(t, 4, len(spans))
76+
assert.Equal(t, "GET", spans[0].Name)
77+
assert.Equal(t, "HEAD", spans[1].Name)
78+
assert.Equal(t, "POST", spans[2].Name)
79+
assert.Equal(t, "POST", spans[3].Name)
80+
}

0 commit comments

Comments
 (0)