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

add initial version of W3C tracestate #887

Closed
wants to merge 8 commits into from
Closed

add initial version of W3C tracestate #887

wants to merge 8 commits into from

Conversation

reyang
Copy link
Contributor

@reyang reyang commented Aug 30, 2018

This is the initial implementation of W3C tracestate in Go.

This is my first time writing program in Go, couple notes/questions:

  1. I want to discuss if want to put thread safety guard on Tracestate.Fork(), or leave it as a responsibility for the API consumer.
  2. Currently the Tracestate object will be copied every time when a new span got created, I plan to implement COW (copy on write) to improve perf.
  3. My next pull request will implement the ochttp integration, which will propagate tracestate in HTTP headers.

@reyang
Copy link
Contributor Author

reyang commented Aug 30, 2018

@rakyll @Ramonza, would you give me some hints? I'm getting the following AppVeyor build error which claims gcc compiler missing:

runtime/cgo
exec: "gcc": executable file not found in %PATH%
FAIL go.opencensus.io/exporter/jaeger [build failed]
FAIL go.opencensus.io/exporter/prometheus [build failed]
FAIL go.opencensus.io/exporter/stackdriver/propagation [build failed]
FAIL go.opencensus.io/exporter/zipkin [build failed]
FAIL go.opencensus.io/internal [build failed]
FAIL go.opencensus.io/plugin/ocgrpc [build failed]
FAIL go.opencensus.io/plugin/ochttp [build failed]
FAIL go.opencensus.io/plugin/ochttp/propagation/b3 [build failed]
FAIL go.opencensus.io/plugin/ochttp/propagation/tracecontext [build failed]
FAIL go.opencensus.io/stats [build failed]
FAIL go.opencensus.io/stats/view [build failed]
FAIL go.opencensus.io/tag [build failed]
FAIL go.opencensus.io/trace [build failed]
FAIL go.opencensus.io/trace/propagation [build failed]
FAIL go.opencensus.io/zpages [build failed]
Command exited with code 2

// TraceState is a tracing-system specific context in a list of key-value pairs.
// TraceState allows different vendors propagate additional information and
// inter-operate with their legacy Id formats.
type Tracestate struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this not just a map?

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Ramonza - @basvanbeek is right, the W3C spec requires tracestate to following a special ordering rule - last modified key-value-pair should appear at the very beginning.

Copy link
Member

Choose a reason for hiding this comment

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

@reyang I think with @Ramonza questioning the choice I think it makes sense to add a comment in the code so it is clear to everybody without needing to deep dive into the specs.

VALUE_FORMAT = `[\x20-\x2b\x2d-\x3c\x3e-\x7e]{0,255}[\x21-\x2b\x2d-\x3c\x3e-\x7e]`
)

var KEY_VALIDATION_RE = regexp.MustCompile(`^(` + KEY_FORMAT + `)$`)
Copy link
Contributor

Choose a reason for hiding this comment

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

Go convention for variables is camelCase

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it, I'm fixing it.

)

