Skip to content

Commit

Permalink
[pdata] Add a documentation with naming conventions
Browse files Browse the repository at this point in the history
Co-authored-by: Pablo Baeyens <pbaeyens31+github@gmail.com>
  • Loading branch information
dmitryax and mx-psi committed Oct 10, 2022
1 parent 06db057 commit daef695
Showing 1 changed file with 214 additions and 0 deletions.
214 changes: 214 additions & 0 deletions pdata/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
# Pipeline data (pdata)

Pipeline data (pdata) implements data structures that represent telemetry data in-memory. All data received
is converted into this format, travels through the pipeline in this format, and is converted from this format by
exporters when sending.

Current implementation primarily uses OTLP protobuf structs as the underlying data structures for many of the
declared structs. We keep a pointer to OTLP protobuf in the "orig" member field. This allows efficient translation
to/from OTLP wire protocol. The underlying data structure is kept private so that we are free to make changes to it
in the future.

API provided by pdata ensures that no underlying data can be used in more than one place. Each pdata instance cannot
contain a reference to an object that is already used somewhere else.

## API naming convention

### Protobuf message representation in pdata

Packages of pdata are not following names of the protobuf packages. pdata has a package per telemetry type starting
with `p`, e.g. `ptace`, and `pcommon` package with API for other protobuf definitions from `common` and `resource`
protobuf packages.

Pipeline data structs SHOULD be based on the names of the underlying OTLP protobuf messages. Data types for
protobuf messages defined as part of another message SHOULD include the owner's name as a prefix. Exceptions to these
rules are possible, but not encouraged. Another name can be chosen for brevity or if the struct provides a completely
different interface to manipulate the underlying data. The following exceptions are currently accepted:

- `plog.Logs` based on `LogsData` protobuf message.
- `pmetric.Metrics` based on `MetricsData` protobuf message.
- `ptrace.Traces` based on `TracesData` protobuf message.
- `pcommon.Slice` based on `ArrayValue` protobuf message.
- `pcommon.Map` based on `KeyValueList` protobuf message.
- `pcommon.Value` based on `AnyValue` protobuf message.

Each pdata struct MUST have an initialization function starting with `New` prefix. Usage of zero-initialized is
prohibited and can cause a panic. Each pdata struct MUST provide the following methods:

- `MoveTo(<type>)`: moves all the data from one struct instance to another. Destination instance is overridden, the
source instance is re-initialized as a new empty instance.
- `CopyTo(<type>)`: copies all the data from one struct instance to another. Destination instance is overridden, the
source instance is not modified.

### Protobuf fields representation in pdata

### Singular fields

Each pdata struct based on a protobuf message SHOULD have getter methods for every protobuf field called the same as
the protobuf field but in CamelCase. Exceptions to this rule are allowed if exposing a field is not desirable in pdata.
pdata can also provide a different interface to manipulate fields without exposing them explicitly as it's done in
`pcommon.Map`. If protobuf field is of scalar or enum type, the corresponding pdata struct MUST provide a setter
method with `Set` prefix.

Some fields of scalar type in protobuf message MAY be represented by another pdata type providing an additional
interface, for example `pcommon.Timestamp`.

### Optional fields

Each optional field in protobuf message exposed in pdata MUST have an additional method with `Has` prefix to
determine if the field is set.

### OneOf fields

A pdata struct representing a protobuf message with a oneof field MUST provide getter and setter methods for each
option of the oneof field. If a oneof field option is another protobuf message, setter name MUST include `Empty`
word, such setter MUST set the oneof field to an empty initialized struct and return it. The following conditions
define if name the oneof field has to be included in the setter and getter methods names:

1. If a oneof field represents the whole protobuf message, name of the oneof field itself MAY be omitted in setter
and getter methods. For example:

```go
func (ms Metric) Sum() Sum
func (ms Metric) SetEmptySum() Sum
```

2. If a oneof field is relevant only to a particular field of the message, the pdata getter and setter methods MUST
include name of the oneof field along with name of the option. For example:

```go
func (ms NumberDataPoint) IntValue() int64
func (ms NumberDataPoint) SetIntValue(v int64)
```

Additionally, the pdata struct MUST provide a type getter for every exposed oneof protobuf field. Name of the getter
MUST be `Type` for oneof fields representing the whole protobuf message (1), or `<oneof field>Type` for oneof fields
relevant only to a particular field. The function MUST return an `int32` enum type called `<struct name>Type` for (1)
and `<struct name><oneof field>Type` for (2). For example:

- `func (ms Metric) Type() MetricType` (1)
- `func (ms NumberDataPoint) ValueType() NumberDataPointValueType` (2)

The enum constants MUST be called the same as the type with a suffix named after the oneof option, e.g.:

- `MetricTypeSum` (1)
- `NumberDataPointValueTypeString` (2)

An unset oneof protobuf value MUST be represented by a pdata constant with `Empty` suffix, e.g.:

- `MetricTypeEmpty` (1)
- `NumberDataPointValueTypeEmpty` (2)

The pdata enum type SHOULD have `String()` method returning a string value of the corresponding oneof field option.

### Repeated fields

