Skip to content

Commit

Permalink
Merge pull request #90 from Workiva/O11Y-2256
Browse files Browse the repository at this point in the history
O11Y-2256 : Resource can be passed into MeterProvider
  • Loading branch information
rmconsole4-wk authored Dec 3, 2022
2 parents de232c2 + 4aa18df commit 2906bd6
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 11 deletions.
1 change: 0 additions & 1 deletion lib/src/api/metrics/noop/noop_meter_provider.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'package:opentelemetry/api.dart';
import 'package:opentelemetry/src/api/metrics/noop/noop_meter.dart';
import 'package:opentelemetry/src/experimental_api.dart';

/// A noop registry for creating named [Meter]s.
Expand Down
6 changes: 6 additions & 0 deletions lib/src/experimental_api.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
@experimental
library experimental_api;

import 'package:meta/meta.dart';

export 'api/metrics/counter.dart' show Counter;
export 'api/metrics/meter_provider.dart' show MeterProvider;
export 'api/metrics/meter.dart' show Meter;
export 'api/metrics/noop/noop_meter.dart' show NoopMeter;
6 changes: 6 additions & 0 deletions lib/src/experimental_sdk.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
@experimental
library experimental_sdk;

import 'package:meta/meta.dart';

export 'sdk/metrics/counter.dart' show Counter;
export 'sdk/metrics/meter_provider.dart' show MeterProvider;
export 'sdk/metrics/meter.dart' show Meter;
export 'sdk/resource/resource.dart' show Resource;
27 changes: 27 additions & 0 deletions lib/src/sdk/common/instrumentation_scope.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'package:opentelemetry/api.dart' as api;

class InstrumentationScope {
final String _name;
final String _version;
final String _schemaUrl;
final List<api.Attribute> _attributes;

InstrumentationScope(
this._name, this._version, this._schemaUrl, this._attributes);

String get name {
return _name;
}

String get version {
return _version;
}

String get schemaUrl {
return _schemaUrl;
}

List<api.Attribute> get attributes {
return _attributes;
}
}
7 changes: 7 additions & 0 deletions lib/src/sdk/metrics/meter.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import 'package:opentelemetry/src/experimental_sdk.dart' as sdk;
import 'package:opentelemetry/src/experimental_api.dart' as api;

import 'state/meter_shared_state.dart';

