@@ -16,15 +16,16 @@ package validation
1616
1717import (
1818 "context"
19- "fmt"
2019 "reflect"
2120 "regexp"
21+ "sync"
2222 "time"
2323
2424 "github.com/golang/protobuf/ptypes"
25-
2625 "google.golang.org/genproto/googleapis/devtools/cloudtrace/v2"
27- "google.golang.org/genproto/googleapis/rpc/errdetails"
26+ genprotoStatus "google.golang.org/genproto/googleapis/rpc/status"
27+ "google.golang.org/grpc/codes"
28+ "google.golang.org/grpc/status"
2829)
2930
3031const (
@@ -56,57 +57,98 @@ var (
5657 agentRegex = regexp .MustCompile (`^opentelemetry-[a-zA-Z]+ [0-9]+\.[0-9]+\.[0-9]+; google-cloud-trace-exporter [0-9]+\.[0-9]+\.[0-9]+$` )
5758)
5859
60+ // SpanData wraps all the span data on the server into a struct.
61+ type SpanData struct {
62+ // If a batch has a bad span, we don't write batch to memory, but still want
63+ // info on them for summary, so need SpansSummary
64+ SpansSummary []* cloudtrace.Span
65+ UploadedSpanNames map [string ]struct {}
66+ UploadedSpans []* cloudtrace.Span
67+ Mutex sync.RWMutex
68+ }
69+
5970// ValidateSpans checks that the spans conform to the API requirements.
6071// That is, required fields are present, and optional fields are of the correct form.
61- func ValidateSpans (requestName string , spans ... * cloudtrace.Span ) error {
72+ // If any violations are detected, the errors will be added to the result table.
73+ func ValidateSpans (requestName string , spanData * SpanData , spans ... * cloudtrace.Span ) error {
74+ var overallError error
75+ currentRequestSpanNames := make (map [string ]struct {})
76+
6277 for _ , span := range spans {
78+ var currentError error
79+
6380 // Validate required fields are present and semantically make sense.
6481 if err := CheckForRequiredFields (requiredFields , reflect .ValueOf (span ), requestName ); err != nil {
65- return err
82+ addSpanToSummary (& spanData .SpansSummary , span , err )
83+ currentError = err
6684 }
67- if err := validateName (span .Name ); err != nil {
68- return err
85+ if err := validateName (span .Name , spanData .UploadedSpanNames , currentRequestSpanNames ); err != nil {
86+ addSpanToSummary (& spanData .SpansSummary , span , err )
87+ currentError = err
6988 }
7089 if err := validateTimeStamps (span ); err != nil {
71- return err
90+ addSpanToSummary (& spanData .SpansSummary , span , err )
91+ currentError = err
7292 }
7393 if err := validateDisplayName (span .DisplayName ); err != nil {
74- return err
94+ addSpanToSummary (& spanData .SpansSummary , span , err )
95+ currentError = err
7596 }
7697
7798 // Validate that if optional fields are present, they conform to the API.
7899 if err := validateAttributes (span .Attributes , maxAttributes ); err != nil {
79- return err
100+ addSpanToSummary (& spanData .SpansSummary , span , err )
101+ currentError = err
80102 }
81103 if err := validateTimeEvents (span .TimeEvents ); err != nil {
82- return err
104+ addSpanToSummary (& spanData .SpansSummary , span , err )
105+ currentError = err
83106 }
84107 if err := validateLinks (span .Links ); err != nil {
85- return err
108+ addSpanToSummary (& spanData .SpansSummary , span , err )
109+ currentError = err
86110 }
111+
112+ if currentError == nil {
113+ addSpanToSummary (& spanData .SpansSummary , span , nil )
114+ } else {
115+ overallError = currentError
116+ }
117+ }
118+
119+ if overallError != nil {
120+ return overallError
87121 }
122+
88123 return nil
89124}
90125
91- // AddSpans adds the given spans to the list of uploaded spans as long as there are no duplicate names.
92- // If a duplicate span name is detected, it will not be written, and an error is returned.
93- func AddSpans (uploadedSpans * []* cloudtrace.Span , uploadedSpanNames map [string ]struct {}, spans ... * cloudtrace.Span ) error {
94- br := & errdetails.ErrorInfo {}
126+ // addSpanToSummary sets the span's status and adds it to the summary slice.
127+ func addSpanToSummary (spanSummary * []* cloudtrace.Span , span * cloudtrace.Span , err error ) {
128+ setSpanStatus (span , err )
129+ * spanSummary = append (* spanSummary , span )
130+ }
95131
96- for _ , span := range spans {
97- if _ , ok := uploadedSpanNames [span .Name ]; ok {
98- br .Reason = span .Name
99- st , err := statusDuplicateSpanName .WithDetails (br )
100- if err != nil {
101- panic (fmt .Sprintf ("unexpected error attaching metadata: %v" , err ))
102- }
103- return st .Err ()
132+ func setSpanStatus (span * cloudtrace.Span , err error ) {
133+ if err == nil {
134+ span .Status = & genprotoStatus.Status {
135+ Code : int32 (codes .OK ),
136+ Message : "OK" ,
137+ }
138+ } else {
139+ span .Status = & genprotoStatus.Status {
140+ Code : int32 (status .Convert (err ).Code ()),
141+ Message : status .Convert (err ).Message (),
104142 }
105- * uploadedSpans = append (* uploadedSpans , span )
106- uploadedSpanNames [span .Name ] = struct {}{}
107143 }
144+ }
108145
109- return nil
146+ // AddSpans adds the given spans to the list of uploaded spans.
147+ func AddSpans (spanData * SpanData , spans ... * cloudtrace.Span ) {
148+ for _ , span := range spans {
149+ spanData .UploadedSpans = append (spanData .UploadedSpans , span )
150+ spanData .UploadedSpanNames [span .Name ] = struct {}{}
151+ }
110152}
111153
112154// Delay will block for the specified amount of time.
@@ -137,13 +179,23 @@ func validateDisplayName(displayName *cloudtrace.TruncatableString) error {
137179 return nil
138180}
139181
140- // validateName verifies that the span is of the form:
182+ // validateName verifies that the span name is not a duplicate, and is of the form:
141183// projects/{project_id}/traces/{trace_id}/spans/{span_id}
142184// where trace_id is a 32-char hex encoding, and span_id is a 16-char hex encoding.
143- func validateName (name string ) error {
185+ func validateName (name string , spanNames map [string ]struct {}, currentRequestSpanNames map [string ]struct {}) error {
186+ if _ , ok := spanNames [name ]; ok {
187+ return statusDuplicateSpanName
188+ }
189+
190+ if _ , ok := currentRequestSpanNames [name ]; ok {
191+ return statusDuplicateSpanName
192+ }
193+
144194 if ! spanNameRegex .MatchString (name ) {
145195 return statusInvalidSpanName
146196 }
197+
198+ currentRequestSpanNames [name ] = struct {}{}
147199 return nil
148200}
149201
0 commit comments