Skip to content
This repository has been archived by the owner on Jul 31, 2023. It is now read-only.

Commit

Permalink
Revert "Remove Stackdriver exporter (#790)" (#854)
Browse files Browse the repository at this point in the history
This reverts commit df6bd80.
  • Loading branch information
Ramon Nogueira authored Aug 2, 2018
1 parent df6bd80 commit 745e4f4
Show file tree
Hide file tree
Showing 10 changed files with 2,620 additions and 0 deletions.
94 changes: 94 additions & 0 deletions exporter/stackdriver/propagation/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright 2018, OpenCensus 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 propagation implement X-Cloud-Trace-Context header propagation used
// by Google Cloud products.
package propagation // import "go.opencensus.io/exporter/stackdriver/propagation"

import (
"encoding/binary"
"encoding/hex"
"fmt"
"net/http"
"strconv"
"strings"

"go.opencensus.io/trace"
"go.opencensus.io/trace/propagation"
)

const (
httpHeaderMaxSize = 200
httpHeader = `X-Cloud-Trace-Context`
)

var _ propagation.HTTPFormat = (*HTTPFormat)(nil)

// HTTPFormat implements propagation.HTTPFormat to propagate
// traces in HTTP headers for Google Cloud Platform and Stackdriver Trace.
type HTTPFormat struct{}

// SpanContextFromRequest extracts a Stackdriver Trace span context from incoming requests.
func (f *HTTPFormat) SpanContextFromRequest(req *http.Request) (sc trace.SpanContext, ok bool) {
h := req.Header.Get(httpHeader)
// See https://cloud.google.com/trace/docs/faq for the header HTTPFormat.
// Return if the header is empty or missing, or if the header is unreasonably
// large, to avoid making unnecessary copies of a large string.
if h == "" || len(h) > httpHeaderMaxSize {
return trace.SpanContext{}, false
}

// Parse the trace id field.
slash := strings.Index(h, `/`)
if slash == -1 {
return trace.SpanContext{}, false
}
tid, h := h[:slash], h[slash+1:]

buf, err := hex.DecodeString(tid)
if err != nil {
return trace.SpanContext{}, false
}
copy(sc.TraceID[:], buf)

// Parse the span id field.
spanstr := h
semicolon := strings.Index(h, `;`)
if semicolon != -1 {
spanstr, h = h[:semicolon], h[semicolon+1:]
}
sid, err := strconv.ParseUint(spanstr, 10, 64)
if err != nil {
return trace.SpanContext{}, false
}
binary.BigEndian.PutUint64(sc.SpanID[:], sid)

// Parse the options field, options field is optional.
if !strings.HasPrefix(h, "o=") {
return sc, true
}
o, err := strconv.ParseUint(h[2:], 10, 64)
if err != nil {
return trace.SpanContext{}, false
}
sc.TraceOptions = trace.TraceOptions(o)
return sc, true
}

// SpanContextToRequest modifies the given request to include a Stackdriver Trace header.
func (f *HTTPFormat) SpanContextToRequest(sc trace.SpanContext, req *http.Request) {
sid := binary.BigEndian.Uint64(sc.SpanID[:])
header := fmt.Sprintf("%s/%d;o=%d", hex.EncodeToString(sc.TraceID[:]), sid, int64(sc.TraceOptions))
req.Header.Set(httpHeader, header)
}
70 changes: 70 additions & 0 deletions exporter/stackdriver/propagation/http_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright 2018, OpenCensus 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 propagation

import (
"net/http"
"reflect"
"testing"

"go.opencensus.io/trace"
)

func TestHTTPFormat(t *testing.T) {
format := &HTTPFormat{}
traceID := [16]byte{16, 84, 69, 170, 120, 67, 188, 139, 242, 6, 177, 32, 0, 16, 0, 0}
spanID1 := [8]byte{255, 0, 0, 0, 0, 0, 0, 123}
spanID2 := [8]byte{0, 0, 0, 0, 0, 0, 0, 123}
tests := []struct {
incoming string
wantSpanContext trace.SpanContext
}{
{
incoming: "105445aa7843bc8bf206b12000100000/18374686479671623803;o=1",
wantSpanContext: trace.SpanContext{
TraceID: traceID,
SpanID: spanID1,
TraceOptions: 1,
},
},
{
incoming: "105445aa7843bc8bf206b12000100000/123;o=0",
wantSpanContext: trace.SpanContext{
TraceID: traceID,
SpanID: spanID2,
TraceOptions: 0,
},
},
}
for _, tt := range tests {
t.Run(tt.incoming, func(t *testing.T) {
req, _ := http.NewRequest("GET", "http://example.com", nil)
req.Header.Add(httpHeader, tt.incoming)
sc, ok := format.SpanContextFromRequest(req)
if !ok {
t.Errorf("exporter.SpanContextFromRequest() = false; want true")
}
if got, want := sc, tt.wantSpanContext; !reflect.DeepEqual(got, want) {
t.Errorf("exporter.SpanContextFromRequest() returned span context %v; want %v", got, want)
}

req, _ = http.NewRequest("GET", "http://example.com", nil)
format.SpanContextToRequest(sc, req)
if got, want := req.Header.Get(httpHeader), tt.incoming; got != want {
t.Errorf("exporter.SpanContextToRequest() returned header %q; want %q", got, want)
}
})
}
}
148 changes: 148 additions & 0 deletions exporter/stackdriver/stackdriver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// Copyright 2018, OpenCensus 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 stackdriver has moved.
//
// Deprecated: Use contrib.go.opencensus.io/exporter/stackdriver instead.
package stackdriver // import "go.opencensus.io/exporter/stackdriver"

import (
"context"
"errors"
"fmt"
"log"
"time"

traceapi "cloud.google.com/go/trace/apiv2"
"go.opencensus.io/stats/view"
"go.opencensus.io/trace"
"golang.org/x/oauth2/google"
"google.golang.org/api/option"
monitoredrespb "google.golang.org/genproto/googleapis/api/monitoredres"
)

// Options contains options for configuring the exporter.
//
// Deprecated: This package has been moved to: contrib.go.opencensus.io/exporter/stackdriver.
type Options struct {
// ProjectID is the identifier of the Stackdriver
// project the user is uploading the stats data to.
// If not set, this will default to your "Application Default Credentials".
// For details see: https://developers.google.com/accounts/docs/application-default-credentials
ProjectID string

// OnError is the hook to be called when there is
// an error uploading the stats or tracing data.
// If no custom hook is set, errors are logged.
// Optional.
OnError func(err error)

// MonitoringClientOptions are additional options to be passed
// to the underlying Stackdriver Monitoring API client.
// Optional.
MonitoringClientOptions []option.ClientOption

// TraceClientOptions are additional options to be passed
// to the underlying Stackdriver Trace API client.
// Optional.
TraceClientOptions []option.ClientOption

// BundleDelayThreshold determines the max amount of time
// the exporter can wait before uploading view data to
// the backend.
// Optional.
BundleDelayThreshold time.Duration

// BundleCountThreshold determines how many view data events
// can be buffered before batch uploading them to the backend.
// Optional.
BundleCountThreshold int

// Resource is an optional field that represents the Stackdriver
// MonitoredResource, a resource that can be used for monitoring.
// If no custom ResourceDescriptor is set, a default MonitoredResource
// with type global and no resource labels will be used.
// Optional.
Resource *monitoredrespb.MonitoredResource

// MetricPrefix overrides the OpenCensus prefix of a stackdriver metric.
// Optional.
MetricPrefix string
}

// Exporter is a stats.Exporter and trace.Exporter
// implementation that uploads data to Stackdriver.
//
// Deprecated: This package has been moved to: contrib.go.opencensus.io/exporter/stackdriver.
type Exporter struct {
traceExporter *traceExporter
statsExporter *statsExporter
}

// NewExporter creates a new Exporter that implements both stats.Exporter and
// trace.Exporter.
//
// Deprecated: This package has been moved to: contrib.go.opencensus.io/exporter/stackdriver.
func NewExporter(o Options) (*Exporter, error) {
if o.ProjectID == "" {
creds, err := google.FindDefaultCredentials(context.Background(), traceapi.DefaultAuthScopes()...)
if err != nil {
return nil, fmt.Errorf("stackdriver: %v", err)
}
if creds.ProjectID == "" {
return nil, errors.New("stackdriver: no project found with application default credentials")
}
o.ProjectID = creds.ProjectID
}
se, err := newStatsExporter(o)
if err != nil {
return nil, err
}
te, err := newTraceExporter(o)
if err != nil {
return nil, err
}
return &Exporter{
statsExporter: se,
traceExporter: te,
}, nil
}

// ExportView exports to the Stackdriver Monitoring if view data
// has one or more rows.
func (e *Exporter) ExportView(vd *view.Data) {
e.statsExporter.ExportView(vd)
}

// ExportSpan exports a SpanData to Stackdriver Trace.
func (e *Exporter) ExportSpan(sd *trace.SpanData) {
e.traceExporter.ExportSpan(sd)
}

// Flush waits for exported data to be uploaded.
//
// This is useful if your program is ending and you do not
// want to lose recent stats or spans.
func (e *Exporter) Flush() {
e.statsExporter.Flush()
e.traceExporter.Flush()
}

func (o Options) handleError(err error) {
if o.OnError != nil {
o.OnError(err)
return
}
log.Printf("Error exporting to Stackdriver: %v", err)
}
Loading

0 comments on commit 745e4f4

Please sign in to comment.