Skip to content

Commit

Permalink
add SpanLimits, implement maxNumAttributes and maxNumAttributeLength
Browse files Browse the repository at this point in the history
fix warnings

adjust attributes.dart file, allow limits to set zero

Change rebuildAttribute part of job to attribute.dart

Remove access of span.attributes

Keep old apis
  • Loading branch information
changliu-wk committed Apr 4, 2022
1 parent a57c636 commit 100448a
Show file tree
Hide file tree
Showing 13 changed files with 423 additions and 16 deletions.
1 change: 1 addition & 0 deletions lib/sdk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ export 'src/sdk/trace/span_processors/simple_processor.dart'
export 'src/sdk/trace/tracer.dart' show Tracer;
export 'src/sdk/trace/tracer_provider.dart' show TracerProvider;
export 'src/sdk/trace/trace_state.dart' show TraceState;
export 'src/sdk/trace/span_limits.dart' show SpanLimits;
34 changes: 26 additions & 8 deletions lib/src/api/common/attribute.dart
Original file line number Diff line number Diff line change
@@ -1,30 +1,48 @@
import 'package:opentelemetry/src/sdk/internal/utils.dart';

/// A representation of a single piece of metadata attached to trace span.
class Attribute {
final String key;
final Object value;

/// Create an Attribute from a String value.
Attribute.fromString(this.key, String this.value);
Attribute.fromString(this.key, String this.value) {
Utils.checkArgument(key != null, "key can't be null");
}

/// Create an Attribute from a boolean value.
// ignore: avoid_positional_boolean_parameters
Attribute.fromBoolean(this.key, bool this.value);
Attribute.fromBoolean(this.key, bool this.value) {
Utils.checkArgument(key != null, "key can't be null");
}

/// Create an Attribute from a double-precision floating-point value.
Attribute.fromDouble(this.key, double this.value);
Attribute.fromDouble(this.key, double this.value) {
Utils.checkArgument(key != null, "key can't be null");
}

/// Create an Attribute from an integer value.
Attribute.fromInt(this.key, int this.value);
Attribute.fromInt(this.key, int this.value) {
Utils.checkArgument(key != null, "key can't be null");
}

/// Create an Attribute from a list of String values.
Attribute.fromStringList(this.key, List<String> this.value);
Attribute.fromStringList(this.key, List<String> this.value) {
Utils.checkArgument(key != null, "key can't be null");
}

/// Create an Attribute from a list of boolean values.
Attribute.fromBooleanList(this.key, List<bool> this.value);
Attribute.fromBooleanList(this.key, List<bool> this.value) {
Utils.checkArgument(key != null, "key can't be null");
}

/// Create an Attribute from a list of double-precision floating-point values.
Attribute.fromDoubleList(this.key, List<double> this.value);
Attribute.fromDoubleList(this.key, List<double> this.value) {
Utils.checkArgument(key != null, "key can't be null");
}

/// Create an Attribute from a list of integer values.
Attribute.fromIntList(this.key, List<int> this.value);
Attribute.fromIntList(this.key, List<int> this.value) {
Utils.checkArgument(key != null, "key can't be null");
}
}
3 changes: 3 additions & 0 deletions lib/src/api/common/attributes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ class Attributes {
/// Retrieve the value associated with the Attribute with key [key].
Object get(String key) => _attributes[key];

///
int get length => _attributes.length;

/// Retrieve the keys of all Attributes in this collection.
Iterable<String> get keys => _attributes.keys;

Expand Down
10 changes: 10 additions & 0 deletions lib/src/api/trace/nonrecording_span.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ class NonRecordingSpan implements api.Span {
return;
}

@override
void setAttribute(api.Attribute attribute) {
return;
}

@override
void setAttributes(List<api.Attribute> attributes) {
return;
}

@override
void end() {
return;
Expand Down
5 changes: 5 additions & 0 deletions lib/src/api/trace/span.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:fixnum/fixnum.dart';

import '../../../api.dart' as api;
import '../../../api.dart';

/// A representation of a single operation within a trace.
///
Expand Down Expand Up @@ -50,6 +51,10 @@ abstract class Span {
/// Retrieve metadata included on this span.
api.Attributes get attributes;

void setAttribute(Attribute attribute);

void setAttributes(List<Attribute> attributes);

/// Retrieve the resource on this span.
api.Resource get resource;

Expand Down
7 changes: 7 additions & 0 deletions lib/src/sdk/internal/utils.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class Utils {
///Check argument
// ignore: avoid_positional_boolean_parameters
static void checkArgument(bool isValid, String errorMessage) {
if (!isValid) throw ArgumentError(errorMessage);
}
}
3 changes: 2 additions & 1 deletion lib/src/sdk/trace/exporters/collector_exporter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'opentelemetry/proto/collector/trace/v1/trace_service.pb.dart'
import 'opentelemetry/proto/trace/v1/trace.pb.dart' as pb_trace;
import 'opentelemetry/proto/resource/v1/resource.pb.dart' as pb_resource;
import 'opentelemetry/proto/common/v1/common.pb.dart' as pb_common;
import '../span.dart' as spansdk;

class CollectorExporter implements api.SpanExporter {
Uri uri;
Expand Down Expand Up @@ -74,7 +75,7 @@ class CollectorExporter implements api.SpanExporter {
return rss;
}

pb_trace.Span _spanToProtobuf(api.Span span) {
pb_trace.Span _spanToProtobuf(spansdk.Span span) {
pb_trace.Status_StatusCode statusCode;
switch (span.status.code) {
case api.StatusCode.unset:
Expand Down
85 changes: 82 additions & 3 deletions lib/src/sdk/trace/span.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import 'dart:html';

import 'package:fixnum/fixnum.dart';
import 'package:opentelemetry/src/sdk/trace/span_limits.dart';

import '../../../api.dart' as api;
import '../../../api.dart';

/// A representation of a single operation within a trace.
class Span implements api.Span {
Expand All @@ -9,9 +13,11 @@ class Span implements api.Span {
final api.SpanStatus _status = api.SpanStatus();
final List<api.SpanProcessor> _processors;
final api.Resource _resource;
SpanLimits _spanLimits = SpanLimits();
final api.InstrumentationLibrary _instrumentationLibrary;
Int64 _startTime;
Int64 _endTime;
api.Attributes _attributes;

@override
String name;
Expand All @@ -22,9 +28,15 @@ class Span implements api.Span {
/// Construct a [Span].
Span(this.name, this._spanContext, this._parentSpanId, this._processors,
this._resource, this._instrumentationLibrary,
{api.Attributes attributes}) {
{api.Attributes attributes, SpanLimits spanlimits}) {
_startTime = Int64(DateTime.now().toUtc().microsecondsSinceEpoch);
this.attributes = attributes ?? api.Attributes.empty();

if (spanlimits != null) _spanLimits = spanlimits;
_attributes = Attributes.empty();
if (attributes != null) {
setAttributes(attributes);
}

for (var i = 0; i < _processors.length; i++) {
_processors[i].onStart();
}
Expand Down Expand Up @@ -79,14 +91,81 @@ class Span implements api.Span {
@override
api.Attributes attributes;

@override
void setAttributes(List<Attribute> attributeList) {
if (_spanLimits.maxNumAttributes == 0) return;

for (var i = 0; i < attributeList.length; i++) {
final attr = attributeList[i];
final obj = _attributes.get(attr.key);
//If current attributes.length is equal or greater than maxNumAttributes and
//key is not in current map, drop it.
if (_attributes.length >= _spanLimits.maxNumAttributes && obj == null) {
continue;
}
_attributes.add(_reBuildAttribute(attr));
}
}

@override
void setAttribute(Attribute attr) {
//Don't want to have any attribute
if (_spanLimits.maxNumAttributes == 0) return;

final obj = _attributes.get(attr.key);
//If current attributes.length is equal or greater than maxNumAttributes and
//key is not in current map, drop it.
if (_attributes.length >= _spanLimits.maxNumAttributes && obj == null) {
return;
}
_attributes.add(_reBuildAttribute(attr));
}

/// reBuild an attribute, this way it is tightly coupled with the type we supported,
/// if later we added more types, then we need to change this method.
api.Attribute _reBuildAttribute(api.Attribute attr) {
if (attr.value is String) {
attr = api.Attribute.fromString(
attr.key,
_applyAttributeLengthLimit(
attr.value, _spanLimits.maxNumAttributeLength));
} else if (attr.value is List<String>) {
final listString = attr.value as List<String>;
for (var j = 0; j < listString.length; j++) {
listString[j] = _applyAttributeLengthLimit(
listString[j], _spanLimits.maxNumAttributeLength);
}
attr = api.Attribute.fromStringList(attr.key, listString);
}
return attr;
}

@override
void recordException(dynamic exception, {StackTrace stackTrace}) {
// ignore: todo
// TODO: O11Y-1531: Consider integration of Events here.
setStatus(api.StatusCode.error, description: exception.toString());
attributes.addAll([
setAttributes([
api.Attribute.fromBoolean('error', true),
api.Attribute.fromString('exception', exception.toString()),
api.Attribute.fromString('stacktrace', stackTrace.toString()),
]);
}

//Truncate just strings which length is longer than configuration.
//Reference: https://github.com/open-telemetry/opentelemetry-java/blob/14ffacd1cdd22f5aa556eeda4a569c7f144eadf2/sdk/common/src/main/java/io/opentelemetry/sdk/internal/AttributeUtil.java#L80
static Object _applyAttributeLengthLimit(Object value, int lengthLimit) {
if (value is String) {
return value.length > lengthLimit
? value.substring(0, lengthLimit)
: value;
} else if (value is List<String>) {
for (var i = 0; i < value.length; i++) {
value[i] = value[i].length > lengthLimit
? value[i].substring(0, lengthLimit)
: value[i];
}
}
return value;
}
}
83 changes: 83 additions & 0 deletions lib/src/sdk/trace/span_limits.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import '../internal/utils.dart';

class SpanLimits {
final _DEFAULT_MAXNUM_ATTRIBUTES = 200;
final _DEFAULT_MAXNUM_EVENTS = 128;
final _DEFAULT_MAXNUM_LINKS = 128;
final _DEFAULT_MAXNUM_ATTRIBUTE_PER_EVENT = 128;
final _DEFAULT_MAXNUM_ATTRIBUTES_PER_LINK = 128;
final _DEFAULT_MAXNUM_ATTRIBUTES_LENGTH = 1000;

int _maxNumAttributes;
int _maxNumEvents;
int _maxNumLink;
int _maxNumAttributesPerEvent;
int _maxNumAttributesPerLink;
int _maxNumAttributeLength;

///setters
set maxNumAttributes(int maxNumberOfAttributes) {
Utils.checkArgument(maxNumberOfAttributes >= 0,
'maxNumberOfAttributes must be greater or equal to zero');
_maxNumAttributes = maxNumberOfAttributes;
}

set maxNumEvents(int maxNumEvents) {
Utils.checkArgument(
maxNumEvents >= 0, 'maxNumEvents must be greater or equal to zero');
_maxNumEvents = maxNumEvents;
}

set maxNumLink(int maxNumLink) {
Utils.checkArgument(
maxNumLink >= 0, 'maxNumLink must be greater or equal to zero');
_maxNumLink = maxNumLink;
}

set maxNumAttributesPerEvent(int maxNumAttributesPerEvent) {
Utils.checkArgument(maxNumAttributesPerEvent >= 0,
'maxNumAttributesPerEvent must be greater or equal to zero');
_maxNumAttributesPerEvent = maxNumAttributesPerEvent;
}

set maxNumAttributesPerLink(int maxNumAttributesPerLink) {
Utils.checkArgument(maxNumAttributesPerLink >= 0,
'maxNumAttributesPerLink must be greater or equal to zero');
_maxNumAttributesPerLink = maxNumAttributesPerLink;
}

set maxNumAttributeLength(int maxNumAttributeLength) {
Utils.checkArgument(maxNumAttributesPerLink >= 0,
'maxNumAttributesPerLink must be greater or equal to zero');
_maxNumAttributeLength = maxNumAttributeLength;
}

///getters
int get maxNumAttributes => _maxNumAttributes;
int get maxNumEvents => _maxNumEvents;
int get maxNumLink => _maxNumLink;
int get maxNumAttributesPerEvent => _maxNumAttributesPerEvent;
int get maxNumAttributesPerLink => _maxNumAttributesPerLink;
int get maxNumAttributeLength => _maxNumAttributeLength;

///constructor
///https://docs.newrelic.com/docs/data-apis/manage-data/view-system-limits/
///https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SpanLimitsBuilder.java
SpanLimits(
{int maxNumAttributes,
int maxNumEvents,
int maxNumLink,
int maxNumAttributesPerEvent,
int maxNumAttributesPerLink,
int maxNumAttributeLength}) {
_maxNumAttributes = maxNumAttributes ?? _DEFAULT_MAXNUM_ATTRIBUTES;
_maxNumEvents = maxNumEvents ?? _DEFAULT_MAXNUM_EVENTS;
_maxNumLink = maxNumLink ?? _DEFAULT_MAXNUM_LINKS;
_maxNumAttributesPerEvent =
maxNumAttributesPerEvent ?? _DEFAULT_MAXNUM_ATTRIBUTE_PER_EVENT;
_maxNumAttributesPerLink =
maxNumAttributesPerLink ?? _DEFAULT_MAXNUM_ATTRIBUTES_PER_LINK;
_maxNumAttributeLength =
maxNumAttributeLength ?? _DEFAULT_MAXNUM_ATTRIBUTES_LENGTH;
}
}
9 changes: 7 additions & 2 deletions lib/src/sdk/trace/tracer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:async';

import '../../../api.dart' as api;
import '../../../sdk.dart' as sdk;
import 'span_limits.dart';

/// An interface for creating [api.Span]s and propagating context in-process.
class Tracer implements api.Tracer {
Expand All @@ -10,9 +11,13 @@ class Tracer implements api.Tracer {
final api.Sampler _sampler;
final api.IdGenerator _idGenerator;
final api.InstrumentationLibrary _instrumentationLibrary;
SpanLimits _spanLimits = SpanLimits();

Tracer(this._processors, this._resource, this._sampler, this._idGenerator,
this._instrumentationLibrary);
this._instrumentationLibrary,
[spanLimits]) {
_spanLimits = spanLimits ?? _spanLimits;
}

@override
api.Span startSpan(String name,
Expand Down Expand Up @@ -50,7 +55,7 @@ class Tracer implements api.Tracer {

return sdk.Span(name, spanContext, parentSpanId, _processors, _resource,
_instrumentationLibrary,
attributes: attributes);
attributes: attributes, spanlimits: _spanLimits);
}

/// Records a span of the given [name] for the given function
Expand Down
Loading

0 comments on commit 100448a

Please sign in to comment.