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

Add option to allow user agent to be explicitly overridden #274

Merged
merged 3 commits into from
Aug 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions stackdriver.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import (
metadataapi "cloud.google.com/go/compute/metadata"
traceapi "cloud.google.com/go/trace/apiv2"
"contrib.go.opencensus.io/exporter/stackdriver/monitoredresource"
opencensus "go.opencensus.io"
"go.opencensus.io/resource"
"go.opencensus.io/resource/resourcekeys"
"go.opencensus.io/stats/view"
Expand Down Expand Up @@ -279,12 +280,18 @@ type Options struct {
// time-series then it will result into an error for the entire CreateTimeSeries request
// which may contain more than one time-series.
ResourceByDescriptor func(*metricdata.Descriptor, map[string]string) (map[string]string, monitoredresource.Interface)

// Override the user agent value supplied to Monitoring APIs and included as an
// attribute in trace data.
UserAgent string
}

const defaultTimeout = 5 * time.Second

var defaultDomain = path.Join("custom.googleapis.com", "opencensus")

var defaultUserAgent = fmt.Sprintf("opencensus-go %s; stackdriver-exporter %s", opencensus.Version(), version)

// Exporter is a stats and trace exporter that uploads data to Stackdriver.
//
// You can create a single Exporter and register it as both a trace exporter
Expand Down Expand Up @@ -362,6 +369,9 @@ func NewExporter(o Options) (*Exporter, error) {
if o.MetricPrefix != "" && !strings.HasSuffix(o.MetricPrefix, "/") {
o.MetricPrefix = o.MetricPrefix + "/"
}
if o.UserAgent == "" {
o.UserAgent = defaultUserAgent
}

se, err := newStatsExporter(o)
if err != nil {
Expand Down
11 changes: 11 additions & 0 deletions stackdriver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,14 @@ func TestGRPC(t *testing.T) {

client.Single(context.Background(), &testpb.FooRequest{SleepNanos: int64(42 * time.Millisecond)})
}

func TestUserAgent(t *testing.T) {
e, err := NewExporter(Options{UserAgent: "OpenCensus Service"})
if err != nil {
t.Fatal(err)
}

if want, got := "OpenCensus Service", e.statsExporter.o.UserAgent; want != got {
t.Fatalf("UserAgent = %q; want %q", got, want)
}
}
7 changes: 2 additions & 5 deletions stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import (
"sync"
"time"

opencensus "go.opencensus.io"
"go.opencensus.io/stats"
"go.opencensus.io/stats/view"
"go.opencensus.io/tag"
Expand All @@ -52,11 +51,9 @@ const (
opencensusTaskKey = "opencensus_task"
opencensusTaskDescription = "Opencensus task identifier"
defaultDisplayNamePrefix = "OpenCensus"
version = "0.10.0"
version = "0.12.3"
Copy link
Contributor Author

@james-bebbington james-bebbington Aug 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this hadn't been updated for a while. Updating this to prepare for the next (and hopefully final) release I will do after merging this PR

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we do want the external code to be able to use this version in their custom user agent string, we should make this field public.

Also, this seems like a weird place to define version since it's not specific to stats AFAICT.
If you prefer to leave this code as is, that's fine by me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea I don't think I will move it. There seems to be quite a lot of global stuff defined in stats.go.

Also as you previously mentioned, it's unlikely external code would want to use this version no. in their custom user agent string, so I will leave this as a private field for now.

)

var userAgent = fmt.Sprintf("opencensus-go %s; stackdriver-exporter %s", opencensus.Version(), version)

// statsExporter exports stats to the Stackdriver Monitoring.
type statsExporter struct {
o Options
Expand Down Expand Up @@ -89,7 +86,7 @@ func newStatsExporter(o Options) (*statsExporter, error) {
return nil, errBlankProjectID
}

opts := append(o.MonitoringClientOptions, option.WithUserAgent(userAgent))
opts := append(o.MonitoringClientOptions, option.WithUserAgent(o.UserAgent))
ctx := o.Context
if ctx == nil {
ctx = context.Background()
Expand Down
4 changes: 2 additions & 2 deletions trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func newTraceExporterWithClient(o Options, c *tracingclient.Client) *traceExport

// ExportSpan exports a SpanData to Stackdriver Trace.
func (e *traceExporter) ExportSpan(s *trace.SpanData) {
protoSpan := protoFromSpanData(s, e.projectID, e.o.Resource)
protoSpan := protoFromSpanData(s, e.projectID, e.o.Resource, e.o.UserAgent)
protoSize := proto.Size(protoSpan)
err := e.bundler.Add(protoSpan, protoSize)
switch err {
Expand Down Expand Up @@ -137,7 +137,7 @@ func (e *traceExporter) pushTraceSpans(ctx context.Context, node *commonpb.Node,
}

for _, span := range spans {
protoSpans = append(protoSpans, protoFromSpanData(span, e.projectID, res))
protoSpans = append(protoSpans, protoFromSpanData(span, e.projectID, res, e.o.UserAgent))
}

req := tracepb.BatchWriteSpansRequest{
Expand Down
6 changes: 5 additions & 1 deletion trace_proto.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const (
)

// proto returns a protocol buffer representation of a SpanData.
func protoFromSpanData(s *trace.SpanData, projectID string, mr *monitoredrespb.MonitoredResource) *tracepb.Span {
func protoFromSpanData(s *trace.SpanData, projectID string, mr *monitoredrespb.MonitoredResource, userAgent string) *tracepb.Span {
if s == nil {
return nil
}
Expand Down Expand Up @@ -109,6 +109,10 @@ func protoFromSpanData(s *trace.SpanData, projectID string, mr *monitoredrespb.M
// Only set the agent label if it is not already set. That enables the
// OpenCensus agent/collector to set the agent label based on the library that
// sent the span to the agent.
//
// We now provide a config option to set the userAgent explicitly, which is
// used both here and in request headers when sending metric data, but have
// retained this non-override functionality for backwards compatibility.
if _, hasAgent := sp.Attributes.AttributeMap[agentLabel]; !hasAgent {
sp.Attributes.AttributeMap[agentLabel] = &tracepb.AttributeValue{
Value: &tracepb.AttributeValue_StringValue{
Expand Down
12 changes: 6 additions & 6 deletions trace_proto_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func generateSpan() {
}

func createExpectedSpans() spans {
ua := trunc(userAgent, len(userAgent))
ua := trunc(defaultUserAgent, len(defaultUserAgent))
expectedSpans := spans{
&tracepb.Span{
DisplayName: trunc("span0", 128),
Expand Down Expand Up @@ -284,7 +284,7 @@ func TestExportTrace(t *testing.T) {

var spbs spans
for _, s := range te.spans {
spbs = append(spbs, protoFromSpanData(s, "testproject", nil))
spbs = append(spbs, protoFromSpanData(s, "testproject", nil, defaultUserAgent))
}
sort.Sort(spbs)

Expand Down Expand Up @@ -396,7 +396,7 @@ func TestExportTraceWithMonitoredResource(t *testing.T) {
mr := createGCEInstanceMonitoredResource()

for _, s := range te.spans {
gceSpbs = append(gceSpbs, protoFromSpanData(s, "testproject", mr))
gceSpbs = append(gceSpbs, protoFromSpanData(s, "testproject", mr, defaultUserAgent))
}

for _, span := range gceSpbs {
Expand All @@ -410,7 +410,7 @@ func TestExportTraceWithMonitoredResource(t *testing.T) {
mr = createGKEContainerMonitoredResource()

for _, s := range te.spans {
gkeSpbs = append(gkeSpbs, protoFromSpanData(s, "testproject", mr))
gkeSpbs = append(gkeSpbs, protoFromSpanData(s, "testproject", mr, defaultUserAgent))
}

for _, span := range gkeSpbs {
Expand All @@ -427,7 +427,7 @@ func TestExportTraceWithMonitoredResource(t *testing.T) {
var awsEc2Spbs spans
mr = createAWSEC2MonitoredResource()
for _, s := range te.spans {
awsEc2Spbs = append(awsEc2Spbs, protoFromSpanData(s, "testproject", mr))
awsEc2Spbs = append(awsEc2Spbs, protoFromSpanData(s, "testproject", mr, defaultUserAgent))
}

for _, span := range awsEc2Spbs {
Expand Down Expand Up @@ -500,7 +500,7 @@ func BenchmarkProto(b *testing.B) {
}
var x int
for i := 0; i < b.N; i++ {
s := protoFromSpanData(sd, `testproject`, nil)
s := protoFromSpanData(sd, `testproject`, nil, defaultUserAgent)
x += len(s.Name)
}
if x == 0 {
Expand Down
38 changes: 36 additions & 2 deletions trace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func TestTraceSpansBufferMaxBytes(t *testing.T) {
exported++
}
for i := 0; i < 10; i++ {
e.ExportSpan(makeSampleSpanData())
e.ExportSpan(makeSampleSpanData(""))
}
close(waitCh)
e.Flush()
Expand All @@ -165,13 +165,47 @@ func TestTraceSpansBufferMaxBytes(t *testing.T) {
}
}

func makeSampleSpanData() *trace.SpanData {
func TestTraceSpansUserAgent(t *testing.T) {
e := newTraceExporterWithClient(Options{
UserAgent: "OpenCensus Service",
Context: context.Background(),
Timeout: 10 * time.Millisecond,
}, nil)

var got string
// set user-agent attribute based on provided option
e.uploadFn = func(spans []*tracepb.Span) {
got = spans[0].Attributes.AttributeMap[agentLabel].GetStringValue().Value
}
e.ExportSpan(makeSampleSpanData(""))
e.Flush()
if want := "OpenCensus Service"; want != got {
t.Fatalf("UserAgent Attribute = %q; want %q", got, want)
}

// if user-agent is already set, do not override
e.uploadFn = func(spans []*tracepb.Span) {
got = spans[0].Attributes.AttributeMap[agentLabel].GetStringValue().Value
}
e.ExportSpan(makeSampleSpanData("My Test Application"))
e.Flush()
if want := "My Test Application"; want != got {
t.Fatalf("UserAgent Attribute = %q; want %q", got, want)
}
}

func makeSampleSpanData(userAgent string) *trace.SpanData {
sd := &trace.SpanData{
Annotations: make([]trace.Annotation, 32),
Links: make([]trace.Link, 32),
MessageEvents: make([]trace.MessageEvent, 128),
Attributes: make(map[string]interface{}),
}

if userAgent != "" {
sd.Attributes[agentLabel] = userAgent
}

for i := 0; i < 32; i++ {
sd.Attributes[fmt.Sprintf("attribute-%d", i)] = ""
}
Expand Down