traces.go raw

   1  // Copyright The OpenTelemetry Authors
   2  // SPDX-License-Identifier: Apache-2.0
   3  
   4  package telemetry
   5  
   6  import (
   7  	"bytes"
   8  	"encoding/json"
   9  	"errors"
  10  	"fmt"
  11  	"io"
  12  )
  13  
  14  // Traces represents the traces data that can be stored in a persistent storage,
  15  // OR can be embedded by other protocols that transfer OTLP traces data but do
  16  // not implement the OTLP protocol.
  17  //
  18  // The main difference between this message and collector protocol is that
  19  // in this message there will not be any "control" or "metadata" specific to
  20  // OTLP protocol.
  21  //
  22  // When new fields are added into this message, the OTLP request MUST be updated
  23  // as well.
  24  type Traces struct {
  25  	// An array of ResourceSpans.
  26  	// For data coming from a single resource this array will typically contain
  27  	// one element. Intermediary nodes that receive data from multiple origins
  28  	// typically batch the data before forwarding further and in that case this
  29  	// array will contain multiple elements.
  30  	ResourceSpans []*ResourceSpans `json:"resourceSpans,omitempty"`
  31  }
  32  
  33  // UnmarshalJSON decodes the OTLP formatted JSON contained in data into td.
  34  func (td *Traces) UnmarshalJSON(data []byte) error {
  35  	decoder := json.NewDecoder(bytes.NewReader(data))
  36  
  37  	t, err := decoder.Token()
  38  	if err != nil {
  39  		return err
  40  	}
  41  	if t != json.Delim('{') {
  42  		return errors.New("invalid TracesData type")
  43  	}
  44  
  45  	for decoder.More() {
  46  		keyIface, err := decoder.Token()
  47  		if err != nil {
  48  			if errors.Is(err, io.EOF) {
  49  				// Empty.
  50  				return nil
  51  			}
  52  			return err
  53  		}
  54  
  55  		key, ok := keyIface.(string)
  56  		if !ok {
  57  			return fmt.Errorf("invalid TracesData field: %#v", keyIface)
  58  		}
  59  
  60  		switch key {
  61  		case "resourceSpans", "resource_spans":
  62  			err = decoder.Decode(&td.ResourceSpans)
  63  		default:
  64  			// Skip unknown.
  65  		}
  66  
  67  		if err != nil {
  68  			return err
  69  		}
  70  	}
  71  	return nil
  72  }
  73  
  74  // ResourceSpans is a collection of ScopeSpans from a Resource.
  75  type ResourceSpans struct {
  76  	// The resource for the spans in this message.
  77  	// If this field is not set then no resource info is known.
  78  	Resource Resource `json:"resource"`
  79  	// A list of ScopeSpans that originate from a resource.
  80  	ScopeSpans []*ScopeSpans `json:"scopeSpans,omitempty"`
  81  	// This schema_url applies to the data in the "resource" field. It does not apply
  82  	// to the data in the "scope_spans" field which have their own schema_url field.
  83  	SchemaURL string `json:"schemaUrl,omitempty"`
  84  }
  85  
  86  // UnmarshalJSON decodes the OTLP formatted JSON contained in data into rs.
  87  func (rs *ResourceSpans) UnmarshalJSON(data []byte) error {
  88  	decoder := json.NewDecoder(bytes.NewReader(data))
  89  
  90  	t, err := decoder.Token()
  91  	if err != nil {
  92  		return err
  93  	}
  94  	if t != json.Delim('{') {
  95  		return errors.New("invalid ResourceSpans type")
  96  	}
  97  
  98  	for decoder.More() {
  99  		keyIface, err := decoder.Token()
 100  		if err != nil {
 101  			if errors.Is(err, io.EOF) {
 102  				// Empty.
 103  				return nil
 104  			}
 105  			return err
 106  		}
 107  
 108  		key, ok := keyIface.(string)
 109  		if !ok {
 110  			return fmt.Errorf("invalid ResourceSpans field: %#v", keyIface)
 111  		}
 112  
 113  		switch key {
 114  		case "resource":
 115  			err = decoder.Decode(&rs.Resource)
 116  		case "scopeSpans", "scope_spans":
 117  			err = decoder.Decode(&rs.ScopeSpans)
 118  		case "schemaUrl", "schema_url":
 119  			err = decoder.Decode(&rs.SchemaURL)
 120  		default:
 121  			// Skip unknown.
 122  		}
 123  
 124  		if err != nil {
 125  			return err
 126  		}
 127  	}
 128  	return nil
 129  }
 130  
 131  // ScopeSpans is a collection of Spans produced by an InstrumentationScope.
 132  type ScopeSpans struct {
 133  	// The instrumentation scope information for the spans in this message.
 134  	// Semantically when InstrumentationScope isn't set, it is equivalent with
 135  	// an empty instrumentation scope name (unknown).
 136  	Scope *Scope `json:"scope"`
 137  	// A list of Spans that originate from an instrumentation scope.
 138  	Spans []*Span `json:"spans,omitempty"`
 139  	// The Schema URL, if known. This is the identifier of the Schema that the span data
 140  	// is recorded in. To learn more about Schema URL see
 141  	// https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
 142  	// This schema_url applies to all spans and span events in the "spans" field.
 143  	SchemaURL string `json:"schemaUrl,omitempty"`
 144  }
 145  
 146  // UnmarshalJSON decodes the OTLP formatted JSON contained in data into ss.
 147  func (ss *ScopeSpans) UnmarshalJSON(data []byte) error {
 148  	decoder := json.NewDecoder(bytes.NewReader(data))
 149  
 150  	t, err := decoder.Token()
 151  	if err != nil {
 152  		return err
 153  	}
 154  	if t != json.Delim('{') {
 155  		return errors.New("invalid ScopeSpans type")
 156  	}
 157  
 158  	for decoder.More() {
 159  		keyIface, err := decoder.Token()
 160  		if err != nil {
 161  			if errors.Is(err, io.EOF) {
 162  				// Empty.
 163  				return nil
 164  			}
 165  			return err
 166  		}
 167  
 168  		key, ok := keyIface.(string)
 169  		if !ok {
 170  			return fmt.Errorf("invalid ScopeSpans field: %#v", keyIface)
 171  		}
 172  
 173  		switch key {
 174  		case "scope":
 175  			err = decoder.Decode(&ss.Scope)
 176  		case "spans":
 177  			err = decoder.Decode(&ss.Spans)
 178  		case "schemaUrl", "schema_url":
 179  			err = decoder.Decode(&ss.SchemaURL)
 180  		default:
 181  			// Skip unknown.
 182  		}
 183  
 184  		if err != nil {
 185  			return err
 186  		}
 187  	}
 188  	return nil
 189  }
 190