-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Add WithClock TracerProviderOption #2052
Changes from 33 commits
5d0f379
a8a08d4
08351ed
6aaec19
33e4408
af73898
401d125
d41de7d
39af369
049cd2e
dcc82b0
e80a1fe
8340380
3f1a87d
f506ac3
b9250b3
c7c088b
d1a5ae0
51e400e
d5514dd
04ef13c
8075f98
4f68db3
beba0cb
1cb2208
3aee73f
ccb0eda
bffe943
e2d7e8f
6173f5c
a81a903
670c671
5206913
9ef9a5a
e924492
2f86f78
094b868
498dc4f
1139987
bfc222e
e5c647d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,7 +27,6 @@ import ( | |
"go.opentelemetry.io/otel/trace" | ||
|
||
"go.opentelemetry.io/otel/sdk/instrumentation" | ||
"go.opentelemetry.io/otel/sdk/internal" | ||
"go.opentelemetry.io/otel/sdk/resource" | ||
) | ||
|
||
|
@@ -112,9 +111,6 @@ type span struct { | |
// name is the name of this span. | ||
name string | ||
|
||
// startTime is the time at which this span was started. | ||
startTime time.Time | ||
|
||
// endTime is the time at which this span was ended. It contains the zero | ||
// value of time.Time until the span is ended. | ||
endTime time.Time | ||
|
@@ -154,6 +150,9 @@ type span struct { | |
|
||
// spanLimits holds the limits to this span. | ||
spanLimits SpanLimits | ||
|
||
// stopwatch holds the Stopwatch returned by Clock.Start method | ||
stopwatch Stopwatch | ||
} | ||
|
||
var _ trace.Span = &span{} | ||
|
@@ -175,7 +174,7 @@ func (s *span) IsRecording() bool { | |
s.mu.Lock() | ||
defer s.mu.Unlock() | ||
|
||
return !s.startTime.IsZero() && s.endTime.IsZero() | ||
return !s.startTime().IsZero() && s.endTime.IsZero() | ||
} | ||
|
||
// SetStatus sets the status of the Span in the form of a code and a | ||
|
@@ -227,7 +226,7 @@ func (s *span) End(options ...trace.SpanEndOption) { | |
|
||
// Store the end time as soon as possible to avoid artificially increasing | ||
// the span's duration in case some operation below takes a while. | ||
et := internal.MonotonicEndTime(s.startTime) | ||
et := s.stopwatch.Started().Add(s.stopwatch.Stop()) | ||
Frefreak marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// Do relative expensive check now that we have an end time and see if we | ||
// need to do any more processing. | ||
|
@@ -364,7 +363,7 @@ func (s *span) SpanKind() trace.SpanKind { | |
func (s *span) StartTime() time.Time { | ||
s.mu.Lock() | ||
defer s.mu.Unlock() | ||
return s.startTime | ||
return s.startTime() | ||
} | ||
|
||
// EndTime returns the time this span ended. For spans that have not yet | ||
|
@@ -497,7 +496,7 @@ func (s *span) snapshot() ReadOnlySpan { | |
sd.resource = s.resource | ||
sd.spanContext = s.spanContext | ||
sd.spanKind = s.spanKind | ||
sd.startTime = s.startTime | ||
sd.startTime = s.startTime() | ||
sd.status = s.status | ||
sd.childSpanCount = s.childSpanCount | ||
|
||
|
@@ -555,8 +554,14 @@ func (s *span) addChild() { | |
|
||
func (*span) private() {} | ||
|
||
func (s *span) startTime() time.Time { | ||
return s.stopwatch.Started() | ||
} | ||
|
||
func startSpanInternal(ctx context.Context, tr *tracer, name string, o *trace.SpanConfig) *span { | ||
span := &span{} | ||
span := &span{ | ||
stopwatch: nilStopwatch{}, | ||
} | ||
|
||
provider := tr.provider | ||
|
||
|
@@ -614,10 +619,10 @@ func startSpanInternal(ctx context.Context, tr *tracer, name string, o *trace.Sp | |
|
||
startTime := o.Timestamp() | ||
if startTime.IsZero() { | ||
startTime = time.Now() | ||
span.stopwatch = tr.provider.clock.Stopwatch() | ||
} else { | ||
span.stopwatch = standardStopwatch(startTime) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the user supplies their own start time it shouldn't mean the user supplied clock is not used. This seems like a design flaw. Should we updated the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess a given Considering a "offset" clock, if user provide |
||
} | ||
span.startTime = startTime | ||
|
||
span.spanKind = trace.ValidateSpanKind(o.SpanKind()) | ||
span.name = name | ||
span.parent = psc | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// 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 trace | ||
|
||
import ( | ||
"time" | ||
) | ||
|
||
// Clock is the entrypoint for providing time to span's start/end timestamp. | ||
// By default the standard "time" package will be used. User can replace | ||
// it with customized clock implementation (e.g. with additional clock | ||
// synchronization logic) by using the `WithClock` option. | ||
type Clock interface { | ||
MrAlias marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Stopwatch returns a started Stopwatch measuring a time interval. | ||
Stopwatch() Stopwatch | ||
} | ||
|
||
type Stopwatch interface { | ||
Frefreak marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Started returns the time the Stopwatch was started. | ||
Started() time.Time | ||
// Stop stops the stopwatch and returns the duration from when this Stopwatch was started. | ||
// This will only be called once when generating span's end time and should return a positive | ||
// time.Duration in order to ensure the monotonicity of span's start/end time. | ||
Stop() time.Duration | ||
} | ||
|
||
type standardClock struct{} | ||
type standardStopwatch time.Time | ||
type nilStopwatch struct{} | ||
|
||
func defaultClock() Clock { | ||
return standardClock{} | ||
} | ||
|
||
func (standardClock) Stopwatch() Stopwatch { | ||
return standardStopwatch(time.Now()) | ||
} | ||
|
||
func (w standardStopwatch) Started() time.Time { | ||
return time.Time(w) | ||
} | ||
|
||
func (w standardStopwatch) Stop() time.Duration { | ||
return time.Since(time.Time(w)) | ||
} | ||
|
||
func (w nilStopwatch) Started() time.Time { | ||
return time.Time{} | ||
} | ||
|
||
func (w nilStopwatch) Stop() time.Duration { | ||
return 0 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1594,6 +1594,49 @@ func TestAddLinksWithMoreAttributesThanLimit(t *testing.T) { | |
} | ||
} | ||
|
||
type frozenClock struct { | ||
now time.Time | ||
} | ||
type frozenStopwatch struct { | ||
started time.Time | ||
} | ||
|
||
func (f frozenStopwatch) Started() time.Time { | ||
return f.started | ||
} | ||
func (f frozenStopwatch) Stop() time.Duration { | ||
return 0 | ||
} | ||
|
||
// newFrozenClock returns a clock which stops at time t | ||
func newFrozenClock(t time.Time) frozenClock { | ||
return frozenClock{ | ||
now: t, | ||
} | ||
} | ||
|
||
func (c frozenClock) Stopwatch() Stopwatch { | ||
return frozenStopwatch{ | ||
started: c.now, | ||
} | ||
} | ||
|
||
func TestCustomClock(t *testing.T) { | ||
te := NewTestExporter() | ||
now := time.Now() | ||
tp := NewTracerProvider(WithSyncer(te), WithClock(newFrozenClock(now))) | ||
tracer := tp.Tracer("custom-clock") | ||
|
||
_, span := tracer.Start(context.Background(), "test-frozen-clock") | ||
time.Sleep(time.Microsecond * 2) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't look needed. Is there a reason to pause here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIRC in windows if a span ends too fast it can result in the same start time/end time. If that happens then the |
||
span.End() | ||
require.Equal(t, te.Len(), 1, "should only have one span") | ||
|
||
got := te.Spans()[0] | ||
assert.Equal(t, now, got.StartTime(), "StartTime should return the frozen time") | ||
assert.Equal(t, now, got.EndTime(), "EndTime should return the frozen time") | ||
} | ||
|
||
type stateSampler struct { | ||
prefix string | ||
f func(trace.TraceState) trace.TraceState | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: It seems like this portion of the docs could be moved to their respective types/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated. I feels like the existing types' comment basically says the same thing so I remove most of the docs here.