class Meter implements api.Meter {
// ignore: unused_field
final MeterSharedState _state;

Meter(this._state);

@override
api.Counter<T> createCounter<T extends num>(String name,
{String description, String unit}) {
Expand Down
27 changes: 19 additions & 8 deletions lib/src/sdk/metrics/meter_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,39 @@ import 'package:opentelemetry/api.dart' as api;
import 'package:opentelemetry/src/experimental_api.dart' as api;
import 'package:opentelemetry/src/experimental_sdk.dart' as sdk;
import 'package:logging/logging.dart';
import 'package:opentelemetry/src/api/metrics/meter_key.dart';
import 'package:opentelemetry/src/sdk/common/instrumentation_scope.dart';
import 'package:opentelemetry/src/sdk/metrics/state/meter_provider_shared_state.dart';

const invalidMeterNameMessage = 'Invalid Meter Name';

class MeterProvider implements api.MeterProvider {
final _meters = <MeterKey, api.Meter>{};
final _logger = Logger('opentelemetry.sdk.metrics.meterprovider');
final _shutdown = false;
final MeterProviderSharedState _sharedState;

sdk.Resource get resource => _sharedState.resource;

MeterProvider({sdk.Resource resource})
: _sharedState = MeterProviderSharedState(resource);

@override
sdk.Meter get(String name,
api.Meter get(String name,
{String version = '',
String schemaUrl = '',
List<api.Attribute> attributes = const []}) {
if (name == null || name == '') {
name = '';
_logger.warning(invalidMeterNameMessage, '', StackTrace.current);
}
version ??= '';
schemaUrl ??= '';
attributes ??= const [];
final key = MeterKey(name, version, schemaUrl, attributes);

return _meters.putIfAbsent(key, () => sdk.Meter());
if (_shutdown) {
_logger.warning('A shutdown MeterProvider cannot provide a Meter', '',
StackTrace.current);
return api.NoopMeter();
}

return _sharedState
.getMeterSharedState(InstrumentationScope(name, version, schemaUrl, attributes))
.meter;
}
}
27 changes: 27 additions & 0 deletions lib/src/sdk/metrics/state/meter_provider_shared_state.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'package:opentelemetry/sdk.dart';
import 'package:opentelemetry/src/sdk/common/instrumentation_scope.dart';
import 'package:opentelemetry/src/sdk/metrics/state/meter_shared_state.dart';
import 'package:quiver/core.dart';

int instrumentationScopeId(InstrumentationScope instrumentationScope) {
return hash3(instrumentationScope.name, instrumentationScope.version,
instrumentationScope.schemaUrl);
}

class MeterProviderSharedState {
Resource resource;
final Map<int, MeterSharedState> _meterSharedStates = {};

MeterProviderSharedState(this.resource);

MeterSharedState getMeterSharedState(
InstrumentationScope instrumentationScope) {
final id = instrumentationScopeId(instrumentationScope);
var meterSharedState = _meterSharedStates[id];
if (meterSharedState == null) {
meterSharedState = MeterSharedState(this, instrumentationScope);
_meterSharedStates[id] = meterSharedState;
}
return meterSharedState;
}
}
17 changes: 17 additions & 0 deletions lib/src/sdk/metrics/state/meter_shared_state.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'package:opentelemetry/src/sdk/common/instrumentation_scope.dart';

import 'package:opentelemetry/src/experimental_sdk.dart' as sdk;

import 'meter_provider_shared_state.dart';

class MeterSharedState {
// ignore: unused_field
final MeterProviderSharedState _meterProviderSharedState;
// ignore: unused_field
final InstrumentationScope _instrumentationScope;
sdk.Meter meter;

MeterSharedState(this._meterProviderSharedState, this._instrumentationScope) {
meter = sdk.Meter(this);
}
}
10 changes: 8 additions & 2 deletions test/unit/sdk/metrics/meter_provider_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ void main() {

test(
'getting by same name, same version, same schema_url and different '
'attributes will return different meter instances', () {
'attributes will return the same meter instance', () {
const meterName = 'meterA';
const version = 'v2';
const url = 'http:schemas.com';
Expand All @@ -154,7 +154,13 @@ void main() {
final meterB = meterProvider.get(meterName,
version: version, schemaUrl: url, attributes: attributesB);

expect(identical(meterA, meterB), false);
expect(identical(meterA, meterB), true);
});

test('resource can be set', () {
final resource = sdk.Resource([api.Attribute.fromString('foo', 'bar')]);
final provider = sdk.MeterProvider(resource: resource);
expect(identical(resource, provider.resource), true);
});

// todo: implement test that verifies that changes to attributes apply to
Expand Down
42 changes: 42 additions & 0 deletions test/unit/sdk/metrics/state/instrumentation_scope_id_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
@TestOn('vm')

import 'package:logging/logging.dart';
import 'package:opentelemetry/api.dart';
import 'package:opentelemetry/src/sdk/common/instrumentation_scope.dart';
import 'package:opentelemetry/src/sdk/metrics/state/meter_provider_shared_state.dart';
import 'package:test/test.dart';

void main() {
group('instrumentationScopeId:', () {
setUp(() {
Logger.root.level = Level.ALL; // defaults to Level.INFO
Logger.root.onRecord.listen((record) {
printOnFailure(
'${record.level.name}: ${record.time}: ${record.message}');
});
});

test('instrumentationScopeId with same parameters returns same id', () {
//int instrumentationScopeId(InstrumentationScope instrumentationScope) {
const nameOne = 'testName';
const versionOne = '';
const schemaUrlOne = '';
const attributesOne = <Attribute>[];

const nameTwo = 'testName';
const versionTwo = '';
const schemaUrlTwo = '';
const attributesTwo = <Attribute>[];

final scopeOne = InstrumentationScope(
nameOne, versionOne, schemaUrlOne, attributesOne);
final idOne = instrumentationScopeId(scopeOne);

final scopeTwo = InstrumentationScope(
nameTwo, versionTwo, schemaUrlTwo, attributesTwo);
final idTwo = instrumentationScopeId(scopeTwo);

expect(idOne, equals(idTwo));
});
});
}

0 comments on commit 2906bd6

Please sign in to comment.