Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: logger processor #209

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/src/api/logs/logger_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import 'package:opentelemetry/src/api/logs/logger.dart';
abstract class LoggerProvider {
/// Gets or creates a [Logger] instance.
///
/// The meter is identified by the combination of [name], [version],
/// The logger is identified by the combination of [name], [version],
/// [schemaUrl] and [attributes]. The [name] SHOULD uniquely identify the
/// instrumentation scope, such as the instrumentation library
/// (e.g. io.opentelemetry.contrib.mongodb), package, module or class name.
Expand Down
12 changes: 6 additions & 6 deletions lib/src/experimental_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ export 'api/context/context_manager.dart' show ContextManager;
export 'api/context/noop_context_manager.dart' show NoopContextManager;
export 'api/context/zone_context.dart' show ZoneContext;
export 'api/context/zone_context_manager.dart' show ZoneContextManager;
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;
export 'api/trace/nonrecording_span.dart' show NonRecordingSpan;
export 'api/logs/logger.dart' show Logger;
export 'api/logs/log_record.dart' show Severity;
export 'api/logs/logger.dart' show Logger;
export 'api/logs/logger_provider.dart' show LoggerProvider;
export 'api/logs/noop/noop_logger.dart' show NoopLogger;
export 'api/logs/noop/noop_logger_provider.dart' show NoopLoggerProvider;
export 'api/metrics/counter.dart' show Counter;
export 'api/metrics/meter.dart' show Meter;
export 'api/metrics/meter_provider.dart' show MeterProvider;
export 'api/metrics/noop/noop_meter.dart' show NoopMeter;
export 'api/trace/nonrecording_span.dart' show NonRecordingSpan;
11 changes: 10 additions & 1 deletion lib/src/experimental_sdk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,16 @@ library experimental_sdk;

import 'package:meta/meta.dart';

export 'sdk/logs/export_result.dart' show ExportResult, ExportResultCode;
export 'sdk/logs/exporters/log_record_exporter.dart' show LogRecordExporter;
export 'sdk/logs/log_record.dart' show ReadableLogRecord, ReadWriteLogRecord, LogRecord;
export 'sdk/logs/log_record_limit.dart' show LogRecordLimits;
export 'sdk/logs/logger.dart' show Logger;
export 'sdk/logs/logger_provider.dart' show LoggerProvider;
export 'sdk/logs/processors/batch_log_record_processor.dart' show BatchLogRecordProcessor;
export 'sdk/logs/processors/log_record_processor.dart' show LogRecordProcessor;
export 'sdk/logs/processors/noop_log_processor.dart' show NoopLogRecordProcessor;
export 'sdk/metrics/counter.dart' show Counter;
export 'sdk/metrics/meter_provider.dart' show MeterProvider;
export 'sdk/metrics/meter.dart' show Meter;
export 'sdk/metrics/meter_provider.dart' show MeterProvider;
export 'sdk/resource/resource.dart' show Resource;
19 changes: 19 additions & 0 deletions lib/src/sdk/common/limits.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:meta/meta.dart';

import '../../../api.dart' as api;
import '../../../sdk.dart' as sdk;
import '../logs/log_record_limit.dart';

/// Applies given [sdk.SpanLimits] to a list of [api.SpanLink]s.
@protected
Expand Down Expand Up @@ -77,6 +78,24 @@ api.Attribute applyAttributeLimits(api.Attribute attr, sdk.SpanLimits limits) {
return attr;
}

@protected
api.Attribute applyAttributeLimitsForLog(api.Attribute attr, LogRecordLimits limits) {
// if maxNumAttributeLength is less than zero, then it has unlimited length.
if (limits.attributeValueLengthLimit < 0) return attr;

if (attr.value is String) {
attr = api.Attribute.fromString(
attr.key, applyAttributeLengthLimit(attr.value as String, limits.attributeValueLengthLimit));
} 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], limits.attributeValueLengthLimit);
}
attr = api.Attribute.fromStringList(attr.key, listString);
}
return attr;
}

/// 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
@protected
Expand Down
15 changes: 15 additions & 0 deletions lib/src/sdk/logs/export_result.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2021-2022 Workiva.
// Licensed under the Apache License, Version 2.0. Please see https://github.com/Workiva/opentelemetry-dart/blob/master/LICENSE for more information