const (
KEY_WITHOUT_VENDOR_FORMAT = `[a-z][_0-9a-z\-\*\/]{0,255}`
Copy link
Contributor

Choose a reason for hiding this comment

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

Go convention for consts is camelCase - I think golint will pick this up

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, I'd be glad to change to camelCase.

I did searched the entire repo to see if there is a convention I could follow, and I'm seeing inconsistent conventions. And golint is not part of CI validation (all I see is gofmt). e.g.

In stats/validation.go, we have

const (
	MaxNameLength = 255
)

in vendor\git.apache.org\thrift.git\lib\go\thrift\application_exception.go (although this is 3rd party dependency), we have

const (
	UNKNOWN_APPLICATION_EXCEPTION  = 0
	UNKNOWN_METHOD                 = 1
	INVALID_MESSAGE_TYPE_EXCEPTION = 2
	WRONG_METHOD_NAME              = 3
	BAD_SEQUENCE_ID                = 4
	MISSING_RESULT                 = 5
	INTERNAL_ERROR                 = 6
	PROTOCOL_ERROR                 = 7
)

trace/trace.go Outdated
@@ -289,6 +295,14 @@ func (s *Span) SpanContext() SpanContext {
return s.spanContext
}

// Tracestate returns the Tracestate of the span context.
func (s *Span) Tracestate() *Tracestate {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why isn't this a method on SpanContext?

Copy link
Contributor Author

@reyang reyang Aug 31, 2018

Choose a reason for hiding this comment

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

@Ramonza, I'll move this to SpanContext.

return ts.Set(key, "")
}

func (ts *Tracestate) Set(key string, value string) string {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we should make any part of SpanContext mutable. Can we make Tracestate immutable for now? I think we can figure out how to make it possible to change the Tracestate in a subsequent PR - I think the focus of this initial work should just be on correct propagation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, we can take the step-by-step approach.

To provide more context, census-instrumentation/opencensus-specs#70 (comment) and census-instrumentation/opencensus-specs#131.

Also adding @wu-sheng for awareness.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Ramonza, I've commented out the Tracestate.Remove and Tracestate.Set for now.

Regarding SpanContext, I'm putting a pointer to Tracestate rather than a hardy copy for Tracestate as we would want to have COW (copy-on-write) Tracestate in the future for performance, taking a hard copy is too expensive.

Copy link
Contributor

Choose a reason for hiding this comment

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

Would you mind removing them entirely for now? We in general never keep around commented code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, I'll remove it.

VALUE_VALIDATION_RE.MatchString(ts.value)
}

// TraceState is a tracing-system specific context in a list of key-value pairs.
Copy link
Contributor

Choose a reason for hiding this comment

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

Inconsistent capitalization

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will fix.

Copy link
Contributor

@semistrict semistrict left a comment

Choose a reason for hiding this comment

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

Some further comments. I think we need more test coverage. I would also ideally like to see somewhere where we actually propagate Tracestate. It's most important that we correctly propagate it for now I think so that we don't break the chain of propagation.

return ts.Set(key, "")
}

func (ts *Tracestate) Set(key string, value string) string {
Copy link
Contributor

Choose a reason for hiding this comment

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

Would you mind removing them entirely for now? We in general never keep around commented code.

return true
}

func (ts *Tracestate) Fork() *Tracestate {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should remove this method as well. It only makes sense if Tracestate is mutable.

*/

func (ts *Tracestate) String() string {
return fmt.Sprintf("tracestate%s", ts.entries)
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this will be a useful output for String() - I would recommend just removing the method

Copy link
Contributor

@semistrict semistrict Aug 31, 2018

Choose a reason for hiding this comment

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

Alternatively, I think it would be reasonable for String() to produce a Tracestate header value in the format from the spec.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please refer to census-instrumentation/opencensus-python#238 (comment) where we agreed to separate the in-process representation and the actual HTTP header format.

@@ -510,6 +520,7 @@ func TestSetSpanStatus(t *testing.T) {
TraceID: tid,
SpanID: SpanID{},
TraceOptions: 0x1,
Tracestate: &Tracestate{},
Copy link
Contributor

@semistrict semistrict Aug 31, 2018

Choose a reason for hiding this comment

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

The zero value of *Tracestate (nil) should be a valid empty Tracestate in the spirit of making the zero value useful. So it should not be necessary to set it here.

}

func (ts *Tracestate) Get(key string) string {
for _, entry := range ts.entries {
Copy link
Contributor

@semistrict semistrict Aug 31, 2018

Choose a reason for hiding this comment

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

To make the zero value of *Tracestate useful, let's add

if ts == nil {
return ""
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Should this method distinguish between an empty string value and the absence of a key? If so, perhaps it should return (string, bool)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Empty string means the key doesn't exist, as the W3C spec wouldn't allow empty string as the value.

}

func (ts *TracestateEntry) IsValid() bool {
return keyValidationRegExp.MatchString(ts.key) &&
Copy link
Contributor

Choose a reason for hiding this comment

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

Is an empty key valid? Is an empty value? It seems from the pattern that it is. Should it be? Ideally we would have a test for this.

@reyang
Copy link
Contributor Author

reyang commented Aug 31, 2018

@Ramonza, agree, that's what I've mentioned in the PR description "My next pull request will implement the ochttp integration, which will propagate tracestate in HTTP headers.", I will add the actual propagation and the test cases in the next PR.

@semistrict
Copy link
Contributor

I would prefer to have it in one PR because I don't think just this alone makes sense on its own -- it doesn't add any functionality and I'm worried that if users see it they will be confused as to why its there.

@semistrict
Copy link
Contributor

I am going to look into the AppVeyor failure, it is unrelated.

@reyang
Copy link
Contributor Author

reyang commented Aug 31, 2018

Close this as it is a dup with #890 which @rghetia is working on.

@reyang reyang deleted the tracestate branch September 25, 2018 22:27
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants