Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proof-of-concept: OTel Resource scope and namespace API #427

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Cleanups: remove ContextWithScope and scope.Empty
  • Loading branch information
jmacd committed Jan 11, 2020
commit 3909dc9e673217c5f0556edd2ccde46926857b7b
25 changes: 7 additions & 18 deletions api/context/scope/current.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ import (
"go.opentelemetry.io/otel/api/trace"
)

// ContextWithScope returns a context with a new current Scope. The
// active Scope's resources will be implicitly associated with metric
// events that happen in the returned context.
// InContext returns a context with this Scope current. The current
// Scope's resources will be implicitly associated with metric events
// that happen in the returned context.
//
// When using a Scope's Tracer() or Meter() handle for an API method
// call, the Scope is automatically applied, making it the current
// Scope for the resulting call.
func ContextWithScope(ctx context.Context, sc Scope) context.Context {
return internal.SetScopeImpl(ctx, sc.scopeImpl)
// Scope in the context of the resulting call.
func (s Scope) InContext(ctx context.Context) context.Context {
return internal.SetScopeImpl(ctx, s)
}

// Current returns the Scope associated with a Context as set by
Expand All @@ -48,7 +48,7 @@ func Current(ctx context.Context) Scope {
}
return Scope{}
}
return Scope{internal.ScopeImpl(ctx).(*scopeImpl)}
return impl.(Scope)
}