class ExportResult {
final ExportResultCode code;
final Exception? error;
final StackTrace? stackTrace;

ExportResult({required this.code, this.error, this.stackTrace});
}

enum ExportResultCode {
success,
failed,
}
10 changes: 10 additions & 0 deletions lib/src/sdk/logs/exporters/log_record_exporter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright 2021-2022 Workiva.
// Licensed under the Apache License, Version 2.0. Please see https://github.com/Workiva/opentelemetry-dart/blob/master/LICENSE for more information

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

abstract class LogRecordExporter {
Future<sdk.ExportResult> export(List<sdk.ReadableLogRecord> logs);

Future<void> shutdown();
}
189 changes: 189 additions & 0 deletions lib/src/sdk/logs/log_record.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
// Copyright 2021-2022 Workiva.
// Licensed under the Apache License, Version 2.0. Please see https://github.com/Workiva/opentelemetry-dart/blob/master/LICENSE for more information

import 'package:fixnum/fixnum.dart';
import 'package:meta/meta.dart';
import 'package:opentelemetry/api.dart' as api;
import 'package:opentelemetry/sdk.dart' as sdk;
import 'package:opentelemetry/src/experimental_api.dart' as api;
import 'package:opentelemetry/src/experimental_sdk.dart' as sdk;

import '../common/limits.dart';

/// https://opentelemetry.io/docs/specs/otel/logs/sdk/#readwritelogrecord
abstract class ReadableLogRecord {
Int64? get timeStamp;

Int64? get observedTimestamp;

String? get severityText;

api.Severity? get severityNumber;

dynamic get body;

sdk.Attributes? get attributes;

api.SpanContext? get spanContext;

sdk.Resource? get resource;

sdk.InstrumentationScope? get instrumentationScope;

int get droppedAttributesCount;
}

abstract class ReadWriteLogRecord extends ReadableLogRecord {
set body(dynamic severity);

set severityText(String? severity);

set severityNumber(api.Severity? severity);
}


class LogRecord implements ReadWriteLogRecord {
@override
final sdk.InstrumentationScope instrumentationScope;

@override
final sdk.Resource? resource;

final sdk.TimeProvider _timeProvider;
final api.Context? context;
final api.SpanContext? _spanContext;
final sdk.LogRecordLimits logRecordLimits;
final DateTime? _timeStamp;
final DateTime? _observedTimestamp;

bool _isReadonly = false;
String? _severityText;
api.Severity? _severityNumber;
dynamic _body;
int _totalAttributesCount = 0;

final sdk.Attributes _attributes;

LogRecord({
required this.instrumentationScope,
required this.logRecordLimits,
api.Severity? severityNumber,
String? severityText,
sdk.Attributes? attributes,
DateTime? timeStamp,
DateTime? observedTimestamp,
dynamic body,
this.resource,
this.context,
sdk.TimeProvider? timeProvider,
}) : _severityText = severityText,
_body = body,
_attributes = sdk.Attributes.empty(),
_severityNumber = severityNumber,
_spanContext = api.spanContextFromContext(context ?? api.Context.current),
_timeStamp = timeStamp,
_observedTimestamp = observedTimestamp,
_timeProvider = timeProvider ?? sdk.DateTimeTimeProvider() {
if (attributes != null) setAttributes(attributes);
}

@override
sdk.Attributes? get attributes => _attributes;

@override
dynamic get body => _body;

@override
set body(dynamic body) {
if (_isReadonly) return;
_body = body;
}

@override
api.SpanContext? get spanContext => _spanContext;

@override
int get droppedAttributesCount => _totalAttributesCount - (attributes?.length ?? 0);

@override
Int64? get timeStamp => _timeStamp != null ? Int64(_timeStamp!.microsecondsSinceEpoch) * 1000 : _timeProvider.now;

@override
Int64? get observedTimestamp =>
_observedTimestamp != null ? Int64(_observedTimestamp!.microsecondsSinceEpoch) * 1000 : _timeProvider.now;

@override
api.Severity? get severityNumber => _severityNumber;

@override
set severityNumber(api.Severity? severity) {
if (_isReadonly) return;
_severityNumber = severity;
}

@override
String? get severityText => _severityText;

@override
set severityText(String? severity) {
if (_isReadonly) return;
_severityText = severity;
}

void setAttributes(sdk.Attributes attributes) {
for (final key in attributes.keys) {
setAttribute(key, attributes.get(key));
}
}

void setAttribute(String key, Object? value) {
if (value == null) return;
if (_isReadonly) return;
if (key.isEmpty) return;
if (logRecordLimits.attributeCountLimit == 0) return;
_totalAttributesCount += 1;
if (value is String) {
_attributes.add(
applyAttributeLimitsForLog(api.Attribute.fromString(key, value), logRecordLimits),
);
}

if (value is bool) {
_attributes.add(api.Attribute.fromBoolean(key, value));
}

if (value is double) {
_attributes.add(api.Attribute.fromDouble(key, value));
}

if (value is int) {
_attributes.add(api.Attribute.fromInt(key, value));
}

if (value is List<String>) {
_attributes.add(
applyAttributeLimitsForLog(api.Attribute.fromStringList(key, value), logRecordLimits),
);
}

if (value is List<bool>) {
_attributes.add(api.Attribute.fromBooleanList(key, value));
}

if (value is List<double>) {
_attributes.add(api.Attribute.fromDoubleList(key, value));
}

if (value is List<int>) {
_attributes.add(api.Attribute.fromIntList(key, value));
}
}

/// A LogRecordProcessor may freely modify logRecord for the duration of the OnEmit call.
/// If logRecord is needed after OnEmit returns (i.e. for asynchronous processing) only reads are permitted.
@internal
void makeReadonly() {
_isReadonly = true;
}
}