Repeated protobuf fields MUST be represented as another pdata struct with `Slice` suffix unless the slice message is
represented in pdata through a modified interface, e.g. `pdata.Map`. Each slice MUST have the following methods:

- `Len()`: returns length of the slice.
- `At(int) <element type>`: returns an element from the slice by its index, can cause "index out of range" panic.
- `CopyTo(<slice type>)`: copies the slice to another slice of the same type overriding the destination.
- `EnsureCapacity(int)`: ensures that the slice have at least the requested capacity.
- `AppendEmpty() <element type>`: adds another slice element with an empty initialized value and returns it.
- `Sort(func(a, b <element type>) bool) <slice type>`: sorts the slice using the function and returns itself.

### Repeated scalar fields

Repeated scalar protobuf fields MUST be represented as another pdata type wrapping native go slices. Name of the
type MUST include name of the primitive data type ending with `Slice`, e.g. `UInt64Slice`. Each pdata type MUST have
the following methods:

- `AsRaw() []<primitive type>`: returns copy of the underlying go slice.
- `FromRaw(val []<primitive type>)`: copies a go slice into the pdata wrapper instance.
- `Len()`: returns length of the slice.
- `At(int) <primitive type>`: returns an element from the slice by its index, can cause "index out of range" panic.
- `EnsureCapacity(int)`: ensures that the slice have at least the requested capacity.
- `AppendEmpty() <primitive type>`: adds another slice element with a zero value.
- `CopyTo(<slice type>)`: copies the slice to another slice of the same type overriding the destination.
- `MoveTo(<slice type>)`: move the slice to another slice of the same type. The destination is overridden, the
source is set to an empty slice.

### Enum fields

Each protobuf enum field exposed in pdata MUST have a type declared with underlying `int64` type. Name of the type MUST
follow the same rules as for struct type names representing protobuf messages:

- If a protobuf enum is defined on the package level, pdata type SHOULD have the same name.
- If a protobuf enum is defined as part of another message, pdata type SHOULD include the protobuf message name as a
prefix.

Constants defined for the pdata int64 type MUST have the same names and numeric values defined in protobuf. Names
of the constants must be translated from ALL_CAPS_SNAKE_CASE to CamelCase.

The pdata enum type SHOULD have `String()` method returning a string value for each constant.

Example of a protobuf enum definition in pdata:

```golang
type AggregationTemporality int32
const (
AggregationTemporalityUnspecified = AggregationTemporality(otlpmetrics.AggregationTemporality_AGGREGATION_TEMPORALITY_UNSPECIFIED)
AggregationTemporalityDelta = AggregationTemporality(otlpmetrics.AggregationTemporality_AGGREGATION_TEMPORALITY_DELTA)
AggregationTemporalityCumulative = AggregationTemporality(otlpmetrics.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE)
)
```

### Flags

Flags fields are typically defined in protobuf as `uint32` fields representing 32 distinct boolean flags. The
exposed protobuf flags fields MUST be defined as new types with `uint32` underlying type and called according to
the same rules as struct names representing protobuf messages and other enum types. Each pdata flags type MUST have
an empty variable of the fields type with `Default` prefix. Each flag MUST have a getter method returning a
particular flag and a setter method with `With` prefix returning a new instance of the pdata flags type. Any instance
of the pdata type is immutable since it's just a wrapper over `uint32`.

The following pdata type is used for log record flags field:

```golang
type LogRecordFlags uint32
var DefaultLogRecordFlags = LogRecordFlags(0)
func (ms LogRecordFlags) IsSampled() bool
func (ms LogRecordFlags) WithIsSampled(b bool) LogRecordFlags
```

### Examples

The following protobuf message:

```protobuf
message NumberDataPoint {
reserved 1;
repeated opentelemetry.proto.common.v1.KeyValue attributes = 7;
fixed64 start_time_unix_nano = 2;
fixed64 time_unix_nano = 3;
oneof value {
double as_double = 4;
sfixed64 as_int = 6;
}
repeated Exemplar exemplars = 5;
uint32 flags = 8;
}
```

is represented by the following pdata API

```go
type NumberDataPoint
func NewNumberDataPoint() NumberDataPoint
func (ms NumberDataPoint) MoveTo(dest NumberDataPoint)
func (ms NumberDataPoint) CopyTo(dest NumberDataPoint)
func (ms NumberDataPoint) Attributes() pcommon.Map
func (ms NumberDataPoint) StartTimestamp() pcommon.Timestamp
func (ms NumberDataPoint) SetStartTimestamp(v pcommon.Timestamp)
func (ms NumberDataPoint) Timestamp() pcommon.Timestamp
func (ms NumberDataPoint) SetTimestamp(v pcommon.Timestamp)
func (ms NumberDataPoint) ValueType() NumberDataPointValueType
func (ms NumberDataPoint) DoubleValue() float64
func (ms NumberDataPoint) SetDoubleValue(v float64)
func (ms NumberDataPoint) IntValue() int64
func (ms NumberDataPoint) SetIntValue(v int64)
func (ms NumberDataPoint) Exemplars() ExemplarSlice
func (ms NumberDataPoint) Flags() DataPointFlags
func (ms NumberDataPoint) SetFlags(v DataPointFlags)
```

0 comments on commit daef695

Please sign in to comment.