// Labels is a convenience method to return a LabelSet given the
Expand Down Expand Up @@ -91,14 +91,3 @@ func UnnamedMeter(ti metric.MeterSDK) metric.Meter {
func NamedMeter(ti metric.MeterSDK, ns core.Namespace) metric.Meter {
return WithMeter(ti).WithNamespace(ns).Meter()
}

// InContext returns a context for this scope. Uses of the global
// Tracer methods (e.g., trace.Start) and Meter methods (e.g.,
// metric.NewInt64Counter) will be constructed using the namespace
// from this scope.
//
// Uses of the global meter.RecordBatch will use the resources of this
// scope from the context.
func (s Scope) InContext(ctx context.Context) context.Context {
return ContextWithScope(ctx, s)
}
7 changes: 1 addition & 6 deletions api/context/scope/scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,6 @@ func (p *Provider) New() Scope {
return Scope{si}
}

// Empty is an empty Scope, equivalent to Scope{}.
func Empty() Scope {
return Scope{}
}

func (s Scope) clone() Scope {
var ri scopeImpl
if s.scopeImpl != nil {
Expand Down Expand Up @@ -214,7 +209,7 @@ func (s *scopeImpl) enterScope(ctx context.Context) context.Context {
if o.scopeImpl == s {
return ctx
}
return ContextWithScope(ctx, Scope{s})
return Scope{s}.InContext(ctx)
}

func (s *scopeImpl) name(n string) core.Name {
Expand Down
17 changes: 7 additions & 10 deletions api/global/internal/benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,10 @@ func (*benchFixture) FinishedCollection() {
func BenchmarkGlobalInt64CounterAddNoSDK(b *testing.B) {
internal.ResetForTest()

sdk := global.Scope().WithNamespace("test").Meter()
scx := global.Scope().WithNamespace("test").AddResources(key.String("A", "B"))
ctx := scx.InContext(context.Background())

ctx := scope.ContextWithScope(
context.Background(),
scope.Empty().AddResources(key.String("A", "B")))

cnt := sdk.NewInt64Counter("int64.counter")
cnt := scx.Meter().NewInt64Counter("int64.counter")

b.ResetTimer()

Expand All @@ -87,12 +84,12 @@ func BenchmarkGlobalInt64CounterAddWithSDK(b *testing.B) {
// Comapare with BenchmarkInt64CounterAdd() in ../../sdk/meter/benchmark_test.go
fix := newFixture(b)

ctx := scope.WithMeter(fix.sdk).
scx := scope.WithMeter(fix.sdk).
WithNamespace("test").
AddResources(key.String("A", "B")).
InContext(context.Background())
AddResources(key.String("A", "B"))
ctx := scx.InContext(context.Background())

cnt := scope.Current(ctx).Meter().NewInt64Counter("int64.counter")
cnt := scx.Meter().NewInt64Counter("int64.counter")

b.ResetTimer()

Expand Down
2 changes: 1 addition & 1 deletion api/global/internal/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func Scope() scope.Scope {
if sc, ok := (*atomic.Value)(atomic.LoadPointer(&internal.GlobalScope)).Load().(scope.Scope); ok {
return sc
}
return scope.Empty()
return scope.Scope{}
}

// SetScope is the internal implementation for global.SetScope().
Expand Down
178 changes: 178 additions & 0 deletions example/scope/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
// Copyright 2019, 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 main

import (
"context"
"log"

"go.opentelemetry.io/otel/api/context/scope"
"go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/global"
"go.opentelemetry.io/otel/api/key"
"go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/api/trace"
metricstdout "go.opentelemetry.io/otel/exporter/metric/stdout"
tracestdout "go.opentelemetry.io/otel/exporter/trace/stdout"
"go.opentelemetry.io/otel/sdk/metric/controller/push"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)

const (
namespace = "ex.com"
)

var (
environmentKey1 = key.New("environment1")
environmentKey2 = key.New("environment2")

resourceKey1 = key.New("resource1")
resourceKey2 = key.New("resource2")

attrKey1 = key.New("attribute1")
attrKey2 = key.New("attribute2")

// Note: metrics are allocated statically. They use the
// global scope's namespace when it is initialized.
counter1 = metric.NewFloat64Counter(
"counter1",
metric.WithKeys(attrKey1, attrKey2),
)
gauge1 = metric.NewFloat64Gauge(
"gauge1",
metric.WithKeys(attrKey1, attrKey2),
)
)

// start sets the global scope with the configured tracer, meter, and resources.
func start() func() {
tracer := initTracer()
meter := initMeter()

telemetry := scope.
WithTracer(tracer).
WithMeter(meter.Meter()).
WithNamespace(namespace).
AddResources(
environmentKey1.String("ENV1"),
environmentKey2.String("ENV2"),
)

global.SetScope(telemetry)

return func() {
meter.Stop()
}
}

func main() {
defer start()()

// Start with no telemetry state
ctx := context.Background()

// Add scoped resources. These are on top of the global resources.
ctx = scope.Current(ctx).AddResources(
resourceKey1.String("res1"),
resourceKey2.String("res2"),
).InContext(ctx)

// Now consider four ways to add "attrKey1" and "attrKey2" attributes
// to a pair of metric events.

////////////////////////////////////////////////////////////
// 1 As a batch, labels passed at the call site

// Using the Meter() from a scope ensures that scope's
// resources are attached.
scope.Current(ctx).Meter().RecordBatch(ctx, []core.KeyValue{
attrKey1.String("val1"),
attrKey2.String("val2"),
},
counter1.Measurement(1),
gauge1.Measurement(2),
)
////////////////////////////////////////////////////////////
// 2 Individual events, labels passed a the call site

// The batch could be written as two events:
counter1.Add(ctx, 1, attrKey1.String("val1"), attrKey2.String("val2"))
gauge1.Set(ctx, 2, attrKey1.String("val1"), attrKey2.String("val2"))

////////////////////////////////////////////////////////////
// 3 By placing the labels in the current resource scope

// Instead of repeating the two attributes above, and where
// LabelSets are currently specified, use scope to introduce local resources:
if true {
ctx := scope.Current(ctx).AddResources(
attrKey1.String("val1"),
attrKey2.String("val2"),
).InContext(ctx)

// Now the "LabelSet" is part of the resource scope.
counter1.Add(ctx, 1)
gauge1.Set(ctx, 2)
}

////////////////////////////////////////////////////////////
// 4 By starting a span with corresponding attributes, which
// enter the current resource scope.

// Creating a new span updates the scope with the span
// attributes as resources.
ctx, span := trace.Start(
ctx,
"a_span",
trace.WithAttributes(
attrKey1.String("val1"),
attrKey2.String("val2"),
),
)
defer span.End()

// These metric events automatically have the current scope's resources.
counter1.Add(ctx, 1)
gauge1.Set(ctx, 2)
}

// initMeter configures the tracing SDK.
func initTracer() trace.TracerSDK {
var err error
exp, err := tracestdout.NewExporter(tracestdout.Options{PrettyPrint: false})
if err != nil {
log.Panicf("failed to initialize trace stdout exporter %v", err)
return nil

}
tri, err := sdktrace.NewTracer(sdktrace.WithSyncer(exp),
sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}))
if err != nil {
log.Panicf("failed to initialize trace provider %v", err)
}
return tri
}

// initMeter configures the metrics SDK.
func initMeter() *push.Controller {
pusher, err := metricstdout.NewExportPipeline(metricstdout.Config{
Quantiles: []float64{0.5, 0.9, 0.99},
PrettyPrint: false,
})
if err != nil {
log.Panicf("failed to initialize metric stdout exporter %v", err)
}
return pusher
}
2 changes: 1 addition & 1 deletion plugin/httptrace/clienttrace.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ type clientTracer struct {
func NewClientTrace(ctx context.Context, scx scope.Scope) *httptrace.ClientTrace {
scx = scx.WithNamespace("go.opentelemetry.io/otel/plugin/httptrace")
ct := &clientTracer{
Context: scope.ContextWithScope(ctx, scx),
Context: scx.InContext(ctx),
activeHooks: make(map[string]trace.Span),
}

Expand Down
Loading