25 changes: 25 additions & 0 deletions lib/src/sdk/logs/log_record_limit.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecord-limits
abstract class LogRecordLimits {
// https://opentelemetry.io/docs/specs/otel/common/#configurable-parameters

int get attributeCountLimit;

int get attributeValueLengthLimit;
}

class LogRecordLimitsImpl implements LogRecordLimits {
final int _attributeCountLimit;
final int _attributeValueLengthLimit;

const LogRecordLimitsImpl({
int attributeCountLimit = 128,
int attributeValueLengthLimit = -1,
}) : _attributeCountLimit = attributeCountLimit,
_attributeValueLengthLimit = attributeValueLengthLimit;

@override
int get attributeCountLimit => _attributeCountLimit;

@override
int get attributeValueLengthLimit => _attributeValueLengthLimit;
}
48 changes: 48 additions & 0 deletions lib/src/sdk/logs/logger.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2021-2022 Workiva.
// Licensed under the Apache License, Version 2.0. Please see https://github.com/Workiva/opentelemetry-dart/blob/master/LICENSE for more information

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

class Logger extends api.Logger {
final sdk.InstrumentationScope instrumentationScope;
final sdk.Resource? resource;
final Function(sdk.ReadWriteLogRecord)? onLogEmit;
final sdk.LogRecordLimits logRecordLimits;
final sdk.TimeProvider? timeProvider;

Logger({
required this.instrumentationScope,
required this.logRecordLimits,
this.onLogEmit,
this.resource,
this.timeProvider,
});

@override
void emit({
sdk.Attributes? attributes,
Context? context,
dynamic body,
DateTime? observedTimestamp,
api.Severity? severityNumber,
String? severityText,
DateTime? timeStamp,
}) {
final log = sdk.LogRecord(
logRecordLimits: logRecordLimits,
resource: resource,
instrumentationScope: instrumentationScope,
context: context,
severityText: severityText,
severityNumber: severityNumber,
attributes: attributes,
body: body,
timeProvider: timeProvider,
);
onLogEmit?.call(log);
log.makeReadonly();
}
}
11 changes: 11 additions & 0 deletions lib/src/sdk/logs/logger_config.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// https://opentelemetry.io/docs/specs/otel/logs/sdk/#loggerconfig
class LoggerConfig {
/// If not explicitly set,
/// the disabled parameter SHOULD default to false ( i.e. Loggers are enabled by default).
/// If a Logger is disabled, it MUST behave equivalently to No-op Logger.
final bool disabled;

const LoggerConfig({
this.disabled = false,
});
}
Loading
Loading